From 9e57359ae3b74853df8f76842be0eee1a0b84267 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Wed, 16 Dec 2020 14:32:15 +0100 Subject: [PATCH 01/12] Prepare first release --- .travis.yml | 40 ++++++++++++++++++++++++++++++++++++++++ README.rst | 10 +++++----- mode/__init__.py | 2 +- setup.py | 1 + tox.ini | 3 ++- 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9a4dca81..547d8a93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -107,6 +107,46 @@ matrix: os: linux dist: xenial stage: test + - python: 3.8.1 + env: TOXENV=3.8 IDENT="3.8.1" RUN_SUITE=y + os: linux + dist: xenial + stage: test + - python: 3.8.2 + env: TOXENV=3.8 IDENT="3.8.2" RUN_SUITE=y + os: linux + dist: xenial + stage: test + - python: 3.8.3 + env: TOXENV=3.8 IDENT="3.8.3" RUN_SUITE=y + os: linux + dist: xenial + stage: test + - python: 3.8.4 + env: TOXENV=3.8 IDENT="3.8.4" RUN_SUITE=y + os: linux + dist: xenial + stage: test + - python: 3.8.5 + env: TOXENV=3.8 IDENT="3.8.5" RUN_SUITE=y + os: linux + dist: xenial + stage: test + - python: 3.8.6 + env: TOXENV=3.8 IDENT="3.8.6" RUN_SUITE=y + os: linux + dist: xenial + stage: test + - python: 3.9.0 + env: TOXENV=3.9 IDENT="3.9.0" RUN_SUITE=y + os: linux + dist: focal + stage: test + - python: 3.9.1 + env: TOXENV=3.9 IDENT="3.9.1" RUN_SUITE=y + os: linux + dist: xenial + stage: test before_install: install: diff --git a/README.rst b/README.rst index 800cf4cc..776f67e1 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ |build-status| |coverage| |license| |wheel| |pyversion| |pyimp| -:Version: 4.3.2 +:Version: 0.1.0 :Web: http://mode-streaming.readthedocs.org/ :Download: http://pypi.org/project/mode-streaming :Source: http://github.com/faust-streaming/mode @@ -251,8 +251,8 @@ http://pypi.org/project/mode-streaming You can install it by doing the following:: - $ tar xvfz mode-streaming-0.0.0.tar.gz - $ cd mode-0.0.0 + $ tar xvfz mode-streaming-0.1.0.tar.gz + $ cd mode-0.1.0 $ python setup.py build # python setup.py install @@ -447,9 +447,9 @@ reported by opening an issue or contacting one or more of the project maintainer This Code of Conduct is adapted from the Contributor Covenant, version 1.2.0 available at http://contributor-covenant.org/version/1/2/0/. -.. |build-status| image:: https://secure.travis-ci.org/faust-streaming/mode.png?branch=master +.. |build-status| image:: https://secure.travis-ci.com/faust-streaming/mode.png?branch=master :alt: Build status - :target: https://travis-ci.org/faust-streaming/mode + :target: https://travis-ci.com/faust-streaming/mode .. |coverage| image:: https://codecov.io/github/faust-streaming/mode/coverage.svg?branch=master :target: https://codecov.io/github/faust-streaming/mode?branch=master diff --git a/mode/__init__.py b/mode/__init__.py index 17c6f3b1..a7211d9a 100644 --- a/mode/__init__.py +++ b/mode/__init__.py @@ -8,7 +8,7 @@ import typing from typing import Any, Mapping, NamedTuple, Sequence -__version__ = '4.3.2' +__version__ = '0.1.0' __author__ = 'Robinhood Markets' __contact__ = 'opensource@robinhood.com' __homepage__ = 'https://github.com/faust-streaming/mode' diff --git a/setup.py b/setup.py index 5747c5ee..970fcb43 100644 --- a/setup.py +++ b/setup.py @@ -32,6 +32,7 @@ def _pyimp(): Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Programming Language :: Python :: Implementation :: CPython Operating System :: POSIX Operating System :: Microsoft :: Windows diff --git a/tox.ini b/tox.ini index 24e95efc..df100ac2 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = 3.8,3.7,3.6,flake8,apicheck,typecheck,docstyle,bandit +envlist = 3.9,3.8,3.7,3.6,flake8,apicheck,typecheck,docstyle,bandit [testenv] deps= @@ -14,6 +14,7 @@ sitepackages = False recreate = False commands = py.test --random-order --open-files -xvv --cov=mode basepython = + 3.9: python3.9 3.8,flake8,typecheck,apicheck,linkcheck,docstyle,bandit: python3.8 3.7: python3.7 3.6: python3.6 From b025ba01804144e27a8c9377fedd38541794bc77 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Wed, 16 Dec 2020 14:34:19 +0100 Subject: [PATCH 02/12] Fix build status png url --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 776f67e1..5da82539 100644 --- a/README.rst +++ b/README.rst @@ -447,7 +447,7 @@ reported by opening an issue or contacting one or more of the project maintainer This Code of Conduct is adapted from the Contributor Covenant, version 1.2.0 available at http://contributor-covenant.org/version/1/2/0/. -.. |build-status| image:: https://secure.travis-ci.com/faust-streaming/mode.png?branch=master +.. |build-status| image:: https://travis-ci.com/faust-streaming/mode.png?branch=master :alt: Build status :target: https://travis-ci.com/faust-streaming/mode From 20b2dbf989b1f3ce5198c91e6dcbfb96c3972fc3 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Wed, 16 Dec 2020 14:36:45 +0100 Subject: [PATCH 03/12] Bump static analysis python version to 3.8.6 --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 547d8a93..b1720cd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,27 +12,27 @@ stages: matrix: include: - - python: 3.8.0 + - python: 3.8.6 env: TOXENV=flake8 os: linux dist: xenial stage: lint - - python: 3.8.0 + - python: 3.8.6 env: TOXENV=typecheck os: linux dist: xenial stage: lint - - python: 3.8.0 + - python: 3.8.6 env: TOXENV=apicheck os: linux dist: xenial stage: lint - - python: 3.8.0 + - python: 3.8.6 env: TOXENV=docstyle os: linux dist: xenial stage: lint - - python: 3.8.0 + - python: 3.8.6 env: TOXENV=bandit os: linux dist: xenial From e56c7f621ec2d96778e7b01eecc870aa28a0e5ee Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Wed, 16 Dec 2020 15:15:36 +0100 Subject: [PATCH 04/12] Disable Python 3.9 due to failing unit tests Will be be re-enabled later --- .travis.yml | 20 ++++++++++---------- setup.py | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index b1720cd3..9ed23691 100644 --- a/.travis.yml +++ b/.travis.yml @@ -137,16 +137,16 @@ matrix: os: linux dist: xenial stage: test - - python: 3.9.0 - env: TOXENV=3.9 IDENT="3.9.0" RUN_SUITE=y - os: linux - dist: focal - stage: test - - python: 3.9.1 - env: TOXENV=3.9 IDENT="3.9.1" RUN_SUITE=y - os: linux - dist: xenial - stage: test + # - python: 3.9.0 + # env: TOXENV=3.9 IDENT="3.9.0" RUN_SUITE=y + # os: linux + # dist: focal + # stage: test + # - python: 3.9.1 + # env: TOXENV=3.9 IDENT="3.9.1" RUN_SUITE=y + # os: linux + # dist: xenial + # stage: test before_install: install: diff --git a/setup.py b/setup.py index 970fcb43..5747c5ee 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,6 @@ def _pyimp(): Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 Programming Language :: Python :: Implementation :: CPython Operating System :: POSIX Operating System :: Microsoft :: Windows From e6e0941c65578f5a40f51fc3445f0f724c158ea6 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Wed, 16 Dec 2020 15:20:05 +0100 Subject: [PATCH 05/12] Upgrade OS used for Python 3.8 tests to latest LTS --- .travis.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ed23691..e7b59991 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,27 +15,27 @@ matrix: - python: 3.8.6 env: TOXENV=flake8 os: linux - dist: xenial + dist: focal stage: lint - python: 3.8.6 env: TOXENV=typecheck os: linux - dist: xenial + dist: focal stage: lint - python: 3.8.6 env: TOXENV=apicheck os: linux - dist: xenial + dist: focal stage: lint - python: 3.8.6 env: TOXENV=docstyle os: linux - dist: xenial + dist: focal stage: lint - python: 3.8.6 env: TOXENV=bandit os: linux - dist: xenial + dist: focal stage: lint - python: 3.6.3 env: TOXENV=3.6 RUN_SUITE=y @@ -105,37 +105,37 @@ matrix: - python: 3.8.0 env: TOXENV=3.8 IDENT="3.8.0" RUN_SUITE=y os: linux - dist: xenial + dist: focal stage: test - python: 3.8.1 env: TOXENV=3.8 IDENT="3.8.1" RUN_SUITE=y os: linux - dist: xenial + dist: focal stage: test - python: 3.8.2 env: TOXENV=3.8 IDENT="3.8.2" RUN_SUITE=y os: linux - dist: xenial + dist: focal stage: test - python: 3.8.3 env: TOXENV=3.8 IDENT="3.8.3" RUN_SUITE=y os: linux - dist: xenial + dist: focal stage: test - python: 3.8.4 env: TOXENV=3.8 IDENT="3.8.4" RUN_SUITE=y os: linux - dist: xenial + dist: focal stage: test - python: 3.8.5 env: TOXENV=3.8 IDENT="3.8.5" RUN_SUITE=y os: linux - dist: xenial + dist: focal stage: test - python: 3.8.6 env: TOXENV=3.8 IDENT="3.8.6" RUN_SUITE=y os: linux - dist: xenial + dist: focal stage: test # - python: 3.9.0 # env: TOXENV=3.9 IDENT="3.9.0" RUN_SUITE=y From 15ffd501de3410fea03be8ae204801f82ee50c99 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Wed, 16 Dec 2020 17:17:29 +0100 Subject: [PATCH 06/12] Update missed files related to bumpversion and documentation --- .bumpversion.cfg | 4 ++-- docs/conf.py | 4 ++-- docs/includes/installation.txt | 10 +++++----- docs/includes/introduction.txt | 8 ++++---- docs/templates/readme.txt | 22 +++++++++++----------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index a4ae49e9..d6aa7c29 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,9 +1,9 @@ [bumpversion] -current_version = 4.3.2 +current_version = 0.1.0 commit = True tag = True parse = (?P\d+)\.(?P\d+)\.(?P\d+)(?P[a-z]+)? -serialize = +serialize = {major}.{minor}.{patch}{releaselevel} {major}.{minor}.{patch} diff --git a/docs/conf.py b/docs/conf.py index 5bec41db..fc73ed25 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,9 +12,9 @@ project='Mode', # version_dev='2.0', # version_stable='1.4', - canonical_url='http://mode.readthedocs.io', + canonical_url='http://mode-streaming.readthedocs.io', webdomain='', - github_project='ask/mode', + github_project='faust-streaming/mode', copyright='2017-2020', html_logo='images/logo.png', html_favicon='images/favicon.ico', diff --git a/docs/includes/installation.txt b/docs/includes/installation.txt index 01080ba8..f53d9414 100644 --- a/docs/includes/installation.txt +++ b/docs/includes/installation.txt @@ -8,7 +8,7 @@ or from source. To install using `pip`:: - $ pip install -U mode + $ pip install -U mode-streaming .. _installing-from-source: @@ -16,12 +16,12 @@ Downloading and installing from source -------------------------------------- Download the latest version of Mode from -http://pypi.org/project/mode +http://pypi.org/project/mode-streaming You can install it by doing the following:: - $ tar xvfz mode-0.0.0.tar.gz - $ cd mode-0.0.0 + $ tar xvfz mode-streaming-0.1.0.tar.gz + $ cd mode-streaming-0.1.0 $ python setup.py build # python setup.py install @@ -39,4 +39,4 @@ With pip You can install the latest snapshot of Mode using the following pip command:: - $ pip install https://github.com/ask/mode/zipball/master#egg=mode + $ pip install https://github.com/faust-streaming/mode/zipball/master#egg=mode diff --git a/docs/includes/introduction.txt b/docs/includes/introduction.txt index 9b066abe..e19da94f 100644 --- a/docs/includes/introduction.txt +++ b/docs/includes/introduction.txt @@ -1,7 +1,7 @@ -:Version: 4.3.2 -:Web: http://mode.readthedocs.org/ -:Download: http://pypi.org/project/mode -:Source: http://github.com/ask/mode +:Version: 0.1.0 +:Web: http://mode-streaming.readthedocs.org/ +:Download: http://pypi.org/project/mode-streaming +:Source: http://github.com/faust-streaming/mode :Keywords: async, service, framework, actors, bootsteps, graph What is Mode? diff --git a/docs/templates/readme.txt b/docs/templates/readme.txt index 3835d7ee..7e23aaf3 100644 --- a/docs/templates/readme.txt +++ b/docs/templates/readme.txt @@ -12,25 +12,25 @@ .. include:: ../includes/code-of-conduct.txt -.. |build-status| image:: https://secure.travis-ci.org/ask/mode.png?branch=master +.. |build-status| image:: https://travis-ci.com/faust-streaming/mode.png?branch=master :alt: Build status - :target: https://travis-ci.org/ask/mode + :target: https://travis-ci.com/faust-streaming/mode -.. |coverage| image:: https://codecov.io/github/ask/mode/coverage.svg?branch=master - :target: https://codecov.io/github/ask/mode?branch=master +.. |coverage| image:: https://codecov.io/github/faust-streaming/mode/coverage.svg?branch=master + :target: https://codecov.io/github/faust-streaming/mode?branch=master -.. |license| image:: https://img.shields.io/pypi/l/mode.svg +.. |license| image:: https://img.shields.io/pypi/l/mode-streaming.svg :alt: BSD License :target: https://opensource.org/licenses/BSD-3-Clause -.. |wheel| image:: https://img.shields.io/pypi/wheel/mode.svg +.. |wheel| image:: https://img.shields.io/pypi/wheel/mode-streaming.svg :alt: Mode can be installed via wheel - :target: http://pypi.org/project/mode/ + :target: http://pypi.org/project/mode-streaming/ -.. |pyversion| image:: https://img.shields.io/pypi/pyversions/mode.svg +.. |pyversion| image:: https://img.shields.io/pypi/pyversions/mode-streaming.svg :alt: Supported Python versions. - :target: http://pypi.org/project/mode/ + :target: http://pypi.org/project/mode-streaming/ -.. |pyimp| image:: https://img.shields.io/pypi/implementation/mode.svg +.. |pyimp| image:: https://img.shields.io/pypi/implementation/mode-streaming.svg :alt: Supported Python implementations. - :target: http://pypi.org/project/mode/ + :target: http://pypi.org/project/mode-streaming/ From 427379bb12db363b8f6234562bcc353883ce7546 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Wed, 16 Dec 2020 18:08:23 +0100 Subject: [PATCH 07/12] Update changelog --- Changelog | 1878 +---------------------------------------------------- 1 file changed, 8 insertions(+), 1870 deletions(-) diff --git a/Changelog b/Changelog index eef67d15..055bcf3b 100644 --- a/Changelog +++ b/Changelog @@ -4,1879 +4,17 @@ Change history ================ -.. version-4.4.0: +.. version-0.1.0: -4.4.0 +0.1.0 ===== -:release-date: 2020-09-28 11:00 A.M PST -:release-by: Ask Solem (:github_user:`ask`) +:release-date: 2020-12-16 06:00 P.M CET +:release-by: Thomas Sarboni (:github_user:`max-k`) -- Supervisors now raises :exc:`SystemExit` on max restarts exceeded. +- Friendly fork of ask/mode : Initial release -- Queue: Now includes high pressure and pressure drop conditions. +- Move to new travis-ci.com domain - This means you can now add callbacks when the queue is under high - pressure, and for when the pressure drops again. +- Add tests on Python 3.8.1-3.8.6 -- Service: Added ``human_tracebacks()`` method. - - -.. _version-4.3.2: - -4.3.2 -===== -:release-date: 2020-02-13 3:21 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Tracebacks: Added support for async generators. - - Also added new functions: - - + :func:`~mode.utils.tracebacks.format_agen_stack` - + :func:`~mode.utils.tracebacks.print_agen_stack` - -- Logging: New version of log argument formatters. - - The existing log argument formatter callbacks did - not have access to the original log record, so could - not make decisions based on matching the log message string - for example. - - A new :func:`~mode.utils.logging.formatter2` decorator - has been added that registers callbacks taking two arguments. - - Example: - - .. sourcecode:: python - - from mode.utils.logging import formatter2 - - @formatter2 - def format_log_argument(arg: Any, record: logging.LogRecord): - # improve aiokafka logging to sort the list - # of topics logged when subscribing to topics - # and make it more human readable - if record.msg.startswith('Subscribing to topics'): - if isinstance(arg, frozenset): - return ', '.join(sorted(arg)) - -.. _version-4.3.1: - -4.3.1 -===== -:release-date: 2020-02-10 2:40 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Added :func:`mode.utils.times.humanize_seconds_ago` - - This formats seconds float to text "n seconds ago", - or "just now" if less than one second. - -- Added :func:`mode.utils.text.enumeration` - - This formats a list of strings to a enumerated list, - for example: - - .. sourcecode:: pycon - - >>> text.enumeration(['x', 'y', '...']) - "1) x\n2) y\n3) ..." - -.. _version-4.3.0: - -4.3.0 -===== -:release-date: 2020-01-22 2:25 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Threads: Now runs with :envvar:`PYTHONASYNCIODEBUG`. - -- Threads: Fixed race condition where pending methods are lost - -- utils.tracebacks: Adds ``print_coro_stack`` and - ``format_coro_stack`` for debugging coroutines. - -- Timers: Adds sleeptime and runtime to logs for more info. - -- Threads: Threads now do two-way keepalive (thread -> parent, parent -> thread) - -- Service: ``add_future`` now sets task name - -- Worker: ``SIGUSR2`` now starts :mod:`pdb` session. - -.. _version-4.2.0: - -4.2.0 -===== -:release-date: 2020-01-15 5:00 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Timers: Exclude timer callback time in drift calculation - -- Service: `maybe_start()` now returns bool, and :const:`True` - if the service was started. - -.. _version-4.1.9: - -4.1.9 -===== -:release-date: 2020-01-13 11:18 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- QueueServiceThread: Stop method queue before stopping child services. - -- Small fixes to render graph images correctly. - -.. _version-4.1.8: - -4.1.8 -===== -:release-date: 2020-01-07 4:00 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- ``ServiceThread.crash()`` now immediately sets exception - when called in main thread. - -.. _version-4.1.7: - -4.1.7 -===== -:release-date: 2020-01-07 3:20 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Now depends on :pypi:`typing_extensions` (Issue #53) - - This dependency was previously missing resulting in errors. - -- Fixed :class:`~mode.threads.ServiceThread` hang on exceptions raised - (Issue #54). - - Contributed by Alexey Basov (:github_user:`r313pp`) - and Jonathan Booth (:github_user:`jbooth-mastery`). - - - -.. _version-4.1.6: - -4.1.6 -===== -:release-date: 2019-12-12 2:30 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- logging: Fixes recursion error on Python 3.6 (Issue #52) - -- Makefile: Added `make develop` to install dependencies into the currently - activated virtualenv. - -- Tests: Removed `was never awaited` warning during test run. - -- CI: Revert back to using Python 3.7.5 for Windows build. - -.. _version-4.1.5: - -4.1.5 -===== -:release-date: 2019-12-11 3:45 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Tests passing on Python 3.8.0 - -- Fixed typing related issues. - -- Added :file:`__init__.py` to `mode.utils` package. - -.. _version-4.1.4: - -4.1.4 -===== -:release-date: 2019-10-30 3:23 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Timers: Do not log drift for subsecond interval timers. - -.. _version-4.1.3: - -4.1.3 -===== -:release-date: 2019-10-29 2:14 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Service: Ignore :exc:`asyncio.CancelledError` when a child service - is stopped. - -- flight recorder: Adds ability to add logging extra-data that - propagates to child recorders. - - -.. _version-4.1.2: - -4.1.2 -===== -:release-date: 2019-10-02 3:41 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Adds easy flight recorder interface. - - Instead of passing flight recorders around, we now have the concept - of a ``current_flight_recorder``. - - There is also a new ``on_timeout`` wrapper object that always - logs to the current flight recorder: - - .. sourcecode:: python - - from mode.utils.logging import flight_recorder, on_timeout - - async def main(): - with flight_recorder(logger, timeout=300): - some_function() - - def some_function(): - on_timeout.error('Fetching data') - fetch_data() - - on_timeout.error('Processing data') - process_data() - - This uses a :class:`~mode.utils.locals.LocalStack`, - so flight recorders can be arbitrarily nested. - Once a flight recorder context exits, the previously active flight - recorder will be set active again. - -- Logging: Default logging format now includes process PID. - -- Adds :class:`~mode.utils.collections.Heap` as generic interface - to the heapq module - -.. _version-4.1.1: - -4.1.1 -===== -:release-date: 2019-10-02 3:41 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- ``is_optional``: Now works with union having multiple args on Python 3.6. - -- ``Proxy``: Due to `Python issue #29581`_ the ``source`` class option - to :class:`~mode.locals.Proxy` cannot be used on Python 3.6. - - To work around this when support for 3.6 is a requirement - you can now use a ``__proxy_source__`` class attribute instead: - - .. sourcecode:: python - - class MappingProxy(Proxy[Mapping]): - proxy_source__ = Mapping - - you want to support 3.7 and up you can continue to use the class syntax: - - .. sourcecode:: python - - class MappingProxy(Proxy[Mapping], source=Mapping): - ... - -.. _`Python issue #29581`: https://bugs.python.org/issue29581 - -.. _version-4.1.0: - -4.1.0 -===== -:release-date: 2019-09-27 2:00 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Worker: Adds ``override_logging`` option to use your own logging - setup (Issue #46). - -- Service: Adds ``Service.crash_reason`` (Issue #50). - -- Service: Adds ``remove_dependency`` to disable dependencies at runtime - (Issue #49). - - Contributed by Martin Maillard. - -- Timers: Increase max drift to silence noisy logs. - -- Proxy: Adds support for lazy proxying of objects. - - See :mod:`mode.locals`. - -- Documentation improvements by: - - + :github_user:`tojkamaster`. - - + :github_user:`casio` - -.. _version-4.0.1: - -4.0.1 -===== -:release-date: 2019-07-17 3:42 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Utils: :class:`~mode.utils.future.stampede` objects can now be - read by :pypi:`Sphinx` and :func:`inspect.signature`. - -- CI: Adds CPython 3.7.3 to build matrix, and set as default for lint stages - -.. _version-4.0.0: - -4.0.0 -===== -:release-date: 2019-05-07 2:21 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- 100% Test Coverage - -- Fixed several edge case bugs that were never reported. - -.. _version-3.2.2: - -3.2.2 -===== -:release-date: 2019-04-07 6:18 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- :class:`~mode.utils.typing.AsyncGenerator` takes two arguments. - - Mistakenly had it take three arguments, like :class:`typing.Generator`.S - -.. _version-3.2.1: - -3.2.1 -===== -:release-date: 2019-04-07 4:07 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Adds :class:`mode.utils.typing.AsyncGenerator` - to import :class:`typing.AsyncGenerator` missing from Python 3.6.0. - -.. _version-3.2.0: - -3.2.0 -===== -:release-date: 2019-04-06 11:00 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Adds ``Service.itertimer``: used to perform periodic - tasks, but with automatic drift adjustment. - -- Adds :func:`mode.utils.mocks.ContextMock` - - To mock a regular context manager. - -.. _version-3.1.3: - -3.1.3 -===== -:release-date: 2019-04-04 08:41 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- ``mode.utils.worker.exiting`` now takes option to print exceptions. - -- Threads: Method queue "starting..." logs now logged with debug severity. - -- Worker: execute_from_commandline no longer swallow errors if loop closed. - -- Adds :class:`mode.locals.LocalStack`. - -.. _version-3.1.2: - -3.1.2 -===== -:release-date: 2019-04-04 08:37 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -+ **Revoked release**: Version without changelog entry was uploaded to PyPI. - Please upgrade to 3.1.3. - -.. _version-3.1.1: - -3.1.1 -===== -:release-date: 2019-03-27 10:02 A.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Service: property ``should_stop`` is now true if service crashed. - -- Timers: Avoid drift + introduce a tiny bit of drift to timers. - - Thanks to Bob Haddleton (:github_user:`bobh66`) for discovering - this issue. - -.. _version-3.1.0: - -3.1.0 -===== -:release-date: 2019-03-21 03:26 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Adds :class:`~mode.utils.contexts.nullcontext` - and :class:`~mode.utils.contexts.asyncnullcontext`. - - Backported from Python 3.7 you can import these - from :mod:`mode.utils.contexts`. - -- Mode project changes: - - + Added :pypi:`bandit` to CI lint build. - - + Added :pypi:`pydocstyle` to CI lint build. - -.. _version-3.0.13: - -3.0.13 -====== -:release-date: 2019-03-20 04:58 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Adds ``CompositeLogger.warning`` alias to ``warn``. - - :pypi:`flake8-logging-format` has a rule that says - you are only allowed to use ``.warning``, so going with that. - -.. _version-3.0.12: - -3.0.12 -====== -:release-date: 2019-03-20 03:23 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Adds :func:`~mode.utils.futures.all_tasks` as a backward compatible - :func:`asyncio.all_tasks`. - -- Signal: Fixes ``.connect()`` decorator to work with parens and without - - Signal decorator now works with parens: - - .. sourcecode:: python - - @signal.connect() - def my_handler(sender, **kwargs): - ... - - and without parens: - - .. sourcecode:: python - - @signal.connect - def my_handler(sender, **kwargs): - ... - -- Signal: Do not use weakref by default. - - Using weakref by default meant it was too easy to connect - a signal handler to only have it disappear because there were - no more references to the object. - -.. _version-3.0.11: - -3.0.11 -====== -:release-date: 2019-03-19 08:50 A.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Adds ThrowableQueue._throw() for non-async version of .throw(). - -.. _version-3.0.10: - -3.0.10 -====== -:release-date: 2019-03-14 03:55 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Worker: was giving successful exit code when an exception is raised. - - The ``.execute_from_commandline`` method now always exits - (its return type is :class:`typing.NoReturn`). - -- Adds :class:`~mode.utils.compat.NoReturn` to :mod:`mode.utils.compat`. - - Import :class:`typing.NoReturn` from here to support Python versions - before 3.6.3. - -.. _version-3.0.9: - -3.0.9 -===== -:release-date: 2019-03-08 01:20 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Threads: Add multiple workers for thread method queue. - - Default number of workers is now 2, to allow for - two recursive calls. - -- Signal: Use `Signal.label` instead of ``.indent`` to be more consistent. - -- Signal: Properly set ``.name`` when signal is member of class. - -- Adds ability to log the FULL traceback of :class:`asyncio.Task`. - -- Service: Stop faster if stopped immediately after start - -- Service: Correctly track dependencies for services added - using ``Service.on_init_dependencies`` (Issue #40). - - Contributed by Nimi Wariboko Jr (:github_user:`nemosupremo`). - -.. _version-3.0.8: - -3.0.8 -===== -:release-date: 2019-01-25 03:54 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Fixes ``DeprecationWarning`` importing from ``collections``. - -- stampede: Fixed edge case where stampede wrapped function - called multiple times. - - Calling the same stampede wrapped function multiple times - within the same event loop iteration would previously call - the function multiple times. - - For example using :func:`asyncio.gather`: - - .. sourcecode:: python - - from mode.utils.futures import stampede - - count = 0 - @stampede - async def update_count(): - global count - count += 1 - - async def main(): - await asyncio.gather( - update_count(), - update_count(), - update_count(), - update_count(), - ) - - assert count == 1 - - Previously this would call the function four times, but with the - fix it's only called once and provides the expected result. - -- Mocks: Adds :func:`~mode.utils.mocks.mask_module` and - :func:`~mode.utils.mocks.patch_module`. - -- CI: Added Windows build. - -- CI: Enabled random order for tests. - - -.. _version-3.0.7: - -3.0.7 -===== -:release-date: 2019-01-18 01:12 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- **ServiceThread** ``.stop()`` would wait for thread shutdown - even if thread was never started. - -- **CI**: Adds CPython 3.7.2 and 3.6.8 to build matrix - -.. _version-3.0.6: - -3.0.6 -===== -:release-date: 2019-01-07 12:10 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Adds ``%(extra)s`` as log format option. - - To add additional context to your logging statements use for example:: - - logger.error('Foo', extra={'data': {'database': 'db1'}}) - -.. _version-3.0.5: - -3.0.5 -===== -:release-date: 2018-12-19 04:40 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Fixes compatibility with :pypi:`colorlog` 4.0.x. - - Contributed by Ryan Whitten (:github_user:`rwhitten577`). - -.. _version-3.0.4: - -3.0.4 -===== -:release-date: 2018-12-07 04:40 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Now depends on :pypi:`mypy_extensions`. - -.. _version-3.0.3: - -3.0.3 -===== -:release-date: 2018-12-07 3:22 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Threads: Fixed delay in shutdown if ``on_thread_stop`` callback raises - exception. - -- Service: Stopping of children no longer propagates exceptions, to ensure - other services are still stopped. - -- Worker: Fixed race condition if worker stopped before being fully started. - - This would lead the worker to shutdown early before fully stopping - all dependent services. - -- Tests: Adds :class:`~mode.utils.mocks.AsyncMagicMock` - -.. _version-3.0.2: - -3.0.2 -===== -:release-date: 2018-12-07 1:14 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Worker: Fixes crash on Windows where signal handlers cannot be registered. - -- Utils: Adds :func:`~mode.utils.objects.shortname` to get non-qualified - object path. - -- Utils: Adds :func:`~mode.utils.objects.canonshortname` to get non-qualified - object path that attempts to resolve the real name of ``__main__``. - -.. _version-3.0.1: - -3.0.1 -===== -:release-date: 2018-12-06 10:20 A.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Worker: Added new callback ``on_worker_shutdown``. - -- Worker: Do not stop twice, instead wait for original stop to complete. - - Signals would start multiple stopping coroutines, leading to - the worker shutting down too fast. - -- Threads: All ``ServiceThread`` services needs a keepalive - coroutine to be scheduled. - -- Supervisor: Fixed issue with ``CrashingSupervisor`` where - service would not crash. - -.. _version-3.0.0: - -3.0.0 -===== -:release-date: 2018-11-30 4:48 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- ``ServiceThread`` no longer uses ``run_in_executor``. - - Since services are long running, it is not a good idea for - them to block pool worker threads. Instead we run one - thread for every ServiceThread. - -- Adds :class:`~mode.threads.QueuedServiceThread` - - This subclass of :class:`~mode.threads.ServiceThread` enables - the use of a queue to send work to the service thread. - - This is useful for services that wrap blocking network clients - for example. - - If you have a blocking Redis client you could run it in a separate - thread like this: - - .. code-block:: python - - class Redis(QueuedServiceThread): - _client: StrictRedis = None - - async def on_start(self) -> None: - self._client = StrictRedis() - - async def get(self, key): - return await self.call_thread(self._client.get, key) - - async def set(self, key, value): - await self.call_thread(self._client.set, key, value) - - The actual redis client will be running in a separate thread (with a - separate event loop). The ``get`` and ``set`` methods will delegate - to the thread, and return only when the thread is finished handling - them and is ready with a result: - - .. sourcecode:: python - - async def use_redis(): - # We use async-with-statement here, but - # can also do `await redis.start()` then `await redis.stop()` - async with Redis() as redis: - await redis.set(key='foo', value='bar') - assert await redis.get(key='foo') == 'bar' - -- Collections: ``FastUserSet`` and ``ManagedUserSet`` now implements - all :class:`set` operations. - -- Collections are now generic types. - - You can now subclass collections with typing information: - - - ``class X(FastUserDict[str, int]): ...`` - - ``class X(ManagedUserDict[str, int]): ...`` - - ``class X(FastUserSet[str]): ...`` - - ``class X(ManagedUserSet[str]): ...`` - -- :func:`~mode.utils.futures.maybe_async` utility now - also works with ``@asyncio.coroutine`` decorated coroutines. - -- Worker: SIGUSR1 cry handler: Fixed crash when coroutine does not have - ``__name__`` attribute. - -.. _version-2.0.4: - -2.0.4 -===== -:release-date: 2018-11-19 1:07 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- ``FlowControlQueue.clear`` now cancels all waiting for ``Queue.put``. - -.. _version-2.0.3: - -2.0.3 -===== -:release-date: 2018-11-05 5:20 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Adds `Service.wait_first(*coros)` - - Wait for the first coroutine to return, where coroutines can also - be :class:`asyncio.Event`. - - Returns :class:`mode.services.WaitResults` with fields: - - - ``.done`` - List of arguments that are now done. - - ``.results`` - List of return values in order of ``.done``. - - ``.stopped`` - Set to True if the service was stopped. - -.. _version-2.0.2: - -2.0.2 -===== -:release-date: 2018-11-03 9:07 A.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Now depends on :pypi:`aiocontextvars` 0.2 - - This release uses :pep:`508` syntax for conditional requirements, - as :ref:`version-2.0.1` did not work when installing wheel. - -.. _version-2.0.1: - -2.0.1 -===== -:release-date: 2018-11-02 7:38 P.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Now depends on :pypi:`aiocontextvars` 0.2 - -.. _version-2.0.0: - -2.0.0 -===== -:release-date: 2018-11-02 9:12 A.M PST -:release-by: Ask Solem (:github_user:`ask`) - -- Services now create the event loop on demand. - - This means the event loop is no longer created in `Service.__init__` - so that services can be defined at module scope without initializing - the loop. - - This makes the ``ServiceProxy`` pattern redundant for most use cases. - -- Adds ``.utils.compat.current_task`` as alias for - :mod:`asyncio.current_task`. - -- Adds support for contextvars in Python 3.6 using :pypi:`aiocontextvars`. - - In mode services you can now use :mod:`contextvars` module even - on Python 3.6, thanks to the work of :github_user:`fantix`. - -.. _version-1.18.2: - -1.18.2 -====== -:release-date: 2018-11-30 6:23 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Worker: SIGUSR1 cry handler: Fixed crash when coroutine does not have - ``__name__`` attribute. - -.. _version-1.18.1: - -1.18.1 -====== -:release-date: 2018-10-03 2:49 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- **Service**: ``Service.from_awaitable(coro)`` improvements. - - The resulting ``service.start`` will now: - - + Convert awaitable to :class:`asyncio.Task`. - + Wait for task to complete. - - then ``service.stop`` will: - - + Cancel the task. - - This ensures an ``asyncio.sleep(10.0)`` within can be - cancelled. If you need some operation to absolutely finish you must - use `asyncio.shield`. - -- **Utils**: ``cached_property`` adds new ``.is_set(o)`` method on descriptor - - This can be used to test for the attribute having been cached/used. - - If you have a class with a ``cached_property``: - - .. sourcecode:: python - - from mode.utils.objects import cached_property - - class X: - - @cached_property - def foo(self): - return 42 - - x = X() - print(x.foo) - - From an instance you can now check if the property was accessed: - - .. sourcecode:: python - - if type(x).foo.is_set(x): - print(f'Someone accessed x.foo and it was cached as: {x.foo}') - -.. _version-1.18.0: - -1.18.0 -====== -:release-date: 2018-10-02 3:32 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- **Worker**: Fixed error when starting :pypi:`aioconsole` on ``--debug`` - - The worker would crash with: - - .. sourcecode:: text - - TypeError: Use `self.add_context(ctx)` for non-async context - - when started with the ``--debug`` flag. - -- **Worker**: New ``daemon`` argument controls shutdown of worker. - - When the flag is enabled, the default, the worker will not shut - down until the worker instance is either explicitly stopped, or - it receives a terminating process signal (``SIGINT``/``SIGTERM``/etc.) - - When disabled, the worker for the given service will shut down as soon as - ``await service.start()`` returns. - - You can think of it as a flag for daemons, but one that doesn't actually - do any of the UNIX daemonization stuff (detaching, etc.). It merely - means the worker continues to run in the background until stopped by - signal. - -- **Service**: Added class method: ``Service.from_awaitable``. - - This can be used to create a service out of any coroutine - or :class:`~typing.Awaitable`: - - .. sourcecode:: python - - from mode import Service, Worker - - async def me(interval=1.0): - print('ME STARTING') - await asyncio.sleep(interval) - print('ME STOPPING') - - def run_worker(interval=1.0): - coro = me(interval=1.0) - Worker(Service.from_awaitable(coro)).execute_from_commandline() - - if __name__ == '__main__': - run_worker() - - .. note:: - - Using a service with ``await self.sleep(1.0)`` is often not what - you want, as stopping the service will have to wait for the sleep - to finish. - - ``Service.from_awaitable`` is as such a last resort for cases - where you're provided a coroutine you cannot implement as a service. - - ``Service.sleep()`` is useful as it will stop sleeping immediately - if the service is stopped: - - class Me(Service): - - async def on_start(self) -> None: - await self.sleep(1.0) - -- **Service**: New method ``_repr_name`` can be used to override the service - class name used in ``repr(service)``. - -.. _version-1.17.3: - -1.17.3 -====== -:release-date: 2018-09-18 4:00 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Service: New attribute ``mundane_level`` decides the logging level - of mundane logging events such as "[X] Starting...", for starting/stopping - and tasks being cancelled. - - The value for this must be a logger level name, and is ``"info"`` by - default. - - If logging for a service is noisy at info-level, you can move it - to debug level by setting this attribute to ``"debug"``: - - .. sourcecode:: python - - class X(Service): - mundane_level = 'debug' - -.. _version-1.17.2: - -1.17.2 -====== -:release-date: 2018-09-17 3:00 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Removed and fixed import from ``collections`` that will be moved - to ``collections.abc`` in Python 3.8. - - This also silences a ``DeprecationWarning`` that was being emitted - on Python 3.7. - -- Type annotations now passing checks on :pypi:`mypy` 0.630. - -.. _version-1.17.1: - -1.17.1 -====== -:release-date: 2018-09-13 6:27 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- Fixes several bugs related to unwrapping ``Optional[List[..]]`` - in :func:`mode.utils.objects.annotations`. - - This functionality is not really related to mode at all, so - should be moved out of this library. Faust uses it for models. - -.. _version-1.17.0: - -1.17.0 -====== -:release-date: 2018-09-12 5:39 P.M PDT -:release-by: Ask Solem (:github_user:`ask`) - -- New async iterator utility: :class:`~mode.utils.aiter.arange` - - Like :class:`range` but returns an async iterator:: - - async for n in arange(0, 10, 2): - ... - -- New async iterator utility: :func:`~mode.utils.aiter.aslice` - - Like :class:`itertools.islice` but works on asynchronous iterators. - -- New async iterator utility: :func:`~mode.utils.aiter.chunks` - - :class:`~mode.utils.aiter.chunks` takes an async iterable and divides - it up into chunks of size n:: - - # Split range of 100 numbers into chunks of 10 each. - async for chunk in chunks(arange(100), 10): - yield chunk - - This gives chunks like this:: - - [ - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - ..., - ] - -.. _version-1.16.0: - -1.16.0 -====== -:release-date: 2018-09-11 1:37 P.M PDT -:release-by: Ask Solem - -- **Distribution**: Installing mode no longer installs the ``t`` directory - containing tests as a Python package. - - Contributed by Michael Seifert - -- **Testing**: New :class:`~mode.utils.mocks.AsyncContextManagerMock` - - You can use this to mock asynchronous context managers. - - Please see :class:`~mode.utils.mocks.AsyncContextManagerMock` for - an example. - -- **CI**: Python 3.7.0 and 3.6.0 was added to the build matrix. - -.. _version-1.15.1: - -1.15.1 -====== -:release-date: 2018-08-15 11:17 A.M PDT -:release-by: Ask Solem - -- Tests now passing on CPython 3.7.0 - -- **Utils**: Adds ``remove_optional`` function in :mod:`mode.utils.objects` - - This can be used to extract the concrete type from ``Optional[Foo]``. - -- **Utils**: Adds ``humanize_seconds`` function to :mod:`mode.utils.times` - -.. _version-1.15.0: - -1.15.0 -====== -:release-date: 2018-06-27 1:39 P.M PDT -:release-by: Ask Solem - -- Worker: Logging can now be set up using dictionary config, by passing - the ``logging_config`` argument to :class:`mode.Worker`. - - Contributed by Allison Wang. - -- Worker: No longer supports the ``logformat`` argument. - - To set up custom log format you must now pass in dict configuration - via the ``logging_config`` argument. - -- Service: ``start()`` accidentally silenced :exc:`asyncio.CancelledError`. - -- Service: Invalid assert caused :class:`~mode.CrashingSupervisor` to - crash with strange error - -.. _version-1.14.1: - -1.14.1 -====== -:release-date: 2018-06-06 1:26 P.M PDT -:release-by: Ask Solem - -- Service: Fixed "coroutine x was never awaited" for background - tasks (``@Service.task`` decorator) when service is started and stopped - in quick succession. - -.. _version-1.14.0: - -1.14.0 -====== -:release-date: 2018-06-05 12:13 P.M PDT -:release-by: Ask Solem - -- Adds method ``Service.wait_many(futures, *, timeout=None)`` - -.. _version-1.13.0: - -1.13.0 -====== -:release-date: 2018-05-16 1:26 P.M PDT -:release-by: Ask Solem - -- Mode now registers as a library having static type annotations. - - This conforms to :pep:`561` -- a new specification that defines - how Python libraries register type stubs to make them available - for use with static analyzers like :pypi:`mypy` and :pypi:`pyre-check`. - -- The code base now passes ``--strict-optional`` type checks. - -.. _version-1.12.5: - -1.12.5 -====== -:release-date: 2018-05-14 4:48 P.M PDT -:release-by: Ask Solem - -- Supervisor: Fixed wrong index calculation in management - of index-based service restart. - -.. _version-1.12.4: - -1.12.4 -====== -:release-date: 2018-05-07 3:20 P.M PDT -:release-by: Ask Solem - -- Adds new mock class for async functions: :func:`mode.utils.mocks.AsyncMock` - - This can be used to mock an async callable:: - - from mode.utils.mocks import AsyncMock - - class App(Service): - - async def on_start(self): - self.ret = await self.some_async_method('arg') - - async def some_async_method(self, arg): - await asyncio.sleep(1) - - @pytest.fixture - def app(): - return App() - - @pytest.mark.asyncio - async def test_something(*, app): - app.some_async_method = AsyncMock() - async with app: # starts and stops the service, calling on_start - app.some_async_method.assert_called_once_with('arg') - assert app.ret is app.some_async_method.coro.return_value - -- Added 100% test coverage for modules: - - + :mod:`mode.proxy` - + :mod:`mode.threads` - + :mod:`mode.utils.aiter` - -.. _version-1.12.3: - -1.12.3 -====== -:release-date: 2018-05-07 3:33 P.M PDT -:release-by: Ask Solem - -Important Notes ---------------- - -- Moved to https://github.com/ask/mode - -Changes -------- - -- Signal: Improved repr when signal has a default sender. - -- DictAttribute: Now supports ``len`` and ``del(d[key])``. - -- Worker: If overriding ``on_first_start`` you can now call - ``default_on_first_start`` instead of super. - - Example:: - - class MyWorker(Worker): - - async def on_first_start(self) -> None: - print('FIRST START') - await self.default_on_first_start() - -.. _version-1.12.2: - -1.12.2 -====== -:release-date: 2018-04-26 11:47 P.M PDT -:release-by: Ask Solem - -- Fixed shutdown error in :class:`~mode.threads.ServiceThread`. - -.. _version-1.12.1: - -1.12.1 -====== -:release-date: 2018-04-24 11:28 P.M PDT -:release-by: Ask Solem - -- Now works with CPython 3.6.1 and 3.6.0. - -.. _version-1.12.0: - -1.12.0 -====== -:release-date: 2018-04-23 1:28 P.M PDT -:release-by: Ask Solem - -Backward Incompatible Changes ------------------------------ - -+ Changed ``Service.add_context`` - - - To add an async context manager (:class:`~typing.AsyncContextManager`), - use :meth:`~mode.Service.add_async_context`:: - - class S(Service): - - async def on_start(self) -> None: - self.context = await self.add_async_context(MyAsyncContext()) - - - To add a regular context manager (:class:`~typing.ContextManager`), - use :meth:`~mode.Service.add_context`:: - - class S(Service): - - async def on_start(self) -> None: - self.context = self.add_context(MyContext()) - - This change was made so that contexts can be added from non-async - functions. To add an *async context* you still need to be within an - async function definition. - -News ----- - -+ Worker: Now redirects :data:`sys.stdout` and :data:`sys.stderr` to the - logging subsystem by default. - - - To disable this pass ``Worker(redirect_stdouts=False)``. - - - The default severity level for ``print`` statements are - :data:`logging.WARN`, but you can change this using - ``Worker(redirect_stdouts_level='INFO')``. - -+ :class:`~mode.Seconds`/:func:`~mode.want_seconds` can now be expressed - as strings and rate strings: - - - float as string: ``want_seconds('1.203') == 1.203`` - - - *10 in one second*: ``want_seconds('10/s') == 10.0`` - - - *10.33 in one hour*: ``want_seconds('10.3/h') == 0.0028611111111111116`` - - - *100 in one hour*: ``want_seconds('100/h') == 0.02777777777777778`` - - - *100 in one day*: ``want_seconds('100/d') == 0.0011574074074074076`` - - This is especially useful for the rate argument - to the :class:`~mode.utils.times.rate_limit` helper. - -+ Added new context manager: :func:`mode.utils.logging.redirect_stdouts`. - -+ Module :mod:`mode.types` now organized by category: - - - Service types: :mod:`mode.types.services` - - - Signal types: :mod:`mode.types.signals` - - - Supervisor types: :mod:`mode.types.supervisors` - -+ :class:`mode.flight_recorder` can now wrap objects so that every method call - on that object will result in the call and arguments to that call - being logged. - - Example logging statements with ``INFO`` severity:: - - with flight_recorder(logger, timeout=10.0) as on_timeout: - redis = on_timeout.wrap_info(self.redis) - await redis.get(key) - - There's also ``wrap_debug(o)``, ``wrap_warn(o)``, ``wrap_error(o)``, - and for any severity: ``wrap(logging.CRIT, o)``. - -Fixes ------ - -+ Fixed bug in ``Service.wait`` on Python 3.7. - -.. _version-1.11.5: - -1.11.5 -====== -:release-date: 2018-04-19 3:12 P.M PST -:release-by: Ask Solem - -+ FlowControlQueue now available in ``mode.utils.queues``. - - This is a backward compatible change. - -+ Tests for FlowControlQueue - -.. _version-1.11.4: - -1.11.4 -====== -:release-date: 2018-04-19 9:36 A.M PST -:release-by: Ask Solem - -+ Adds :class:`mode.flight_recorder` - - This is a logging utility to log stuff only when something - times out. - - For example if you have a background thread that is sometimes - hanging:: - - class RedisCache(mode.Service): - - @mode.timer(1.0) - def _background_refresh(self) -> None: - self._users = await self.redis_client.get(USER_KEY) - self._posts = await self.redis_client.get(POSTS_KEY) - - You want to figure out on what line this is hanging, but logging - all the time will provide way too much output, and will even change - how fast the program runs and that can mask race conditions, so that - they never happen. - - Use the flight recorder to save the logs and only log when it times out: - - .. sourcecode:: python - - logger = mode.get_logger(__name__) - - class RedisCache(mode.Service): - - @mode.timer(1.0) - def _background_refresh(self) -> None: - with mode.flight_recorder(logger, timeout=10.0) as on_timeout: - on_timeout.info(f'+redis_client.get({USER_KEY!r})') - await self.redis_client.get(USER_KEY) - on_timeout.info(f'-redis_client.get({USER_KEY!r})') - - on_timeout.info(f'+redis_client.get({POSTS_KEY!r})') - await self.redis_client.get(POSTS_KEY) - on_timeout.info(f'-redis_client.get({POSTS_KEY!r})') - - If the body of this :keyword:`with` statement completes before the - timeout, the logs are forgotten about and never emitted -- if it - takes more than ten seconds to complete, we will see these messages - in the log: - - .. sourcecode:: text - - [2018-04-19 09:43:55,877: WARNING]: Warning: Task timed out! - [2018-04-19 09:43:55,878: WARNING]: Please make sure it is hanging before restarting. - [2018-04-19 09:43:55,878: INFO]: [Flight Recorder-1] (started at Thu Apr 19 09:43:45 2018) Replaying logs... - [2018-04-19 09:43:55,878: INFO]: [Flight Recorder-1] (Thu Apr 19 09:43:45 2018) +redis_client.get('user') - [2018-04-19 09:43:55,878: INFO]: [Flight Recorder-1] (Thu Apr 19 09:43:49 2018) -redis_client.get('user') - [2018-04-19 09:43:55,878: INFO]: [Flight Recorder-1] (Thu Apr 19 09:43:46 2018) +redis_client.get('posts') - [2018-04-19 09:43:55,878: INFO]: [Flight Recorder-1] -End of log- - - Now we know this ``redis_client.get`` call can take too long to complete, - and should consider adding a timeout to it. - -.. _version-1.11.3: - -1.11.3 -====== -:release-date: 2018-04-18 5:22 P.M PST -:relese-by: Ask Solem - -- Cry handler (`kill -USR1`): Truncate huge data in stack frames. - -- ServiceProxy: Now supports ``_crash`` method. - -.. _version-1.11.2: - -1.11.2 -====== -:release-date: 2018-04-18 5:02 P.M PST -:release-by: Ask Solem - -- Service: ``add_future()`` now maintains futures in a set and futures - are automatically removed from it when done. - -- Cry handler (`kill -USR1`) now shows name of Service.task background tasks. - -- Stampede: Now propagates cancellation. - -.. _version-1.11.1: - -1.11.1 -====== -:release-date: 2018-04-18 11:08 P.M PST -:release-by: Ask Solem - -- Service.add_context: Now works with AsyncContextManager. - -- CI now runs functional tests. - -- Added supervisor and service tests. - -.. _version-1.11.0: - -1.11.0 -====== -:release-date: 2018-04-17 1:23 P.M PST -:release-by: Ask Solem - -- Supervisor: Fixes bug with max restart triggering too early. - -- Supervisor: Also restart child services. - -- Service: Now supports ``__post_init__`` like Python 3.7 dataclasses. - -- Service: Crash is logged even if crashed multiple times. - -.. _version-1.10.4: - -1.10.4 -====== -:release-date: 2018-04-13 3:53 P.M PST -:release-by: Ask Solem - -- Supervisor: Log full traceback when restarting service. - -.. _version-1.10.3: - -1.10.3 -====== -:release-date: 2018-04-11 10:58 P.M PST -:release-by: Ask Solem - -- setup_logging: now ensure logging is setup by clearing root logger handlers. - -.. _version-1.10.2: - -1.10.2 -====== -:release-date: 2018-04-03 4:50 P.M PST -:release-by: Ask Solem - -- Fixed wrong version number in Changelog. - -.. _version-1.10.1: - -1.10.1 -======= -:release-date: 2018-04-03 4:43 P.M PST -:release-by: Ask Solem - -- Service.wait: If the future we are waiting for is cancelled we must - propagate :exc:`CancelledError`. - -.. _version-1.10.0: - -1.10.0 -====== -:release-date: 2018-03-30 12:36 P.M PST -:release-by: Ask Solem - -- New supervisor: :class:`~mode.supervisors.ForfeitOneForOneSupervisor`. - - If a service in the group crashes we give up on that service - and don't start it again. - -- New supervisor: :class:`~mode.supervisor.ForfeitOneForAllSupervisor`. - - If a service in the group crashes we give up on it, but also - stop all services in the group and give up on them also. - -- Service Logging: Renamed ``self.log.crit`` to ``self.log.critical``. - - The old name is still available and is not deprecated at this time. - - -.. _version-1.9.2: - -1.9.2 -===== -:release-date: 2018-03-20 10:17 P.M PST -:release-by: Ask Solem - -- Adds ``FlowControlEvent.clear()`` to clear all contents of flow - controlled queues. - -- :class:`~mode.utils.futures.FlowControlEvent` now starts in a suspended - state. - - To disable this pass ``FlowControlEvent(initially_suspended=False))``. - -- Adds ``Service.service_reset`` method to reset service - start/stopped/crashed/etc., flags - -.. _version-1.9.1: - -1.9.1 -===== -:release-date: 2018-03-05 11:51 P.M PST -:release-by: Ask Solem - -- No longer depends on :pypi:`terminaltables`. - -.. _version-1.9.0: - -1.9.0 -===== -:release-date: 2018-03-05 11:33 P.M PST -:release-by: Ask Solem - -Backward Incompatible Changes -============================= - -- Module ``mode.utils.debug`` renamed to :mod:`mode.debug`. - - This is unlikely to affect users as this module is only used by mode - internally. - - This module had to move because it imports ``mode.Service``, and - the :mod:`mode.utils` package is not allowed to import from the - :mod:`mode` package at all. - -News -==== - -- Added function :func:`mode.utils.import.smart_import`. - -- Added non-async version of :class:`mode.Signal`: :class:`mode.SyncSignal`. - - The signal works exactly the same as the asynchronous version, except - ``Signal.send`` must not be :keyword:`await`-ed:: - - on_configured = SyncSignal() - on_configured.send(sender=obj) - - -- Added method ``iterate`` to :class:`mode.utils.imports.FactoryMapping`. - - This enables you to iterate over the extensions added to a - :mod:`setuptools` entrypoint. - -Fixes -===== - -- ``StampedeWrapper`` now correctly clears flag when original call done. - -.. _version-1.8.0: - -1.8.0 -===== -:release-date: 2018-02-20 04:01 P.M PST -:release-by: Ask Solem - -Backward Incompatible Changes ------------------------------ - -- API Change to fix memory leak in ``Service.wait``. - - The ``Service.wait(*futures)`` method was added to be able to wait for - this list of futures but also stop waiting if the service is stopped or - crashed:: - - import asyncio - from mode import Service - - class X(Service): - on_thing_ready: asyncio.Event - - def __post_init__(self): - self.on_thing_ready = asyncio.Event(loop=loop) - - @Service.task - async def _my_background_task(self): - while not self.should_stop: - # wait for flag to be set (or service stopped/crashed) - await self.wait(self.on_thing_ready.wait()) - print('FLAG SET') - - The problem with this was - - 1) The wait flag would return None and not raise an exception if the - service is stopped/crashed. - 2) Futures would be scheduled on the event loop but not properly cleaned - up, creating a very slow memory leak. - 3) No return value was returned for succesful feature. - - So to properly implement this we had to change the API of the ``wait`` - method to return a tuple instead, and to only allow a single coroutine to - be passed to wait:: - - @Service.task - async def _my_background_task(self): - while not self.should_stop: - # wait for flag to be set (or service stopped/crashed) - result, stopped = await self.wait(self.on_thing_ready) - if not stopped: - print('FLAG SET') - - This way the user can provide an alternate path when the service is - stopped/crashed while waiting for this event. - - A new shortcut method ``wait_for_stopped(fut)`` was also added:: - - # wait for flag to be set (or service stopped/crashed) - if not await self.wait_for_stopped(self.on_thing_ready): - print('FLAG SET') - - Moreover, you can now pass :class:`asyncio.Event` objects directly to - ``wait()``. - -News ----- - -- Added :class:`mode.utils.collections.DictAttribute`. - -- Added :class:`mode.utils.collections.AttributeDict`. - -Bugs ----- - -- Signals can create clone of signal with default sender already set - - .. code-block:: python - - signal: Signal[int] = Signal() - signal = signal.with_default_sender(obj) - -.. _version-1.7.0: - -1.7.0 -===== -:release-date: 2018-02-05 12:28 P.M PST -:release-by: Ask Solem - -- Adds :mod:`mode.utils.aiter` for missing ``aiter`` and ``anext`` functions. - -- Adds :mod:`mode.utils.futures` for :class:`asyncio.Task` related tools. - -- Adds :mod:`mode.utils.collections` for custom mapping/set and list - data structures. - -- Adds :mod:`mode.utils.imports` for importing modules at runtime, - as well as utilities for typed :mod:`setuptools` entry-points. - -- Adds :mod:`mode.utils.text` for fuzzy matching user input. - -.. _version-1.6.0: - -1.6.0 -===== -:release-date: 2018-02-05 11:10 P.M PST -:release-by: Ask Solem - -- Fixed bug where ``@Service.task`` background tasks were not started - in subclasses. - -- Service: Now has two exit stacks: ``.exit_stack`` & ``.async_exit_stack``. - - This is a backward incompatible change, but probably nobody was accessing - ``.exit_stack`` directly. - - Use ``await Service.enter_context(ctx)`` with both regular and - asynchronous context managers:: - - class X(Service): - - async def on_start(self) -> None: - # works with both context manager types. - await self.enter_context(async_context) - await self.enter_context(context) - -- Adds :func:`~mode.utils.contextlib.asynccontextmanager`` decorator - from CPython 3.7b1. - - This decorator works exactly the same as - :func:`contextlib.contextmanager`, but for :keyword:`async with`. - - Import it from :mod:`mode.utils.contexts`:: - - from mode.utils.contexts import asynccontextmanager - - @asynccontextmanager - async def connection_or_default(conn: Connection = None) -> Connection: - if connection is None: - async with connection_pool.acquire(): - yield - else: - yield connection - - async def main(): - async with connection_or_default() as connection: - ... - -- Adds :class:`~mode.utils.contexts.AsyncExitStack` from CPython 3.7b1 - - This works like :class:`contextlib.ExitStack`, but for asynchronous - context managers used with :keyword:`async with`. - -- Logging: Worker debug log messages are now colored blue when colors are - enabled. - - -.. _version-1.5.0: - -1.5.0 -===== -:release-date: 2018-01-04 03:43 P.M PST -:release-by: Ask Solem - -- Service: Adds new ``await self.add_context(context)`` - - This adds a new context manager to be entered when the service starts, - and exited once the service exits. - - The context manager can be either a :class:`typing.AsyncContextManager` - (:keyword:`async with`) or a - regular :class:`typing.ContextManager` (:keyword:`with`). - -- Service: Added ``await self.add_runtime_dependency()`` which unlike - ``add_dependency`` starts the dependent service if the self is already - started. - -- Worker: Now supports a new ``console_port`` argument to specify a port - for the :pypi:`aiomonitor` console, different than the default (50101). - - .. note:: - - The aiomonitor console is only started when ``Worker(debug=True, ...)`` - is set. - -.. _version-1.4.0: - -1.4.0 -===== -:release-date: 2017-12-21 09:50 A.M PST -:release-by: Ask Solem - -- Worker: Add support for parameterized logging handlers. - - Contributed by Prithvi Narasimhan. - -.. _version-1.3.0: - -1.3.0 -===== -:release-date: 2017-12-04 01:17 P.M PST -:release-by: Ask Solem - -- Now supports color output in logs when logging to a terminal. - -- Now depends on :pypi:`colorlog` - -- Added :class:`mode.Signal`: async. implementation of the observer - pattern (think Django signals). - -- DependencyGraph is now a generic type: ``DependencyGraph[int]`` - -- Node is now a generic type: ``Node[Service]``. - -.. _version-1.2.1: - -1.2.1 -===== -:release-date: 2017-11-06 04:50 P.M PST -:release-by: Ask Solem - -- Service: Subclasses can now override a Service.task method. - - Previously it would unexpectedly start two tasks: - the task defined in the superclass and the task defined in - the subclass. - -.. _version-1.2.0: - -1.2.0 -===== -:release-date: 2017-11-02 03:17 P.M PDT -:release-by: Ask Solem - -- Renames PoisonpillSupervisor to CrashingSupervisor. - -- Child services now stopped even if not fully started. - - Previously ``child_service.stop()`` would not be called - if `child_service.start()` never completed, but as a service - might be in the process of starting other child services, we need - to call stop even if not fully started. - -.. _version-1.1.1: - -1.1.1 -===== -:release-date: 2017-10-25 04:34 P.M PDT -:release-by: Ask Solem - -- Added alternative event loop implementations: eventlet, gevent, uvloop. - - E.g. to use gevent as the event loop, install mode using: - - .. sourcecode:: console - - $ pip install mode[gevent] - - and add this line to the top of your worker entrypoint module:: - - import mode.loop - mode.loop.use('gevent') - -- Service: More fixes for the weird `__init_subclass__` behavior - only seen in Python 3.6.3. - -- ServiceThread: Now propagates errors raised in the thread - to the main thread. - -.. _version-1.1.0: - -1.1.0 -===== -:release-date: 2017-10-19 01:35 P.M PDT -:release-by: Ask Solem - -- ServiceThread: Now inherits from Service, and uses - ``loop.run_in_executor()`` to start the service as a thread. - -- setup_logging: filename argument is now respected. - -.. _version-1.0.2: - -1.0.2 -===== -:release-date: 2017-10-10 01:51 P.M PDT -:release-by: Ask Solem - -- Adds support for Python 3.6.0 - -- Adds backports of typing improvements in CPython 3.6.1 - to ``mode.utils.compat``: ``AsyncContextManager``, ``ChainMap``, - ``Counter``, and ``Deque``. - -- ``Supervisor.add`` and ``.discard`` now takes an arbitrary number - of services to add/discard as star arguments. - -- Fixed typo in example: ``Service.task`` -> ``mode.Service.task``. - - Contributed by Xu Jing. - -.. _version-1.0.1: - -1.0.1 -===== -:release-date: 2017-10-05 02:53 P.M PDT -:release-by: Ask Solem - -- Fixes compatibility with Python 3.6.3. - - Python 3.6.3 badly broke ``__init_subclass__``, in such a way that - any class attribute set is set for all subclasses. - -.. _version-1.0.0: - -1.0.0 -===== -:release-date: 2017-10-04 01:29 P.M PDT -:release-by: Ask Solem - -- Initial release +- Fix broken tests From cd0695ed8dd8f6b84f89fff92c62308b8847323f Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Thu, 17 Dec 2020 11:22:23 +0100 Subject: [PATCH 08/12] Fix Python 3.9 support --- .travis.yml | 20 ++++++------- mode/utils/objects.py | 67 ++++++++++++++----------------------------- setup.py | 1 + 3 files changed, 33 insertions(+), 55 deletions(-) diff --git a/.travis.yml b/.travis.yml index e7b59991..7377e46f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -137,16 +137,16 @@ matrix: os: linux dist: focal stage: test - # - python: 3.9.0 - # env: TOXENV=3.9 IDENT="3.9.0" RUN_SUITE=y - # os: linux - # dist: focal - # stage: test - # - python: 3.9.1 - # env: TOXENV=3.9 IDENT="3.9.1" RUN_SUITE=y - # os: linux - # dist: xenial - # stage: test + - python: 3.9.0 + env: TOXENV=3.9 IDENT="3.9.0" RUN_SUITE=y + os: linux + dist: focal + stage: test + - python: 3.9.1 + env: TOXENV=3.9 IDENT="3.9.1" RUN_SUITE=y + os: linux + dist: xenial + stage: test before_install: install: diff --git a/mode/utils/objects.py b/mode/utils/objects.py index 0f2476de..13fadbbe 100644 --- a/mode/utils/objects.py +++ b/mode/utils/objects.py @@ -446,61 +446,32 @@ def remove_optional(typ: Type) -> Type: def is_union(typ: Type) -> bool: name = typ.__class__.__name__ - return ( - (name == '_GenericAlias' and typ.__origin__ is typing.Union) or # 3.7 - name == '_Union' # 3.6 + return any( + [ + name == '_UnionGenericAlias', # 3.9 + name == '_GenericAlias' and typ.__origin__ is typing.Union, # 3.7 + name == '_Union', # 3.6 + ] ) def is_optional(typ: Type) -> bool: - args = getattr(typ, '__args__', ()) - if typ.__class__.__name__ == '_GenericAlias': - # Py3.7 - if typ.__origin__ is typing.Union: - for arg in args: - if arg is type(None): # noqa - return True - elif typ.__class__.__name__ == '_Union': # pragma: no cover - # Py3.6 - # Optional[x] actually returns Union[x, type(None)] - if args and type(None) in args: # noqa - return True + if is_union(typ): + args = getattr(typ, '__args__', ()) + return any([True for arg in args if arg is None or arg is type(None)]) # noqa return False def _remove_optional(typ: Type, *, find_origin: bool = False) -> Tuple[List[Any], Type]: args = getattr(typ, '__args__', ()) - if typ.__class__.__name__ == '_GenericAlias': - # 3.7 - if typ.__origin__ is typing.Union: - # Optional[List[int]] -> Union[List[int], NoneType] - found_None = False - union_type_args: Optional[List] = None - union_type: Optional[Type] = None - for arg in args: - if arg is None or arg is type(None): # noqa - found_None = True - else: - # returns ((int,), list) - union_type_args = getattr(arg, '__args__', ()) - union_type = arg - if find_origin: - union_type = getattr(arg, '__origin__', arg) - if union_type is not None and found_None: - return cast(List, union_type_args), union_type - else: - if find_origin: - # List[int] -> ((int,), list) - typ = typ.__origin__ # for List this is list, etc. - return args, typ - elif typ.__class__.__name__ == '_Union': # pragma: no cover - # Py3.6 - # Optional[List[int]] gives Union[List[int], type(None)] + if is_union(typ): + # 3.7+: Optional[List[int]] -> Union[List[int], NoneType] + # 3.6: Optional[List[int]] -> Union[List[int], type(None)] # returns: ((int,), list) found_None = False - union_type_args = None - union_type = None + union_type_args: Optional[List] = None + union_type: Optional[Type] = None for arg in args: if arg is None or arg is type(None): # noqa found_None = True @@ -508,12 +479,18 @@ def _remove_optional(typ: Type, *, union_type_args = getattr(arg, '__args__', ()) union_type = arg if find_origin: - if union_type is not None: + if union_type is not None and sys.version_info.minor == 6: union_type = _py36_maybe_unwrap_GenericMeta(union_type) + else: + union_type = getattr(arg, '__origin__', arg) if union_type is not None and found_None: return cast(List, union_type_args), union_type if find_origin: - typ = _py36_maybe_unwrap_GenericMeta(typ) + if hasattr(typ, '__origin__'): + # List[int] -> ((int,), list) + typ = typ.__origin__ # for List this is list, etc. + else: + typ = _py36_maybe_unwrap_GenericMeta(typ) return args, typ diff --git a/setup.py b/setup.py index 5747c5ee..970fcb43 100644 --- a/setup.py +++ b/setup.py @@ -32,6 +32,7 @@ def _pyimp(): Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Programming Language :: Python :: Implementation :: CPython Operating System :: POSIX Operating System :: Microsoft :: Windows From c4622762fca73492ede87daa95f95133dfed56f5 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Thu, 17 Dec 2020 11:24:06 +0100 Subject: [PATCH 09/12] Add Python 3.9 support to Changelog --- Changelog | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 055bcf3b..0d223428 100644 --- a/Changelog +++ b/Changelog @@ -8,7 +8,7 @@ 0.1.0 ===== -:release-date: 2020-12-16 06:00 P.M CET +:release-date: 2020-12-17 14:00 P.M CET :release-by: Thomas Sarboni (:github_user:`max-k`) - Friendly fork of ask/mode : Initial release @@ -18,3 +18,5 @@ - Add tests on Python 3.8.1-3.8.6 - Fix broken tests + +- Add Python 3.9 support From 32871a9e006390ac74e98ed9bc5ee8a70caa8723 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Thu, 17 Dec 2020 11:35:05 +0100 Subject: [PATCH 10/12] Add informations about fork in README --- README.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.rst b/README.rst index 5da82539..10e5ba1d 100644 --- a/README.rst +++ b/README.rst @@ -10,6 +10,21 @@ :Source: http://github.com/faust-streaming/mode :Keywords: async, service, framework, actors, bootsteps, graph + +Why the fork +============ + +We have decided to fork the original *Mode* project because there is a critical process of releasing new versions which causes uncertainty in the community. Everybody is welcome to contribute to this *fork*, and you can be added as a manitainer. + +We want to: + +- Ensure continues release +- Code quality +- Support latest Python versions +- Update the documentation + +and more... + What is Mode? ============= From 1f652a7f6f69eebfd6be9552c66e2cf65ba46eb4 Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Thu, 17 Dec 2020 11:37:30 +0100 Subject: [PATCH 11/12] Fix flake8 --- mode/utils/objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/utils/objects.py b/mode/utils/objects.py index 13fadbbe..ee70958e 100644 --- a/mode/utils/objects.py +++ b/mode/utils/objects.py @@ -451,7 +451,7 @@ def is_union(typ: Type) -> bool: name == '_UnionGenericAlias', # 3.9 name == '_GenericAlias' and typ.__origin__ is typing.Union, # 3.7 name == '_Union', # 3.6 - ] + ], ) From 20fa4d3d18324f2146f722f0a5351c4aeef051bc Mon Sep 17 00:00:00 2001 From: Thomas Sarboni Date: Thu, 17 Dec 2020 14:49:03 +0100 Subject: [PATCH 12/12] One more fix in unit tests related to 3.9 support --- mode/utils/objects.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mode/utils/objects.py b/mode/utils/objects.py index ee70958e..6b6dfbbf 100644 --- a/mode/utils/objects.py +++ b/mode/utils/objects.py @@ -488,8 +488,6 @@ def _remove_optional(typ: Type, *, if find_origin: if hasattr(typ, '__origin__'): # List[int] -> ((int,), list) - typ = typ.__origin__ # for List this is list, etc. - else: typ = _py36_maybe_unwrap_GenericMeta(typ) return args, typ