Version history

This library adheres to Semantic Versioning 2.0.

UNRELEASED

As this is a major release with several backwards incompatible changes, see the Migration guide if you’re upgrading your existing application.

  • BACKWARD INCOMPATIBLE Changes in the application runner:

    • Asphalt now runs via AnyIO, rather than asyncio, although the asyncio backend is used by default

    • The runner now outputs an elaborate tree of component startup tasks if the application fails to start within the allotted time

    • Dropped the --unsafe switch for asphalt run – configuration files are now always parsed in unsafe mode

    • Changed configuration parsing to no longer treat dotted keys in configuration overrides to be interpreted as structural shortcuts (i.e. foo.bar.baz interpreted as a shortcut for a structure 3 levels deep: foo: {bar: {baz: ...}}), as this prevented proper configuration for loggers that have dots in their names

    • Added a shortcut for specify multiple components of the same type using a scheme like mycomponenttype/name1, mycomponenttype/name2 where mycomponenttype is used as the type for both components

    • Switched from ruamel.yaml to PyYAML as the backing YAML library

    • Changed how CLIApplicationComponent is run – their run() method is now called directly by the runner after the component has started

  • BACKWARD INCOMPATIBLE Changes in concurrency handling:

    • Added the start_service_task() function for starting background service tasks, bound to the root context, from Component.start()

    • Added the start_background_task_factory() function for launching ad-hoc background tasks in a manner compatible with Structured Concurrency

    • Dropped the @executor decorator

    • Dropped all context-bound methods of jumping to and from worker threads (Context.call_async(), Context.call_in_executor(), Context.threadpool())

  • BACKWARD INCOMPATIBLE Changes in the component API:

    • Dropped the ctx parameter from Component.start() and CLIApplicationComponent.run()

    • Changed how CLIApplicationComponent works: they no longer start a service task that call the run() method, but instead the runner will call it directly

    • Added the start_component() function which is now the preferred method for starting components directly (e.g. in test suites)

  • BACKWARD INCOMPATIBLE Changes in (Asphalt) context handling:

    • Dropped the TeardownError exception in favor of PEP 654 exception groups

    • Dropped the Context.loop attribute

    • Dropped the Context.context_chain attribute

    • Dropped the Context.close() method (Context objects are now required to be used as context managers)

    • Dropped the deprecated ability to use a Context as a synchronous context manager

    • Dropped the deprecated parent argument to Context

    • Dropped support for context attributes

    • Dropped the ctx parameter from resource factory callbacks

    • Refactored the Context.get_resource(), Context.require_resource() and Context.request_resource() methods (and their free-function counterparts) to just two:

      • get_resource(): a coroutine function, capable of triggering asynchronous resource factories, (and when called during component start-up, optionally waits for the resource to become available)

      • get_resource_nowait() the synchronous counterpart to the above, but incapable of waiting for resources or triggering asynchronous resource factories

    • Contexts now make a shallow copy of the non-generated resources, and resource factories, from their parents rather than allowing lookup from parents. This was done to prevent service tasks from using resources added later in the application startup cycle which would be torn down before the tasks.

  • BACKWARD INCOMPATIBLE Dropped the deprecated Dependency() marker

  • BACKWARD INCOMPATIBLE Changes in the event system:

    • The connect(), disconnect() and dispatch_raw() methods were removed

    • The dispatch() method now accepts only an Event object and fills in the source, topic and time attributes

    • The Event class no longer has an initializer, so subclasses don’t have to call super().__init__() anymore

  • BACKWARD INCOMPATIBLE Moved all exported functions and classes directly to asphalt.core and made submodules private

  • Added support for Python 3.13

  • Dropped support for Python 3.7 and 3.8

4.12.0

  • Removed explicit run-time argument type checks and the typeguard dependency

4.11.1 (2023-03-23)

  • Worked around the presence of the importlib_metadata module on Python 3.10 and later causing PluginContainer.create_component() to fail with AttributeError

