Skip to content

0.17.0

Choose a tag to compare

@x42005e1f x42005e1f released this 14 Jun 12:24
· 35 commits to main since this release
450bbe8

Added

  • with_() methods to all awaitable primitives (for waiting with given parameters) and the timeout parameter to all blocking asynchronous functions (semantically equivalent to that of blocking green functions). This unifies the interface and provides safe handling of large timeouts (which is particularly relevant for Curio) and zero timeouts (which is particularly relevant for Trio) across all supported libraries; in this sense, it is a logical continuation of the extensive work on timeouts carried out in version 0.15.0. The main reason for this change is the observation that the lack of timeouts as parameters for asynchronous operations leads to boilerplate code with asyncio.wait_for() in some dependent repositories, which is clearly undesirable.
  • aiologic.meta.replaces_when_imported() as a safer alternative to wrapt.when_imported() + aiologic.meta.replaces().
  • aiologic.meta.import_original() as an aiologic.meta.import_from()-like unified alternative to eventlet.patcher.original() and gevent.monkey.get_original().
  • aiologic.meta.isgreenpatched() as a unified alternative to eventlet.patcher.is_monkey_patched() and gevent.monkey.is_module_patched().
  • aiologic.meta.lookup_static() and aiologic.meta.resolve_special() to look for a name via the MRO without triggering user code. They are similar to inspect.getattr_static(), but never search via the MRO of the passed object's type (to avoid confusion between, for example, EnumType.__call__() and user-defined __call__() in an Enum subclass) and have a simpler (more efficient) implementation. Unlike aiologic.meta.lookup_static(), aiologic.meta.resolve_special() resolves descriptors using the passed arguments.
  • aiologic.meta.isdatadescriptor_static() and aiologic.meta.ismethoddescriptor_static() to check whether an object is a descriptor of the specified type. aiologic.meta.isdatadescriptor_static() uses the same algorithm as aiologic.meta.lookup_static(), so it returns True only for objects that actually work as data descriptors (not prone to false positives, unlike inspect.isdatadescriptor()). Note that unlike inspect.ismethoddescriptor(), aiologic.meta.ismethoddescriptor_static() follows the PEP 590 definition, where method descriptors associate the __get__() method with the __call__() method (a special case of such descriptors are functions).
  • aiologic.meta.ismetaclass_static() and aiologic.meta.isclass_static() to check whether an object is a metaclass (subclass of type) and a class (instance of type), respectively. Unlike inspect.isclass(), they behave in the same way as the functions below.
  • aiologic.meta.issubclass_static() and aiologic.meta.isinstance_static() as analogues to built-in functions but without triggering user code. Unlike the latter, they also ignore pseudo-classes (arbitrary objects providing the __bases__ attribute), virtual subclasses, and the __class__ attribute (which can be replaced by object proxies).
  • aiologic.meta.isgeneratorlike(), aiologic.meta.iscoroutinelike(), and aiologic.meta.isasyncgenlike() as inspect-like functions that check whether an object implements a certain interface (corresponding to the function name).
  • aiologic.meta.isgeneratorfactory(), aiologic.meta.iscoroutinefactory(), and aiologic.meta.isasyncgenfactory() as functions that differ from inspect.is*function() in that they have broader semantics and more flexible checks.
  • aiologic.meta.markgeneratorfactory(), aiologic.meta.markcoroutinefactory(), and aiologic.meta.markasyncgenfactory() as a way to fine-tune the above (and some standard) functions regardless of the Python version.
  • aiologic.meta.generator() and aiologic.meta.coroutine() functions for transforming generator/coroutine factories into the corresponding object functions. Unlike types.coroutine(), they always return a new function, and it is always a native object function (unless they have been cythonized). Mainly added to define the __await__() method via an asynchronous function (and subsequent transformation into a generator function) with no overhead when calling, with annotation updates (for sphinx.ext.autodoc).
  • aiologic.meta.GeneratorCoroutineWrapper as a way to create universal objects that are similar to both types.GeneratorType and types.CoroutineType. Useful when you need to use a coroutine in generator functions or a generator in coroutine functions, and for some reason you cannot transform the function itself.
  • aiologic.meta.getsro() as a way to analyze the signature resolution order, that is, the chain of (probably) callable objects that have a direct impact on the signature of the passed callable object. Inspired by the inspect.signature() implementation.

Changed

  • All functions that previously used inspect.iscoroutinefunction() now rely on aiologic.meta.iscoroutinefactory(). This makes detecting asynchronous functions (coroutine factories) more accurate and also allows them to be marked on any version of Python.
  • The aiologic.meta.SingletonEnum constructor now reverts to the original behavior if there are zero or more than one members, or if additional parameters are passed, which should make it safer.
  • aiologic.meta.SingletonEnum instances now allow setting attributes for any properties (descriptors) that have a setter. Previously, only slot descriptors (member descriptors) were supported, which prevented any user-defined setter from being called.
  • aiologic.meta.replaces() now raises a LookupError with an informative message instead of a KeyError if there is no function of the same name in the namespace, which should make debugging easier.
  • aiologic.meta.replaces() now preserves the original function type in annotations, while requiring the __name__ attribute to be present in the passed function, which should make it more convenient and safer to use for custom callable objects.
  • aiologic.meta.copies() now performs shallow copying of mutable objects such as __kwdefaults__ and __annotations__ to avoid propagating changes to the copy's signature when the original function's signature changes.
  • aiologic.meta.copies() now forces copying when both functions are the same object, which increases its scope of application.
  • aiologic.meta.import_from() now uses typing.Any as the return type instead of object to avoid using typing.cast() for dynamic imports in __init__ modules.

Deprecated

  • The copy() methods (see below).
  • Pickling/copying by state (for queues and flags). This behavior was inconsistent with other primitives because these ones were interpreted as collections, which does more harm than good. It also makes it hard to optimize queues.

Fixed

  • Low-level Trio waiters allowed multiple reschedules for the same instance, which caused Trio to crash at its underlying assert. Only direct use of them by the user was affected (low-level events already prevent multiple wake() calls in general, so no high-level primitives were affected). See fleming79/async-kernel#493 (comment) for details.
  • Functions with sphinx.ext.autodoc-specific paths did not have the expected behavior due to a peculiarity of module reloading. Now they also check environment variables.
  • Since aiologic.meta.export() includes all public names in __all__, __future__.annotations and typing.TYPE_CHECKING were also included (as 'annotations' and 'TYPE_CHECKING', respectively) if they were imported in __init__.py.
  • aiologic.meta.export_dynamic() and aiologic.meta.export_deprecated() raised a RuntimeError when registering already registered symbolic links, which prevented the module from being reloaded. Now they allow re-registration for the same values.