summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext
Commit message (Collapse)AuthorAgeFilesLines
* Add async_engine_from_config()Nils Philippsen2021-12-102-0/+24
| | | | | | | | | | | | | Added :func:`_asyncio.async_engine_config` function to create an async engine from a configuration dict. This otherwise behaves the same as :func:`_sa.engine_from_config`. Fixes: #7301 Closes: #7302 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/7302 Pull-request-sha: c7c758833b6c37b7509b8c5bed4f26ac0ccc0395 Change-Id: I64feadf95b5015c24fe0fa0dbae6755b72d1713e
* Warn when caching is disabled / documentMike Bayer2021-12-061-5/+100
| | | | | | | | | | | | | | | | | | | | | | | | | | | This patch adds new warnings for all elements that don't indicate their caching behavior, including user-defined ClauseElement subclasses and third party dialects. it additionally adds new documentation to discuss an apparent performance degradation in 1.4 when caching is disabled as a result in the significant expense incurred by ORM lazy loaders, which in 1.3 used BakedQuery so were actually cached. As a result of adding the warnings, a fair degree of lesser used SQL expression objects identified that they did not define caching behavior so would have been producing ``[no key]``, including PostgreSQL constructs ``hstore`` and ``array``. These have been amended to use inherit cache where appropriate. "on conflict" constructs in PostgreSQL, MySQL, SQLite still explicitly don't generate a cache key at this time. The change also adds a test for all constructs via assert_compile() to assert they will not generate cache warnings. Fixes: #7394 Change-Id: I85958affbb99bfad0f5efa21bc8f2a95e7e46981
* document get_bind() overrides for async sessionMike Bayer2021-11-291-1/+78
| | | | | Change-Id: Ifcc936a5861d49857d1f365416190cfbd0981aac References: #7383
* Added support for ``psycopg`` dialect.Federico Caselli2021-11-261-0/+1
| | | | | | | Both sync and async versions are supported. Fixes: #6842 Change-Id: I57751c5028acebfc6f9c43572562405453a2f2a4
* Clean up most py3k compatFederico Caselli2021-11-245-22/+14
| | | | Change-Id: I8172fdcc3103ff92aa049827728484c8779af6b7
* Remove object in class definitionFederico Caselli2021-11-228-22/+22
| | | | | References: #4600 Change-Id: I2a62ddfe00bc562720f0eae700a497495d7a987a
* handle dunder names in @declared_attr separatelyMike Bayer2021-11-172-1/+19
| | | | | | | | | | | | | | | | Fixed Mypy crash which would occur when using Mypy plugin against code which made use of :class:`_orm.declared_attr` methods for non-mapped names like ``__mapper_args__``, ``__table_args__``, or other dunder names, as the plugin would try to interpret these as mapped attributes which would then be later mis-handled. As part of this change, the decorated function is still converted by the plugin into a generic assignment statement (e.g. ``__mapper_args__: Any``) so that the argument signature can continue to be annotated in the same way one would for any other ``@classmethod`` without Mypy complaining about the wrong argument type for a method that isn't explicitly ``@classmethod``. Fixes: #7321 Change-Id: I55656e867876677c5c55143449db371344be8600
* fully implement future engine and remove legacyMike Bayer2021-11-072-52/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | The major action here is to lift and move future.Connection and future.Engine fully into sqlalchemy.engine.base. This removes lots of engine concepts, including: * autocommit * Connection running without a transaction, autobegin is now present in all cases * most "autorollback" is obsolete * Core-level subtransactions (i.e. MarkerTransaction) * "branched" connections, copies of connections * execution_options() returns self, not a new connection * old argument formats, distill_params(), simplifies calling scheme between engine methods * before/after_execute() events (oriented towards compiled constructs) don't emit for exec_driver_sql(). before/after_cursor_execute() is still included for this * old helper methods superseded by context managers, connection.transaction(), engine.transaction() engine.run_callable() * ancient engine-level reflection methods has_table(), table_names() * sqlalchemy.testing.engines.proxying_engine References: #7257 Change-Id: Ib20ed816642d873b84221378a9ec34480e01e82c
* First round of removal of python 2Federico Caselli2021-11-011-23/+4
| | | | | References: #4600 Change-Id: I61e35bc93fe95610ae75b31c18a3282558cd4ffe
* deprecation warnings: strings in loader options, join, with_parentMike Bayer2021-10-251-3/+16
| | | | | | | | | | | | | Repairs one in-library deprecation warning regarding mapper propagation of options raises maxfail to 250, as 25 is too low when we are trying to address many errors at once. the 25 was originally due to the fact that our fixtures would be broken after that many failures in most cases, which today should not be the case nearly as often. Change-Id: I26affddf42e2cae2aaf9561633e9b8cd431eb189
* process bulk_update_tuples before cache key or compilationMike Bayer2021-10-191-4/+0
| | | | | | | | | | | | | | | | | | | | | | | | | | | Fixed regression where the use of a :class:`_orm.hybrid_property` attribute or a mapped :func:`_orm.composite` attribute as a key passed to the :meth:`_dml.Update.values` method for an ORM-enabled :class:`_dml.Update` statement, as well as when using it via the legacy :meth:`_orm.Query.update` method, would be processed for incoming ORM/hybrid/composite values within the compilation stage of the UPDATE statement, which meant that in those cases where caching occurred, subsequent invocations of the same statement would no longer receive the correct values. This would include not only hybrids that use the :meth:`_orm.hybrid_property.update_expression` method, but any use of a plain hybrid attribute as well. For composites, the issue instead caused a non-repeatable cache key to be generated, which would break caching and could fill up the statement cache with repeated statements. The :class:`_dml.Update` construct now handles the processing of key/value pairs passed to :meth:`_dml.Update.values` and :meth:`_dml.Update.ordered_values` up front when the construct is first generated, before the cache key has been generated so that the key/value pairs are processed each time, and so that the cache key is generated against the individual column/value pairs that will ultimately be used in the statement. Fixes: #7209 Change-Id: I08f248d1d60ea9690b014c21439b775d951fb9e5
* Add documentation on how to use the events with asyncioFederico Caselli2021-10-082-0/+46
| | | | | | Co-authored-by: Mike Bayer <mike_mp@zzzcomputing.com> Fixes: #6899 Change-Id: I965af321fb36d9645fe3fc2675ad9943f24e32f2
* fix(6435): support `MemberExpr` for enum column declarationHiroshi Ogawa2021-10-051-1/+1
| | | | | | | | | | | | | Fixed issue in mypy plugin to improve upon some issues detecting ``Enum()`` SQL types containing custom Python enumeration classes. Pull request courtesy Hiroshi Ogawa. Fixes: #6435 Closes: #7048 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/7048 Pull-request-sha: 59f5c89688792f6af3b07488d5cf97f8f2e964dc Change-Id: I05adbec74ceac1ecfdc5a242bfe7aa4b2eb805e4
* Add missing methods added in :ticket:`6991`Federico Caselli2021-09-281-0/+3
| | | | | | | to ``scoped_session`` and ``async_scoped_session``. Fixes: #7103 Change-Id: If80481771d9b428f2403af3862e0479bd069257e
* Surface driver connection object when using a proxied dialectFederico Caselli2021-09-171-4/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | Improve the interface used by adapted drivers, like the asyncio ones, to access the actual connection object returned by the driver. The :class:`_engine._ConnectionRecord` and :class:`_engine._ConnectionFairy` now have two new attributes: * ``dbapi_connection`` always represents a DBAPI compatible object. For pep-249 drivers, this is the DBAPI connection as it always has been, previously accessed under the ``.connection`` attribute. For asyncio drivers that SQLAlchemy adapts into a pep-249 interface, the returned object will normally be a SQLAlchemy adaption object called :class:`_engine.AdaptedConnection`. * ``driver_connection`` always represents the actual connection object maintained by the third party pep-249 DBAPI or async driver in use. For standard pep-249 DBAPIs, this will always be the same object as that of the ``dbapi_connection``. For an asyncio driver, it will be the underlying asyncio-only connection object. The ``.connection`` attribute remains available and is now a legacy alias of ``.dbapi_connection``. Fixes: #6832 Change-Id: Ib72f97deefca96dce4e61e7c38ba430068d6a82e
* Add scalars method to connection and session classesMiguel Grinberg2021-09-142-0/+103
| | | | | | | | | | | | | | | | | | | | Added new methods :meth:`_orm.Session.scalars`, :meth:`_engine.Connection.scalars`, :meth:`_asyncio.AsyncSession.scalars` and :meth:`_asyncio.AsyncSession.stream_scalars`, which provide a short cut to the use case of receiving a row-oriented :class:`_result.Result` object and converting it to a :class:`_result.ScalarResult` object via the :meth:`_engine.Result.scalars` method, to return a list of values rather than a list of rows. The new methods are analogous to the long existing :meth:`_orm.Session.scalar` and :meth:`_engine.Connection.scalar` methods used to return a single value from the first row only. Pull request courtesy Miguel Grinberg. Fixes: #6990 Closes: #6991 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/6991 Pull-request-sha: b3e0bb3042c55b0cc5af6a25cb3f31b929f88a47 Change-Id: Ia445775e24ca964b0162c2c8e5ca67dd1e39199f
* Added loader options to session.merge, asyncsession.mergeDaniel Stone2021-09-021-4/+38
| | | | | | | | | | | | | | | Added loader options to :meth:`_orm.Session.merge` and :meth:`_asyncio.AsyncSession.merge`, which will apply the given loader options to the ``get()`` used internally by merge, allowing eager loading of relationships etc. to be applied when the merge process loads a new object. Pull request courtesy Daniel Stone. Fixes: #6955 Closes: #6957 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/6957 Pull-request-sha: ab4d96cd5da9a5dd01112b8dcd6514db64aa8d9f Change-Id: I5b94dfda1088a8bc6396e9fd9a072827df1f8680
* add asyncio.gather() example; add connection optsMike Bayer2021-09-021-2/+7
| | | | | | | | | | | | while I dont like this approach very much, people will likely be asking for it a lot, so represent the most correct and efficient form we can handle right now. Added missing ``**kw`` arguments to the :meth:`_asyncio.AsyncSession.connection` method. Change-Id: Idadae2a02a4d96ecb96a5723ce64d017ab4c6217 References: https://github.com/sqlalchemy/sqlalchemy/discussions/6965
* remove async session slotsMike Bayer2021-09-011-8/+0
| | | | | | | | | | 1798c3cf1c added __slots__ to the base which then caused af0824fd79 to fail. as we can't use an assignable class variable with slots, remove slots from AsyncSession which is how this class was working anyway. Fixes: #6967 Change-Id: I4e0adab923db8e77cf748a8728e253258838e8f1
* Merge "Allow custom sync session class in ``AsyncSession``."mike bayer2021-08-301-3/+49
|\
| * Allow custom sync session class in ``AsyncSession``.Federico Caselli2021-08-301-3/+49
| | | | | | | | | | | | | | | | | | | | | | The :class:`_asyncio.AsyncSession` now supports overriding which :class:`_orm.Session` it uses as the proxied instance. A custom ``Session`` class can be passed using the :paramref:`.AsyncSession.sync_session_class` parameter or by subclassing the ``AsyncSession`` and specifying a custom :attr:`.AsyncSession.sync_session_class`. Fixes: #6689 Change-Id: Idf9c24eae6c9f4e2fff292ed748feaa449a8deaa
* | Improve error message when inspecting async proxiesFederico Caselli2021-08-302-0/+28
|/ | | | | | | Provide better error message when trying to insepct and async engine or asnyc connection. Change-Id: I907f3a22c6b76fe43df9d40cb0e69c57f74a7982
* Merge "dont assume argument lists for column property"mike bayer2021-08-282-7/+27
|\
| * dont assume argument lists for column propertyMike Bayer2021-08-272-7/+27
| | | | | | | | | | | | | | | | Fixed issue where mypy plugin would crash when interpreting a ``query_expression()`` construct. Fixes: #6950 Change-Id: Ic1f28d135bf6eb05c92061430c0d5a3663b804ef
* | Handle mappings passed to ``execution_options``.Federico Caselli2021-08-261-2/+15
|/ | | | | | | | | | Fixed a bug in :meth:`_asyncio.AsyncSession.execute` and :meth:`_asyncio.AsyncSession.stream` that required ``execution_options`` to be an instance of ``immutabledict`` when defined. It now correctly accepts any mapping. Fixes: #6943 Change-Id: Ic09de480dc2da1b0bdce25acb60b8f01371971f9
* ensure "sqlalchemy" info set for all considered classesMike Bayer2021-08-252-0/+7
| | | | | | | | | Fixed issue in mypy plugin where columns on a mixin would not be correctly interpreted if the mapped class relied upon a ``__tablename__`` routine that came from a superclass. Fixes: #6937 Change-Id: I74aed4862d0545008ee67f781aaa794ab6866926
* Merge "Deprecate scoped_session usage with async sessions"mike bayer2021-08-232-0/+4
|\
| * Deprecate scoped_session usage with async sessionsFederico Caselli2021-07-282-0/+4
| | | | | | | | | | | | | | | | | | Deprecate usage of :class:`_orm.scoped_session` with asyncio drivers. When using Asyncio the :class:`_asyncio.async_scoped_session` should be used instead. Fixes: #6746 Change-Id: I540d57a406f59efc37fc61f0e9dfe03f32fe2904
* | Merge "Refactor mypy plugin"mike bayer2021-08-076-312/+410
|\ \ | |/ |/|
| * Refactor mypy pluginBryan Forbes2021-08-076-312/+410
| | | | | | | | | | | | | | | | | | | | A major refactor of the mypy plugin Closes: #6764 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/6764 Pull-request-sha: 3e2295b2da7b57a6669f26db0df78f6409934184 Change-Id: I067d56dcfbc998ddd1b22a448f756859428b9e31
* | accommodate plain core textual statementsMike Bayer2021-07-271-3/+5
| | | | | | | | | | | | | | | | | | Fixed issue where the horizontal sharding extension would not correctly accommodate for a plain textual SQL statement passed to :meth:`_orm.Session.execute`. Fixes: #6816 Change-Id: Ie2b71b06d10793443dbd5e1b271c56cbf9431bb3
* | Documentation improvementsFederico Caselli2021-07-201-0/+9
|/ | | | | | | | | | | | | | Also remove deprecated usage: - load_only does not accept strings - case.whens is positional only Ref #6712 Ref #5994 Ref #6121 Ref #6785 Ref https://groups.google.com/g/sqlalchemy/c/-cnhThEu3kk Change-Id: I5db49a075b9d3d332518b9d189a24b13b502e2af
* Replace all http:// links to https://Federico Caselli2021-07-0425-29/+29
| | | | | | Also replace http://pypi.python.org/pypi with https://pypi.org/project Change-Id: I84b5005c39969a82140706472989f2a30b0c7685
* have automap suppress overlaps warning for mapped secondaryMike Bayer2021-06-251-0/+9
| | | | | | | | | | | | | | | | Fixed regression in :mod:`sqlalchemy.ext.automap` extension such that the use case of creating an explicit mapped class to a table that is also the :paramref:`_orm.relationship.secondary` element of a :func:`_orm.relationship` that automap will be generating would emit the "overlaps" warnings introduced in 1.4 and discussed at :ref:`error_qzyx`. While generating this case from automap is still subject to the same caveats that the "overlaps" warning refers towards, as automap is intended for more ad-hoc use cases, the condition which produces the warning is disabled when a many-to-many relationship with this particular pattern is generated. Fixes: #6679 Change-Id: Ib3a53982b076ed4999b0d3235f84008b9e2f1cce
* accommodate no cls info found in _scan_declarativeMike Bayer2021-06-212-1/+6
| | | | | | | | | Fixed issue in mypy plugin where class info for a custom declarative base would not be handled correctly on a cached mypy pass, leading to an AssertionError being raised. Fixes: #6476 Change-Id: If78340673e6a4d16d8f7cf787ce3bdb02c8bd47b
* Implement async_scoped_sessionjason3gb2021-06-162-0/+102
| | | | | | | | | | | | | | | Implemented :class:`_asyncio.async_scoped_session` to address some asyncio-related incompatibilities between :class:`_orm.scoped_session` and :class:`_asyncio.AsyncSession`, in which some methods (notably the :meth:`_asyncio.async_scoped_session.remove` method) should be used with the ``await`` keyword. Fixes: #6583 Closes: #6603 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/6603 Pull-request-sha: 0e8ef87dc824dcd83dca01641441afc453c8e07a Change-Id: I9bfe56f8670302ff0015d9dc56c1e3ac5b92b118
* Implement proxy back reference system for asyncioMike Bayer2021-06-024-37/+177
| | | | | | | | | | | | | | | | | | | | | | | | | | Implemented a new registry architecture that allows the ``Async`` version of an object, like ``AsyncSession``, ``AsyncConnection``, etc., to be locatable given the proxied "sync" object, i.e. ``Session``, ``Connection``. Previously, to the degree such lookup functions were used, an ``Async`` object would be re-created each time, which was less than ideal as the identity and state of the "async" object would not be preserved across calls. From there, new helper functions :func:`_asyncio.async_object_session`, :func:`_asyncio.async_session` as well as a new :class:`_orm.InstanceState` attribute :attr:`_orm.InstanceState.asyncio_session` have been added, which are used to retrieve the original :class:`_asyncio.AsyncSession` associated with an ORM mapped object, a :class:`_orm.Session` associated with an :class:`_asyncio.AsyncSession`, and an :class:`_asyncio.AsyncSession` associated with an :class:`_orm.InstanceState`, respectively. This patch also implements new methods :meth:`_asyncio.AsyncSession.in_nested_transaction`, :meth:`_asyncio.AsyncSession.get_transaction`, :meth:`_asyncio.AsyncSession.get_nested_transaction`. Fixes: #6319 Change-Id: Ia452a7e7ce9bad3ff8846c7dea8d45c839ac9fac
* Removed automap deprecation warningjason3gb2021-05-251-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Fixed a deprecation warning that was emitted when using :func:`_automap.automap_base` without passing an existing ``Base``. Fixes: #6529 <!-- Provide a general summary of your proposed changes in the Title field above --> ### Description Fixed the automap's declarative import from sqlalchemy.orm instead from the deprecated ext.declarative package ### Checklist <!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once) --> This pull request is: - [ ] A documentation / typographical error fix - Good to go, no issue or tests are needed - [x] A short code fix - please include the issue number, and create an issue if none exists, which must include a complete example of the issue. one line code fixes without an issue and demonstration will not be accepted. - Please include: `Fixes: #<issue number>` in the commit message - please include tests. one line code fixes without tests will not be accepted. - [ ] A new feature implementation - please include the issue number, and create an issue if none exists, which must include a complete example of how the feature would look. - Please include: `Fixes: #<issue number>` in the commit message - please include tests. **Have a nice day!** Closes: #6533 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/6533 Pull-request-sha: 46d40005eae51fd7423da66f6ad4072f65744c1c Change-Id: I432df70d4221873ca229076d5d3ac20b2870c13b
* Clarify close for sync / async sessionMike Bayer2021-05-241-1/+27
| | | | | | | | | Add seealso links from Session.close() and AsyncSession.close() to narrative description, clarify in both places what the method does and does not do. Change-Id: Ib804753a86b4761e5f198c52121e8433c851cbc4 References: #6528
* Ensure extended instrumentation is fully disposedMike Bayer2021-05-201-4/+4
| | | | | | | | | | | | | Fixed regression in the ``sqlalchemy.ext.instrumentation`` extension that prevented instrumentation disposal from working completely. This fix includes both a 1.4 regression fix as well as a fix for a related issue that existed in 1.3 also. As part of this change, the :class:`sqlalchemy.ext.instrumentation.InstrumentationManager` class now has a new method ``unregister()``, which replaces the previous method ``dispose()``, which was not called as of version 1.4. Fixes: #6390 Change-Id: I1b7a0f190b56d31b93b5ba11f67dc9f73889958b
* Remove pep484 type comments from the codeFederico Caselli2021-05-164-117/+41
| | | | | | | | | | | | | Current effort is around the stub package, and having typing in two places makes thing worse, since the types here are usually outdated compared to the version in the stubs. Once v2 gets under way we can start consolidating the types here. Fixes: #6461 Change-Id: I7132a444bd7138123074bf5bc664b4bb119a85ce
* Update black flak8 and zimportsFederico Caselli2021-05-121-2/+3
| | | | Change-Id: I488c9557eda390e4a88319affd4c8813ee274f80
* unify transactional context managersMike Bayer2021-05-053-21/+16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Applied consistent behavior to the use case of calling ``.commit()`` or ``.rollback()`` inside of an existing ``.begin()`` context manager, with the addition of potentially emitting SQL within the block subsequent to the commit or rollback. This change continues upon the change first added in :ticket:`6155` where the use case of calling "rollback" inside of a ``.begin()`` contextmanager block was proposed: * calling ``.commit()`` or ``.rollback()`` will now be allowed without error or warning within all scopes, including that of legacy and future :class:`_engine.Engine`, ORM :class:`_orm.Session`, asyncio :class:`.AsyncEngine`. Previously, the :class:`_orm.Session` disallowed this. * The remaining scope of the context manager is then closed; when the block ends, a check is emitted to see if the transaction was already ended, and if so the block returns without action. * It will now raise **an error** if subsequent SQL of any kind is emitted within the block, **after** ``.commit()`` or ``.rollback()`` is called. The block should be closed as the state of the executable object would otherwise be undefined in this state. Fixes: #6288 Change-Id: I8b21766ae430f0fa1ac5ef689f4c0fb19fc84336
* Re-infer statements that got more specific on subsequent passMike Bayer2021-04-203-32/+103
| | | | | | | | | | | | | | | | | | | | | | | | Fixed issue where mypy plugin would not correctly interpret an explicit :class:`_orm.Mapped` annotation in conjunction with a :func:`_orm.relationship` that refers to a class by string name; the correct annotation would be downgraded to a less specific one leading to typing errors. The thing figured out here is that after we've already scanned a class in the semanal stage and created DeclClassApplied, when we are called again with that same DeclClassApplied, for this specific kind of case we actually now have *better* types than we did before, where the left side that looked like List?[Address?] now seems to say builtins.list[official.module.Address] - so let's take the right side expression again, this time embedded in our Mapped._empty_constructor() expression, and run the infer all over again just like mypy would. Just not setting the "wrong" type here fixed the test cases but by re-applying the whole infer we get the correct Mapped[] on the left side too. Fixes: #6255 Change-Id: Iafe7254374f685a8458c7a1db82aafc2ed6d0232
* Fixed ``instrument_declarative`` registry call.Federico Caselli2021-04-171-3/+3
| | | | | | | | Fixed :func:`_declarative.instrument_declarative` that called a non existing registry method. Fixes: #6291 Change-Id: I6fc8db84f72240cc82e7f6f3a784c424f5ccfc96
* Fix OrderingList handlingBryan Forbes2021-04-131-0/+22
| | | | | | | | Revised the fix for ``OrderingList`` from version 1.4.7 which was testing against the incorrect API. Fixes: #6205 Change-Id: I1d3f8b6534b70ae000294c2a67f08117cb77ee99
* Update mypy plugin to conform to strict modeBryan Forbes2021-04-126-215/+382
| | | | Change-Id: I09a3df5af2f2d4ee34d8d72c3dedc4f236df8eb1
* Merge "Fix typo that prevented setting the ``bind`` attribute of an ↵mike bayer2021-04-121-1/+1
|\ | | | | | | :class:`_asyncio.AsyncSession` to the correct value."
| * Fix typo that prevented setting the ``bind``Federico Caselli2021-04-101-1/+1
| | | | | | | | | | | | | | attribute of an :class:`_asyncio.AsyncSession` to the correct value. Fixes: #6220 Change-Id: I91021351b8076e4aa4139af4b2cf359b3c0404af
* | Update mypy plugin to only use public plugin APIBryan Forbes2021-04-096-35/+40
|/ | | | Change-Id: Id7f4e4a39e17c1b6ec3c754e2fc5c6ba4b437c38