4.11.0 (2022-12-03)

  • Added the --set option for asphalt run for overriding specific values in configuration (PR by David Brochart)

  • Made configuration files optional (PR by David Brochart)

4.10.1 (2022-06-10)

  • Fixed root context being unset as the current context before its close() method is run

4.10.0 (2022-05-27)

  • Changed @executor to propagate the PEP 567 context to the worker thread, just like Context.call_in_executor()

4.9.1 (2022-05-22)

  • Fixed type annotation for @context_teardown

  • Improved type annotations

4.9.0 (2022-05-22)

  • Added asphalt.core.get_resources() as a top-level shortcut for current_context().get_resources(...)

  • Allowed resource retrieval and generation in teardown callbacks until the context has been completely closed (this would previously raise RuntimeError("this context has already been closed"))

  • Allowed specifying optional dependencies with dependency injection, using either Optional[SomeType] (all Python versions) or SomeType | None (Python 3.10+)

  • Allowed omitting the types argument in Context.add_resource_factory() if the factory callable has a return type annotation

  • Context.call_in_executor() now copies the current PEP 567 context to the worker thread, allowing operations that require the “current context” to be present (e.g. dependency injection)

  • Raise better errors when the developer forgets to call resource() or forgets to add the @inject decorator

  • Raise a UserWarning when @inject is used on a function that has no resource() declarations

  • Fixed @inject not resolving forward references in type annotations in locally defined functions

  • Improved type annotations

4.8.0 (2022-04-28)

  • Context now accepts parametrized generic classes as resource types

  • Deprecated context variables in favor of dependency injection

  • Replaced asphalt.core.context.Dependency with asphalt.core.context.resource due to issues with strict type checking (the former is now deprecated and will be removed in v5.0)

  • Added support for dependency injection on synchronous functions

  • Added shortcut functions for obtaining resources (asphalt.core.get_resource(), asphalt.core.require_resource())

  • Fixed dependency injection not working with forward references (from __future__ import annotations)

4.7.0 (2022-04-08)

  • Removed all uses of Python 3.5 style await yield_() from core code and documentation

  • Added tracking of current Asphalt context in a PEP 555 context variable, available via current_context()

  • Added dependency injection in coroutine functions via Dependency() and inject()

4.6.0 (2021-12-15)

  • Added support for Python 3.10

  • Dropped support for Python 3.5 and 3.6

  • Removed all uses of the loop argument (fixes Python 3.10 compatibility)

  • Switched to v0.15+ API of ruamel.yaml

  • Switched from pkg_resources to importlib.metadata for loading entry points

  • Fixed DeprecationWarning about passing coroutine objects to asyncio.wait()

  • Fixed DeprecationWarning about implicitly creating a new event loop using get_event_loop()

  • Added the py.typed marker to enable type checking with dependent projects

  • Deprecated the use of Context as a synchronous context manager

4.5.0 (2019-03-26)

  • Added new custom YAML tags (!Env, !BinaryFile and !TextFile)

4.4.4 (2018-05-08)

  • Changed the async_timeout dependency to allow the 3.x and newer releases

4.4.3 (2018-02-05)

  • Fixed exception in stream_events() cleanup code introduced in the previous release

4.4.2 (2018-02-02)

  • Fixed memory leak when stream_events() is called but the returned generator is never used

4.4.1 (2018-01-21)

  • Fixed incompatibility with Python 3.5.2

4.4.0 (2017-11-25)

  • Removed the requirement for async generators to yield at least once when wrapped with @context_teardown

  • Removed aiogevent support since it has been removed from PyPI

4.3.0 (2017-11-05)

  • The runner now calls logging.shutdown() after the event loop has been closed

  • Added the Context.get_resources() method

  • Made stream_events() connect to the signal when called instead of the first iteration of the async generator

4.2.0 (2017-08-24)

  • Allowed selecting the service to run with asphalt run using an environment variable (ASPHALT_SERVICE)

4.1.0 (2017-08-18)

  • Added support for the Tokio event loop

  • Added a feature to the runner that lets one define multiple services in a configuration file and select which one to run

  • Increased the runner default start timeout to 10 seconds

4.0.0 (2017-06-04)

  • BACKWARD INCOMPATIBLE When a teardown callback raises an exception during Context.close(), a TeardownException is raised at the end instead of the error being logged

  • Renamed the asphalt.core.command module to asphalt.core.cli

  • Fixed the inability to override the component type from external configuration (contributed by Devin Fee)

3.0.2 (2017-05-05)

  • Fixed CLIApplicationComponent running prematurely (during the application start phase) and skipping the proper shutdown sequence

  • Fixed return code from CLIApplicationComponent being ignored

3.0.1 (2017-04-30)

  • Fixed run_application() not working on Windows due to NotImplementedError when adding the SIGTERM signal handler

3.0.0 (2017-04-10)

  • BACKWARD INCOMPATIBLE Upped the minimum Python version to 3.5.2 from 3.5.0

  • BACKWARD INCOMPATIBLE Renamed the asphalt.core.util module to asphalt.core.utils

  • The asphalt.core.event module was overhauled:

    • BACKWARD INCOMPATIBLE Removed the monotime attribute from the Event class

    • BACKWARD INCOMPATIBLE Dropped the return_future argument from Signal.dispatch() and Signal.dispatch_event() – they now always return an awaitable that resolves to a boolean, indicating whether all callbacks were successful or not

    • BACKWARD INCOMPATIBLE Made the max_queue_size argument in Signal.stream_events and stream_events() into a keyword-only argument

    • BACKWARD INCOMPATIBLE Signal.dispatch_event() was renamed to Signal.dispatch_raw()

    • Added the filter argument to Signal.stream_events() and stream_events() which can restrict the events that are yielded by them

    • Added the time constructor argument to the Event class

  • The asphalt.core.context module was overhauled:

    • “lazy resources” are now called “resource factories”

    • Context.get_resources() now returns a set of ResourceContainer (instead of a list)

    • BACKWARD INCOMPATIBLE The default_timeout parameter was removed from the Context constructor

    • BACKWARD INCOMPATIBLE The timeout parameter of

      Context.request_resource() was removed

    • BACKWARD INCOMPATIBLE The alias parameter of Context.request_resource() was renamed to name

    • BACKWARD INCOMPATIBLE Removed the Context.finished signal in favor of the new add_teardown_callback() method which has different semantics (callbacks are called in LIFO order and awaited for one at a time)

    • BACKWARD INCOMPATIBLE Removed the ability to remove resources from a Context

    • Added several new methods to the Context class: close(), get_resource(), require_resource()

    • BACKWARD INCOMPATIBLE Context.publish_resource() was renamed to Context.add_resource()

    • BACKWARD INCOMPATIBLE Context.publish_lazy_resource() was renamed to Context.add_resource_factory()

    • BACKWARD INCOMPATIBLE The Context.get_resources() method was removed until it can be replaced with a better thought out API

    • BACKWARD INCOMPATIBLE The Resource class was removed from the public API

    • Three new methods were added to the Context class to bridge asyncio_extras and Executor resources: call_async(), call_in_executor() and threadpool()

    • Added a new decorator, @executor to help run code in specific Executor resources

  • The application runner (asphalt.core.runner) got some changes too:

    • BACKWARD INCOMPATIBLE The runner no longer cancels all active tasks on exit

    • BACKWARD INCOMPATIBLE There is now a (configurable, defaults to 5 seconds) timeout for waiting for the root component to start

    • Asynchronous generators are now closed after the context has been closed (on Python 3.6+)

    • The SIGTERM signal now cleanly shuts down the application

  • Switched from asyncio_extras to async_generator as the async generator compatibility library

  • Made the current event loop accessible (from any thread) as the loop property from any asphalt.core.context.Context instance to make it easier to schedule execution of async code from worker threads

  • The asphalt.core.utils.merge_config() function now accepts None as either argument (or both)

2.1.1 (2017-02-01)

  • Fixed memory leak which prevented objects containing Signals from being garbage collected

  • Log a message on startup that indicates whether optimizations (-O or PYTHONOPTIMIZE) are enabled

2.1.0 (2016-09-26)

  • Added the possibility to specify more than one configuration file on the command line

  • Added the possibility to use the command line interface via python -m asphalt ...

  • Added the CLIApplicationComponent class to facilitate the creation of Asphalt based command line tools

  • Root component construction is now done after installing any alternate event loop policy provider

  • Switched YAML library from PyYAML to ruamel.yaml

  • Fixed a corner case where in wait_event() the future’s result would be set twice, causing an exception in the listener

  • Fixed coroutine-based lazy resource returning a CoroWrapper instead of a Future when asyncio’s debug mode has been enabled

  • Fixed a bug where a lazy resource would not be created separately for a context if a parent context contained an instance of the same resource

2.0.0 (2016-05-09)

  • BACKWARD INCOMPATIBLE Dropped Python 3.4 support in order to make the code fully rely on the new async/await, async for and async with language additions

  • BACKWARD INCOMPATIBLE De-emphasized the ability to implicitly run code in worker threads. As such, Asphalt components are no longer required to transparently work outside of the event loop thread. Instead, use asyncio_extras.threads.call_async()` to call asynchronous code from worker threads if absolutely necessary. As a direct consequence of this policy shift, the ``asphalt.core.concurrency module was dropped in favor of the asyncio_extras library.

  • BACKWARD INCOMPATIBLE The event system was completely rewritten:

    • instead of inheriting from EventSource, event source classes now simply assign Signal instances to attributes and use object.signalname.connect() to listen to events

    • all event listeners are now called independently of each other and coroutine listeners are run concurrently

    • added the ability to stream events

    • added the ability to wait for a single event to be dispatched

  • BACKWARD INCOMPATIBLE Removed the asphalt.command module from the public API

  • BACKWARD INCOMPATIBLE Removed the asphalt quickstart command

  • BACKWARD INCOMPATIBLE Removed the asphalt.core.connectors module

  • BACKWARD INCOMPATIBLE Removed the optional argument of Context.request_resource()

  • BACKWARD INCOMPATIBLE Removed the asphalt.core.runners entry point namespace

  • BACKWARD INCOMPATIBLE Component.start() is now required to be a coroutine method

  • BACKWARD INCOMPATIBLE Removed regular context manager support from the Context class (asynchronous context manager support still remains)

  • BACKWARD INCOMPATIBLE The Context.publish_resource(), Context.publish_lazy_resource() and Context.remove_resource() methods are no longer coroutine methods

  • BACKWARD INCOMPATIBLE Restricted resource names to alphanumeric characters and underscores

  • Added the possibility to specify a custom event loop policy

  • Added support for uvloop

  • Added support for aiogevent

  • Added the ability to use coroutine functions as lazy resource creators (though that just makes them return a Future instead)

  • Added the ability to get a list of all the resources in a Context

  • Changed the asphalt.core.util.resolve_reference() function to return invalid reference strings as-is

  • Switched from argparse to click for the command line interface

  • All of Asphalt core’s public API is now importable directly from asphalt.core

1.2.0 (2016-01-02)

  • Moved the @asynchronous and @blocking decorators to the asphalt.core.concurrency package along with related code (they’re still importable from asphalt.core.util until v2.0)

  • Added typeguard checks to fail early if arguments of wrong types are passed to functions

1.1.0 (2015-11-19)

  • Decorated ContainerComponent.start with @asynchronous so that it can be called by a blocking subclass implementation

  • Added the stop_event_loop function to enable blocking callables to shut down Asphalt’s event loop

1.0.0 (2015-10-18)

  • Initial release