Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-aiosignal for 
openSUSE:Factory checked in at 2025-07-10 23:15:00
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-aiosignal (Old)
 and      /work/SRC/openSUSE:Factory/.python-aiosignal.new.7373 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-aiosignal"

Thu Jul 10 23:15:00 2025 rev:6 rq:1291582 version:1.4.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-aiosignal/python-aiosignal.changes        
2025-06-13 18:43:47.034772467 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-aiosignal.new.7373/python-aiosignal.changes  
    2025-07-10 23:15:15.281054528 +0200
@@ -1,0 +2,10 @@
+Thu Jul 10 04:53:25 UTC 2025 - Steve Kowalik <steven.kowa...@suse.com>
+
+- Upgrade to 1.4.0:
+  * Added decorator functionality to Signal as a convenient way to add a
+    callback
+  * Improved type safety by allowing callback parameters to be type checked
+    (typing-extensions is now required for Python <3.13). Parameters for a
+    Signal callback should now be defined like Signal[int, str]
+
+-------------------------------------------------------------------

Old:
----
  aiosignal-1.3.2.tar.gz

New:
----
  aiosignal-1.4.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-aiosignal.spec ++++++
--- /var/tmp/diff_new_pack.EBuJsC/_old  2025-07-10 23:15:15.957082688 +0200
+++ /var/tmp/diff_new_pack.EBuJsC/_new  2025-07-10 23:15:15.957082688 +0200
@@ -16,27 +16,31 @@
 #
 
 
-%define skip_python2 1
 %{?sle15_python_module_pythons}
 Name:           python-aiosignal
-Version:        1.3.2
+Version:        1.4.0
 Release:        0
 Summary:        a list of registered asynchronous callbacks
 License:        Apache-2.0
 URL:            https://github.com/aio-libs/aiosignal
 Source:         
https://files.pythonhosted.org/packages/source/a/aiosignal/aiosignal-%{version}.tar.gz
+BuildRequires:  %{python_module base >= 3.9}
 BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-frozenlist >= 1.1.0
+%if 0%{?python_version_nodots} < 313
+Requires:       python-typing_extensions >= 4.4
+%endif
 BuildArch:      noarch
 # SECTION test requirements
 BuildRequires:  %{python_module frozenlist >= 1.1.0}
 BuildRequires:  %{python_module pytest-asyncio}
 BuildRequires:  %{python_module pytest-cov}
 BuildRequires:  %{python_module pytest}
+BuildRequires:  %{python_module typing_extensions >= 4.4}
 # /SECTION
 %python_subpackages
 

++++++ aiosignal-1.3.2.tar.gz -> aiosignal-1.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/.codecov.yml 
new/aiosignal-1.4.0/.codecov.yml
--- old/aiosignal-1.3.2/.codecov.yml    1970-01-01 01:00:00.000000000 +0100
+++ new/aiosignal-1.4.0/.codecov.yml    2025-07-04 00:38:03.000000000 +0200
@@ -0,0 +1,32 @@
+coverage:
+  range: 95..100
+
+  status:
+    project: off
+
+flags:
+  library:
+    paths:
+    - aiosignal/
+  configs:
+    paths:
+    - requirements/
+    - .git*
+    - '*.toml'
+    - '*.yml'
+  changelog:
+    paths:
+    - CHANGES/
+    - CHANGES.rst
+  docs:
+    paths:
+    - docs/
+    - '*.md'
+    - '*.rst'
+    - '*.txt'
+  tests:
+    paths:
+    - tests/
+  tools:
+    paths:
+    - tools/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/.coveragerc 
new/aiosignal-1.4.0/.coveragerc
--- old/aiosignal-1.3.2/.coveragerc     1970-01-01 01:00:00.000000000 +0100
+++ new/aiosignal-1.4.0/.coveragerc     2025-07-04 00:38:03.000000000 +0200
@@ -0,0 +1,22 @@
+[html]
+show_contexts = true
+skip_covered = false
+
+[paths]
+_site-packages-to-src-mapping =
+  .
+  */lib/pypy*/site-packages
+  */lib/python*/site-packages
+  *\Lib\site-packages
+
+[run]
+branch = true
+cover_pylib = false
+omit =
+  setup.py
+parallel = true
+relative_files = true
+source =
+  .
+source_pkgs =
+  aiosignal
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/CHANGES.rst 
new/aiosignal-1.4.0/CHANGES.rst
--- old/aiosignal-1.3.2/CHANGES.rst     2024-12-13 18:10:20.000000000 +0100
+++ new/aiosignal-1.4.0/CHANGES.rst     2025-07-04 00:38:03.000000000 +0200
@@ -14,6 +14,29 @@
 
 .. towncrier release notes start
 
+1.4.0 (2025-07-03)
+==================
+
+Features
+--------
+
+- Added decorator functionality to ``Signal`` as a convenient way to add a 
callback -- by ``@Vizonex``.
+  `#699 <https://github.com/aio-libs/aiosignal/pulls/699>`_
+
+- Improved type safety by allowing callback parameters to be type checked 
(typing-extensions is now required for Python <3.13).
+  Parameters for a ``Signal`` callback should now be defined like 
``Signal[int, str]`` -- by @Vizonex and @Dreamsorcerer.
+  `#699 <https://github.com/aio-libs/aiosignal/pulls/699>`_, `#710 
<https://github.com/aio-libs/aiosignal/pulls/710>`_
+
+
+Misc
+----
+
+- Removed the sphinxcontrib-asyncio documentation dependency.
+  `#528 <https://github.com/aio-libs/aiosignal/pull/528>`_
+
+
+----
+
 1.3.2 (2024-12-13)
 ==================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/CONTRIBUTORS.txt 
new/aiosignal-1.4.0/CONTRIBUTORS.txt
--- old/aiosignal-1.3.2/CONTRIBUTORS.txt        2024-12-13 18:10:20.000000000 
+0100
+++ new/aiosignal-1.4.0/CONTRIBUTORS.txt        2025-07-04 00:38:03.000000000 
+0200
@@ -3,3 +3,4 @@
 Andrew Svetlov
 Martijn Pieters
 Nikolay Kim
+Vizonex
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/MANIFEST.in 
new/aiosignal-1.4.0/MANIFEST.in
--- old/aiosignal-1.3.2/MANIFEST.in     2024-12-13 18:10:20.000000000 +0100
+++ new/aiosignal-1.4.0/MANIFEST.in     2025-07-04 00:38:03.000000000 +0200
@@ -1,10 +1,15 @@
+include .codecov.yml
+include .coveragerc
 include LICENSE
 include CHANGES.rst
 include README.rst
 include CONTRIBUTORS.txt
 include Makefile
+include pytest.ini
+include tox.ini
 graft aiosignal
 graft docs
+graft requirements
 graft tests
 global-include *.pyi
 global-exclude *.pyc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/PKG-INFO new/aiosignal-1.4.0/PKG-INFO
--- old/aiosignal-1.3.2/PKG-INFO        2024-12-13 18:10:33.602803500 +0100
+++ new/aiosignal-1.4.0/PKG-INFO        2025-07-04 00:38:12.641044000 +0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: aiosignal
-Version: 1.3.2
+Version: 1.4.0
 Summary: aiosignal: a list of registered asynchronous callbacks
 Home-page: https://github.com/aio-libs/aiosignal
 Maintainer: aiohttp team <t...@aiohttp.org>
@@ -26,6 +26,8 @@
 Description-Content-Type: text/x-rst
 License-File: LICENSE
 Requires-Dist: frozenlist>=1.1.0
+Requires-Dist: typing-extensions>=4.2; python_version < "3.13"
+Dynamic: license-file
 
 =========
 aiosignal
@@ -35,8 +37,8 @@
    :target: https://github.com/aio-libs/aiosignal/actions?query=workflow%3ACI
    :alt: GitHub status for master branch
 
-.. image:: 
https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg
-   :target: https://codecov.io/gh/aio-libs/aiosignal
+.. image:: 
https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg?flag=pytest
+   :target: https://codecov.io/gh/aio-libs/aiosignal?flags[0]=pytest
    :alt: codecov.io status for master branch
 
 .. image:: https://badge.fury.io/py/aiosignal.svg
@@ -86,25 +88,12 @@
 
    $ pip install aiosignal
 
-The library requires Python 3.8 or newer.
-
 
 Documentation
 =============
 
 https://aiosignal.readthedocs.io/
 
-Communication channels
-======================
-
-*gitter chat* https://gitter.im/aio-libs/Lobby
-
-Requirements
-============
-
-- Python >= 3.8
-- frozenlist >= 1.0.0
-
 License
 =======
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/README.rst 
new/aiosignal-1.4.0/README.rst
--- old/aiosignal-1.3.2/README.rst      2024-12-13 18:10:20.000000000 +0100
+++ new/aiosignal-1.4.0/README.rst      2025-07-04 00:38:03.000000000 +0200
@@ -6,8 +6,8 @@
    :target: https://github.com/aio-libs/aiosignal/actions?query=workflow%3ACI
    :alt: GitHub status for master branch
 
-.. image:: 
https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg
-   :target: https://codecov.io/gh/aio-libs/aiosignal
+.. image:: 
https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg?flag=pytest
+   :target: https://codecov.io/gh/aio-libs/aiosignal?flags[0]=pytest
    :alt: codecov.io status for master branch
 
 .. image:: https://badge.fury.io/py/aiosignal.svg
@@ -57,25 +57,12 @@
 
    $ pip install aiosignal
 
-The library requires Python 3.8 or newer.
-
 
 Documentation
 =============
 
 https://aiosignal.readthedocs.io/
 
-Communication channels
-======================
-
-*gitter chat* https://gitter.im/aio-libs/Lobby
-
-Requirements
-============
-
-- Python >= 3.8
-- frozenlist >= 1.0.0
-
 License
 =======
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/aiosignal/__init__.py 
new/aiosignal-1.4.0/aiosignal/__init__.py
--- old/aiosignal-1.3.2/aiosignal/__init__.py   2024-12-13 18:10:20.000000000 
+0100
+++ new/aiosignal-1.4.0/aiosignal/__init__.py   2025-07-04 00:38:03.000000000 
+0200
@@ -1,11 +1,27 @@
+import sys
+from typing import Any, Awaitable, Callable, TypeVar
+
 from frozenlist import FrozenList
 
-__version__ = "1.3.2"
+if sys.version_info >= (3, 11):
+    from typing import Unpack
+else:
+    from typing_extensions import Unpack
+
+if sys.version_info >= (3, 13):
+    from typing import TypeVarTuple
+else:
+    from typing_extensions import TypeVarTuple
+
+_T = TypeVar("_T")
+_Ts = TypeVarTuple("_Ts", default=Unpack[tuple[()]])
+
+__version__ = "1.4.0"
 
 __all__ = ("Signal",)
 
 
-class Signal(FrozenList):
+class Signal(FrozenList[Callable[[Unpack[_Ts]], Awaitable[object]]]):
     """Coroutine-based signal implementation.
 
     To connect a callback to a signal, use any list method.
@@ -16,16 +32,16 @@
 
     __slots__ = ("_owner",)
 
-    def __init__(self, owner):
+    def __init__(self, owner: object):
         super().__init__()
         self._owner = owner
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         return "<Signal owner={}, frozen={}, {!r}>".format(
             self._owner, self.frozen, list(self)
         )
 
-    async def send(self, *args, **kwargs):
+    async def send(self, *args: Unpack[_Ts], **kwargs: Any) -> None:
         """
         Sends data to all registered receivers.
         """
@@ -33,4 +49,11 @@
             raise RuntimeError("Cannot send non-frozen signal.")
 
         for receiver in self:
-            await receiver(*args, **kwargs)  # type: ignore
+            await receiver(*args, **kwargs)
+
+    def __call__(
+        self, func: Callable[[Unpack[_Ts]], Awaitable[_T]]
+    ) -> Callable[[Unpack[_Ts]], Awaitable[_T]]:
+        """Decorator to add a function to this Signal."""
+        self.append(func)
+        return func
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/aiosignal/__init__.pyi 
new/aiosignal-1.4.0/aiosignal/__init__.pyi
--- old/aiosignal-1.3.2/aiosignal/__init__.pyi  2024-12-13 18:10:20.000000000 
+0100
+++ new/aiosignal-1.4.0/aiosignal/__init__.pyi  1970-01-01 01:00:00.000000000 
+0100
@@ -1,12 +0,0 @@
-from typing import Any, Generic, TypeVar
-
-from frozenlist import FrozenList
-
-__all__ = ("Signal",)
-
-_T = TypeVar("_T")
-
-class Signal(FrozenList[_T], Generic[_T]):
-    def __init__(self, owner: Any) -> None: ...
-    def __repr__(self) -> str: ...
-    async def send(self, *args: Any, **kwargs: Any) -> None: ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/aiosignal.egg-info/PKG-INFO 
new/aiosignal-1.4.0/aiosignal.egg-info/PKG-INFO
--- old/aiosignal-1.3.2/aiosignal.egg-info/PKG-INFO     2024-12-13 
18:10:33.000000000 +0100
+++ new/aiosignal-1.4.0/aiosignal.egg-info/PKG-INFO     2025-07-04 
00:38:12.000000000 +0200
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: aiosignal
-Version: 1.3.2
+Version: 1.4.0
 Summary: aiosignal: a list of registered asynchronous callbacks
 Home-page: https://github.com/aio-libs/aiosignal
 Maintainer: aiohttp team <t...@aiohttp.org>
@@ -26,6 +26,8 @@
 Description-Content-Type: text/x-rst
 License-File: LICENSE
 Requires-Dist: frozenlist>=1.1.0
+Requires-Dist: typing-extensions>=4.2; python_version < "3.13"
+Dynamic: license-file
 
 =========
 aiosignal
@@ -35,8 +37,8 @@
    :target: https://github.com/aio-libs/aiosignal/actions?query=workflow%3ACI
    :alt: GitHub status for master branch
 
-.. image:: 
https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg
-   :target: https://codecov.io/gh/aio-libs/aiosignal
+.. image:: 
https://codecov.io/gh/aio-libs/aiosignal/branch/master/graph/badge.svg?flag=pytest
+   :target: https://codecov.io/gh/aio-libs/aiosignal?flags[0]=pytest
    :alt: codecov.io status for master branch
 
 .. image:: https://badge.fury.io/py/aiosignal.svg
@@ -86,25 +88,12 @@
 
    $ pip install aiosignal
 
-The library requires Python 3.8 or newer.
-
 
 Documentation
 =============
 
 https://aiosignal.readthedocs.io/
 
-Communication channels
-======================
-
-*gitter chat* https://gitter.im/aio-libs/Lobby
-
-Requirements
-============
-
-- Python >= 3.8
-- frozenlist >= 1.0.0
-
 License
 =======
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/aiosignal.egg-info/SOURCES.txt 
new/aiosignal-1.4.0/aiosignal.egg-info/SOURCES.txt
--- old/aiosignal-1.3.2/aiosignal.egg-info/SOURCES.txt  2024-12-13 
18:10:33.000000000 +0100
+++ new/aiosignal-1.4.0/aiosignal.egg-info/SOURCES.txt  2025-07-04 
00:38:12.000000000 +0200
@@ -1,3 +1,5 @@
+.codecov.yml
+.coveragerc
 CHANGES.rst
 CONTRIBUTORS.txt
 LICENSE
@@ -5,10 +7,11 @@
 Makefile
 README.rst
 pyproject.toml
+pytest.ini
 setup.cfg
 setup.py
+tox.ini
 aiosignal/__init__.py
-aiosignal/__init__.pyi
 aiosignal/py.typed
 aiosignal.egg-info/PKG-INFO
 aiosignal.egg-info/SOURCES.txt
@@ -20,5 +23,13 @@
 docs/index.rst
 docs/make.bat
 docs/spelling_wordlist.txt
+requirements/ci-bot.txt
+requirements/ci-wheel.txt
+requirements/ci.txt
+requirements/dev.txt
+requirements/doc-spelling.txt
+requirements/doc.txt
+requirements/towncrier.txt
+requirements/wheel.txt
 tests/conftest.py
 tests/test_signals.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/aiosignal.egg-info/requires.txt 
new/aiosignal-1.4.0/aiosignal.egg-info/requires.txt
--- old/aiosignal-1.3.2/aiosignal.egg-info/requires.txt 2024-12-13 
18:10:33.000000000 +0100
+++ new/aiosignal-1.4.0/aiosignal.egg-info/requires.txt 2025-07-04 
00:38:12.000000000 +0200
@@ -1 +1,4 @@
 frozenlist>=1.1.0
+
+[:python_version < "3.13"]
+typing-extensions>=4.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/docs/conf.py 
new/aiosignal-1.4.0/docs/conf.py
--- old/aiosignal-1.3.2/docs/conf.py    2024-12-13 18:10:20.000000000 +0100
+++ new/aiosignal-1.4.0/docs/conf.py    2025-07-04 00:38:03.000000000 +0200
@@ -45,7 +45,6 @@
 extensions = [
     "sphinx.ext.viewcode",
     "sphinx.ext.intersphinx",
-    "sphinxcontrib.asyncio",
 ]
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/docs/index.rst 
new/aiosignal-1.4.0/docs/index.rst
--- old/aiosignal-1.3.2/docs/index.rst  2024-12-13 18:10:20.000000000 +0100
+++ new/aiosignal-1.4.0/docs/index.rst  2025-07-04 00:38:03.000000000 +0200
@@ -15,6 +15,14 @@
 The only available operation is calling the previously registered
 callbacks by using ``await sig.send(data)``.
 
+The callback parameters, which should be passed in the ``.send()`` call, can be
+specified for a type checker:
+
+```python
+signal = Signal[int, str](owner)
+signal.send(42, "foo")
+```
+
 For concrete usage examples see the :ref:`aiohttp:aiohttp-web-signals` section 
of the :doc:`aiohttp:web_advanced` chapter of the :doc:`aiohttp documentation 
<aiohttp:index>`.
 
 API
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/docs/spelling_wordlist.txt 
new/aiosignal-1.4.0/docs/spelling_wordlist.txt
--- old/aiosignal-1.3.2/docs/spelling_wordlist.txt      2024-12-13 
18:10:20.000000000 +0100
+++ new/aiosignal-1.4.0/docs/spelling_wordlist.txt      2025-07-04 
00:38:03.000000000 +0200
@@ -66,9 +66,6 @@
 css
 ctor
 Ctrl
-Cython
-cythonized
-Cythonize
 de
 deduplicate
 # de-facto:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/pytest.ini 
new/aiosignal-1.4.0/pytest.ini
--- old/aiosignal-1.3.2/pytest.ini      1970-01-01 01:00:00.000000000 +0100
+++ new/aiosignal-1.4.0/pytest.ini      2025-07-04 00:38:03.000000000 +0200
@@ -0,0 +1,83 @@
+[pytest]
+addopts =
+  # `pytest-xdist`:
+  # --numprocesses=auto
+  # NOTE: the plugin disabled because it's slower with so few tests
+  # --numprocesses=0
+
+  # Show 10 slowest invocations:
+  --durations=10
+
+  # Report all the things == -rxXs:
+  -ra
+
+  # Show values of the local vars in errors/tracebacks:
+  --showlocals
+
+  # Autocollect and invoke the doctests from all modules:
+  # https://docs.pytest.org/en/stable/doctest.html
+  --doctest-modules
+
+  # Pre-load the `pytest-cov` plugin early:
+  -p pytest_cov
+
+  # `pytest-cov`:
+  --cov
+  --cov-config=.coveragerc
+  --cov-context=test
+  --no-cov-on-fail
+
+  # Fail on config parsing warnings:
+  # --strict-config
+
+  # Fail on non-existing markers:
+  # * Deprecated since v6.2.0 but may be reintroduced later covering a
+  #   broader scope:
+  # --strict
+  # * Exists since v4.5.0 (advised to be used instead of `--strict`):
+  --strict-markers
+
+asyncio_mode = auto
+asyncio_default_fixture_loop_scope = function
+
+doctest_optionflags = ALLOW_UNICODE ELLIPSIS
+
+# Marks tests with an empty parameterset as xfail(run=False)
+empty_parameter_set_mark = xfail
+
+faulthandler_timeout = 30
+
+filterwarnings =
+  error
+
+# https://docs.pytest.org/en/stable/usage.html#creating-junitxml-format-files
+junit_duration_report = call
+# xunit1 contains more metadata than xunit2 so it's better for CI UIs:
+junit_family = xunit1
+junit_logging = all
+junit_log_passing_tests = true
+junit_suite_name = aiosignal_test_suite
+
+# A mapping of markers to their descriptions allowed in strict mode:
+markers =
+
+minversion = 6.1.0
+
+# Optimize pytest's lookup by restricting potentially deep dir tree scan:
+norecursedirs =
+  build
+  dependencies
+  dist
+  docs
+  .*
+  *.egg
+  *.egg-info
+  */*.egg-info
+  */**/*.egg-info
+  *.dist-info
+  */*.dist-info
+  */**/*.dist-info
+
+testpaths = tests/
+
+xfail_strict = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/requirements/ci-bot.txt 
new/aiosignal-1.4.0/requirements/ci-bot.txt
--- old/aiosignal-1.3.2/requirements/ci-bot.txt 1970-01-01 01:00:00.000000000 
+0100
+++ new/aiosignal-1.4.0/requirements/ci-bot.txt 2025-07-04 00:38:03.000000000 
+0200
@@ -0,0 +1,2 @@
+-r ci-wheel.txt
+-e .
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/requirements/ci-wheel.txt 
new/aiosignal-1.4.0/requirements/ci-wheel.txt
--- old/aiosignal-1.3.2/requirements/ci-wheel.txt       1970-01-01 
01:00:00.000000000 +0100
+++ new/aiosignal-1.4.0/requirements/ci-wheel.txt       2025-07-04 
00:38:03.000000000 +0200
@@ -0,0 +1,5 @@
+-r wheel.txt
+
+coverage==7.9.1
+pytest-cov==6.2.1
+tox==4.27.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/requirements/ci.txt 
new/aiosignal-1.4.0/requirements/ci.txt
--- old/aiosignal-1.3.2/requirements/ci.txt     1970-01-01 01:00:00.000000000 
+0100
+++ new/aiosignal-1.4.0/requirements/ci.txt     2025-07-04 00:38:03.000000000 
+0200
@@ -0,0 +1,7 @@
+setuptools-git==1.2
+mypy==1.16.1; implementation_name=="cpython"
+mypy-extensions==1.1.0; implementation_name=="cpython"
+
+-r ci-wheel.txt
+-r doc.txt
+-e .
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/requirements/dev.txt 
new/aiosignal-1.4.0/requirements/dev.txt
--- old/aiosignal-1.3.2/requirements/dev.txt    1970-01-01 01:00:00.000000000 
+0100
+++ new/aiosignal-1.4.0/requirements/dev.txt    2025-07-04 00:38:03.000000000 
+0200
@@ -0,0 +1,3 @@
+-r ci.txt
+-r towncrier.txt
+cherry_picker==2.5.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/requirements/doc-spelling.txt 
new/aiosignal-1.4.0/requirements/doc-spelling.txt
--- old/aiosignal-1.3.2/requirements/doc-spelling.txt   1970-01-01 
01:00:00.000000000 +0100
+++ new/aiosignal-1.4.0/requirements/doc-spelling.txt   2025-07-04 
00:38:03.000000000 +0200
@@ -0,0 +1,2 @@
+-r doc.txt
+sphinxcontrib-spelling==8.0.1; platform_system!="Windows"  # We only use it in 
Travis CI
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/requirements/doc.txt 
new/aiosignal-1.4.0/requirements/doc.txt
--- old/aiosignal-1.3.2/requirements/doc.txt    1970-01-01 01:00:00.000000000 
+0100
+++ new/aiosignal-1.4.0/requirements/doc.txt    2025-07-04 00:38:03.000000000 
+0200
@@ -0,0 +1,3 @@
+sphinx==8.2.3
+pygments>=2.1
+aiohttp-theme==0.1.7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/requirements/towncrier.txt 
new/aiosignal-1.4.0/requirements/towncrier.txt
--- old/aiosignal-1.3.2/requirements/towncrier.txt      1970-01-01 
01:00:00.000000000 +0100
+++ new/aiosignal-1.4.0/requirements/towncrier.txt      2025-07-04 
00:38:03.000000000 +0200
@@ -0,0 +1 @@
+towncrier==24.8.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/requirements/wheel.txt 
new/aiosignal-1.4.0/requirements/wheel.txt
--- old/aiosignal-1.3.2/requirements/wheel.txt  1970-01-01 01:00:00.000000000 
+0100
+++ new/aiosignal-1.4.0/requirements/wheel.txt  2025-07-04 00:38:03.000000000 
+0200
@@ -0,0 +1,4 @@
+pytest==8.4.1
+pytest-asyncio==1.0.0
+pre-commit==4.2.0
+twine==6.1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/setup.cfg 
new/aiosignal-1.4.0/setup.cfg
--- old/aiosignal-1.3.2/setup.cfg       2024-12-13 18:10:33.602803500 +0100
+++ new/aiosignal-1.4.0/setup.cfg       2025-07-04 00:38:12.642043800 +0200
@@ -1,6 +1,3 @@
-[bdist_wheel]
-universal = True
-
 [metadata]
 name = aiosignal
 version = attr: aiosignal.__version__
@@ -40,6 +37,7 @@
 include_package_data = True
 install_requires = 
        frozenlist >= 1.1.0
+       typing-extensions >= 4.2; python_version < '3.13'
 
 [pep8]
 max-line-length = 88
@@ -65,34 +63,6 @@
        @abc.abstractmethod
        @abstractmethod
 
-[tool:pytest]
-addopts = --cov=aiosignal -v -rxXs
-filterwarnings = error
-junit_suite_name = aiosignal_test_suite
-junit_family = xunit2
-norecursedirs = dist docs build .tox .eggs
-minversion = 3.8.2
-testpaths = tests/
-asyncio_mode = strict
-asyncio_default_fixture_loop_scope = function
-
-[coverage:run]
-branch = True
-source = aiosignal
-omit = site-packages
-
-[mypy]
-follow_imports = silent
-strict_optional = True
-warn_redundant_casts = True
-check_untyped_defs = True
-disallow_any_generics = True
-disallow_untyped_defs = True
-warn_unused_ignores = True
-
-[mypy-pytest]
-ignore_missing_imports = true
-
 [egg_info]
 tag_build = 
 tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/tests/test_signals.py 
new/aiosignal-1.4.0/tests/test_signals.py
--- old/aiosignal-1.3.2/tests/test_signals.py   2024-12-13 18:10:20.000000000 
+0100
+++ new/aiosignal-1.4.0/tests/test_signals.py   2025-07-04 00:38:03.000000000 
+0200
@@ -1,10 +1,16 @@
 import re
+import sys
 from unittest import mock
 
 import pytest
 
 from aiosignal import Signal
 
+if sys.version_info >= (3, 11):
+    from typing import Unpack
+else:
+    from typing_extensions import Unpack
+
 
 class Owner:
     def __repr__(self) -> str:
@@ -16,24 +22,32 @@
     return Owner()
 
 
-@pytest.mark.asyncio
+async def test_signal_positional_args(owner: Owner) -> None:
+    async def callback(a: int, b: str) -> None:
+        return
+
+    signal = Signal[int, str](owner)
+    signal.append(callback)
+    signal.freeze()
+    await signal.send(42, "foo")
+
+
 async def test_add_signal_handler_not_a_callable(owner: Owner) -> None:
     callback = True
     signal = Signal(owner)
-    signal.append(callback)
+    signal.append(callback)  # type: ignore[arg-type]
     signal.freeze()
     with pytest.raises(TypeError):
         await signal.send()
 
 
-@pytest.mark.asyncio
 async def test_function_signal_dispatch_kwargs(owner: Owner) -> None:
     signal = Signal(owner)
     kwargs = {"foo": 1, "bar": 2}
 
     callback_mock = mock.Mock()
 
-    async def callback(**kwargs):
+    async def callback(**kwargs: object) -> None:
         callback_mock(**kwargs)
 
     signal.append(callback)
@@ -43,25 +57,22 @@
     callback_mock.assert_called_once_with(**kwargs)
 
 
-@pytest.mark.asyncio
 async def test_function_signal_dispatch_args_kwargs(owner: Owner) -> None:
-    signal = Signal(owner)
-    args = {"a", "b"}
+    signal = Signal[Unpack[tuple[str, ...]]](owner)
     kwargs = {"foo": 1, "bar": 2}
 
     callback_mock = mock.Mock()
 
-    async def callback(*args, **kwargs):
+    async def callback(*args: str, **kwargs: object) -> None:
         callback_mock(*args, **kwargs)
 
     signal.append(callback)
     signal.freeze()
 
-    await signal.send(*args, **kwargs)
-    callback_mock.assert_called_once_with(*args, **kwargs)
+    await signal.send("a", "b", **kwargs)
+    callback_mock.assert_called_once_with("a", "b", **kwargs)
 
 
-@pytest.mark.asyncio
 async def test_non_coroutine(owner: Owner) -> None:
     signal = Signal(owner)
     kwargs = {"foo": 1, "bar": 2}
@@ -129,14 +140,13 @@
     assert list(signal) == [m1]
 
 
-@pytest.mark.asyncio
 async def test_cannot_send_non_frozen_signal(owner: Owner) -> None:
     signal = Signal(owner)
 
     callback_mock = mock.Mock()
 
-    async def callback(**kwargs):
-        callback_mock(**kwargs)
+    async def callback(**kwargs: object) -> None:
+        callback_mock(**kwargs)  # pragma: no cover  # mustn't be called
 
     signal.append(callback)
 
@@ -158,3 +168,17 @@
         )
         is not None
     )
+
+async def test_decorator_callback_dispatch_args_kwargs(owner: Owner) -> None:
+    signal = Signal(owner)
+    args = {"a", "b"}
+    kwargs = {"foo": 1, "bar": 2}
+
+    callback_mock = mock.Mock()
+
+    @signal
+    async def callback(*args: object, **kwargs: object) -> None:
+        callback_mock(*args, **kwargs)
+
+    signal.freeze()
+    await signal.send(*args, **kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aiosignal-1.3.2/tox.ini new/aiosignal-1.4.0/tox.ini
--- old/aiosignal-1.3.2/tox.ini 1970-01-01 01:00:00.000000000 +0100
+++ new/aiosignal-1.4.0/tox.ini 2025-07-04 00:38:03.000000000 +0200
@@ -0,0 +1,426 @@
+[tox]
+envlist = check, clean, {py39,py310,py311,py312,py313}, report
+minversion = 4
+
+
+[python-cli-options]
+byte-warnings = -b
+byte-errors = -bb
+max-isolation = -E -s -I
+# some-isolation = -I
+# FIXME: Python 2 shim. Is this equivalent to the above?
+some-isolation = -E -s
+warnings-to-errors = -Werror
+
+
+[testenv]
+description = Run pytest under {envpython}
+# deps =
+#   pytest
+#   pytest-asyncio
+#   pytest-xdist
+#   pytest-cov
+deps = -rrequirements{/}ci-wheel.txt
+
+commands =
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -W 'ignore:Coverage failure::pytest_cov.plugin' \
+    -m pytest \
+      {tty:--color=yes} \
+      {posargs:--cov-report=html:{envtmpdir}{/}htmlcov{/}}
+commands_post =
+  -{envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c \
+      'import atexit, os, sys; \
+      os.getenv("GITHUB_ACTIONS") == "true" or sys.exit(); \
+      import coverage; \
+      gh_summary_fd = open(\
+        os.environ["GITHUB_STEP_SUMMARY"], encoding="utf-8", mode="a",\
+      ); \
+      atexit.register(gh_summary_fd.close); \
+      cov = coverage.Coverage(); \
+      cov.load(); \
+      cov.report(file=gh_summary_fd, output_format="markdown")'
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c \
+      'import os, pathlib, sys; \
+      os.getenv("GITHUB_ACTIONS") == "true" or sys.exit(); \
+      cov_report_arg_prefix = "--cov-report=xml:"; \
+      test_report_arg_prefix = "--junitxml="; \
+      cov_reports = [\
+        arg[len(cov_report_arg_prefix):] for arg in sys.argv \
+        if arg.startswith(cov_report_arg_prefix)\
+      ]; \
+      test_reports = [\
+        arg[len(test_report_arg_prefix):] for arg in sys.argv \
+        if arg.startswith(test_report_arg_prefix)\
+      ]; \
+      cov_report_file = cov_reports[-1] if cov_reports else None; \
+      test_report_file = test_reports[-1] if test_reports else None; \
+      gh_output_fd = open(\
+        os.environ["GITHUB_OUTPUT"], encoding="utf-8", mode="a",\
+      ); \
+      cov_report_file and \
+        print(f"cov-report-files={cov_report_file !s}", file=gh_output_fd); \
+      test_report_file and \
+        print(f"test-result-files={test_report_file !s}", file=gh_output_fd); \
+      print("codecov-flags=pytest", file=gh_output_fd); \
+      gh_output_fd.close()' \
+    {posargs}
+  # Print out the output coverage dir and a way to serve html:
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c\
+      'import pathlib, shlex, sys; \
+      cov_html_report_arg_prefix = "--cov-report=html:"; \
+      cov_html_reports = [\
+        arg[len(cov_html_report_arg_prefix):] for arg in sys.argv \
+        if arg.startswith(cov_html_report_arg_prefix)\
+      ]; \
+      cov_html_reports or sys.exit(); \
+      cov_html_report_dir = pathlib.Path(cov_html_reports[-1]); \
+      index_file = cov_html_report_dir / "index.html";\
+      index_file.exists() or sys.exit(); \
+      html_url = f"file://\{index_file\}";\
+      browse_cmd = shlex.join(("python3", "-Im", "webbrowser", html_url)); \
+      serve_cmd = shlex.join((\
+        "python3", "-Im", "http.server", \
+        "--directory", str(cov_html_report_dir), "0", \
+      )); \
+      print(f"\nTo open the HTML coverage report, run\n\n\
+      \t\{browse_cmd !s\}\n");\
+      print(f"To serve \
+      the HTML coverage report with a local web server, use\n\n\
+      \t\{serve_cmd !s\}\n")' \
+    {posargs:--cov-report=html:{envtmpdir}{/}htmlcov{/}}
+package = editable
+pass_env =
+  CI
+  GITHUB_*
+  SSH_AUTH_SOCK
+  TERM
+set_env =
+  COVERAGE_PROCESS_START = {toxinidir}{/}.coveragerc
+wheel_build_env = .pkg
+
+
+[testenv:cleanup-dists]
+description =
+  Wipe the the dist{/} folder
+deps =
+commands_pre =
+commands =
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c \
+      'import os, shutil, sys; \
+      dists_dir = "{toxinidir}{/}dist{/}"; \
+      shutil.rmtree(dists_dir, ignore_errors=True); \
+      sys.exit(os.path.exists(dists_dir))'
+commands_post =
+package = skip
+
+
+[testenv:build-dists]
+description =
+  Build dists with {basepython} and put them into the dist{/} folder
+depends =
+  cleanup-dists
+deps =
+  build
+commands =
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -m build \
+      {posargs:}
+commands_post =
+package = skip
+
+
+[testenv:metadata-validation]
+description =
+  Verify that dists under the `dist{/}` dir
+  have valid metadata
+depends =
+  build-dists
+deps = -rrequirements{/}wheel.txt
+commands =
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -m twine \
+      check \
+      --strict \
+      dist{/}*
+commands_post =
+package = skip
+
+
+[testenv:pre-commit]
+description =
+  Run the quality checks under {basepython}; run as
+  `SKIP=check-id1,check-id2 tox r -e pre-commit` to instruct the underlying
+  `pre-commit` invocation avoid running said checks; Use
+  `tox r -e pre-commit -- check-id1 --all-files` to select checks matching IDs
+  aliases{:} `tox r -e pre-commit -- mypy --all-files` will run 3 MyPy
+  invocations, but `tox r -e pre-commit -- mypy-py313 --all-files` runs one.
+commands =
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -m pre_commit \
+      run \
+      --color=always \
+      --show-diff-on-failure \
+      {posargs:--all-files}
+
+  # Print out the advice on how to install pre-commit from this env into Git:
+  -{envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c \
+      'cmd = "{envpython} -m pre_commit install"; \
+      scr_width = len(cmd) + 10; \
+      sep = "=" * scr_width; \
+      cmd_str = "    $ \{cmd\}";' \
+      'print(f"\n\{sep\}\nTo install pre-commit hooks into the Git repo, run:\
+      \n\n\{cmd_str\}\n\n\{sep\}\n")'
+commands_post =
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c \
+      'import os, pathlib, sys; \
+      os.getenv("GITHUB_ACTIONS") == "true" or sys.exit(); \
+      project_root_path = pathlib.Path(r"{toxinidir}"); \
+      test_results_dir = pathlib.Path(r"{temp_dir}") / ".test-results"; \
+      coverage_result_files = ",".join(\
+        str(xml_path.relative_to(project_root_path)) \
+        for xml_path in test_results_dir.glob("mypy--py-*{/}cobertura.xml")\
+      ); \
+      gh_output_fd = open(\
+        os.environ["GITHUB_OUTPUT"], encoding="utf-8", mode="a",\
+      ); \
+      print(\
+        f"cov-report-files={coverage_result_files !s}", file=gh_output_fd\
+      ); \
+      print("codecov-flags=MyPy", file=gh_output_fd); \
+      gh_output_fd.close()'
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c \
+      'import itertools, os, pathlib, shlex, sys; \
+      os.getenv("GITHUB_ACTIONS") == "true" or sys.exit(); \
+      test_results_dir = pathlib.Path(r"{temp_dir}") / ".test-results"; \
+      text_and_json_reports = itertools.chain( \
+        test_results_dir.glob("mypy--py-*{/}*.json"), \
+        test_results_dir.glob("mypy--py-*{/}*.txt"), \
+      ); \
+      report_contents = { \
+        report{:} report.read_text() \
+        for report in text_and_json_reports \
+      }; \
+      reports_summary_text_blob = "\n\n".join( \
+        f"\N\{NUMBER SIGN\}\N\{NUMBER SIGN\} {report_path.parent.name}{:} " \
+        f"`{report_path.name}`\n\n" \
+        f"```{report_path.suffix[1:]}\n{report_text}\n```\n" \
+        for report_path, report_text in report_contents.items() \
+      ); \
+      gh_summary_fd = open( \
+        os.environ["GITHUB_STEP_SUMMARY"], encoding="utf-8", mode="a", \
+      ); \
+      print(reports_summary_text_blob, file=gh_summary_fd); \
+      gh_summary_fd.close()'
+  # Print out the output coverage dir and a way to serve html:
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c\
+      'import os, pathlib, sys; \
+      os.getenv("GITHUB_ACTIONS") == "true" and sys.exit(); \
+      len(sys.argv) >= 3 and all(\
+        arg != "mypy" and not arg.startswith("mypy-py3") \
+        for arg in sys.argv \
+      ) and sys.exit(); \
+      project_root_path = pathlib.Path(r"{toxinidir}"); \
+      test_results_dir = pathlib.Path(r"{temp_dir}") / ".test-results"; \
+      coverage_html_report_urls = [\
+        f"file://\{xml_path !s\}" \
+        for xml_path in test_results_dir.glob("mypy--py-*{/}index.html")\
+      ]; \
+      coverage_html_report_open_cmds = [\
+      f"python3 -Im webbrowser \N\{QUOTATION MARK\}\{html_url 
!s\}\N\{QUOTATION MARK\}" \
+      for html_url in coverage_html_report_urls\
+      ]; \
+      coverage_html_report_open_cmds_blob = "\n\n\t".join(\
+        coverage_html_report_open_cmds,\
+      ); \
+      print(\
+        f"\nTo open the HTML coverage reports, run\n\n\
+        \t\{coverage_html_report_open_cmds_blob !s\}\n"\
+      ); \
+      print(\
+        f"[*] Find rest of JSON and text reports, are in the same 
directories."\
+      )\
+      ' \
+    {posargs:--all-files}
+deps = -rrequirements{/}wheel.txt
+isolated_build = true
+package = skip
+pass_env =
+  {[testenv]pass_env}
+  SKIP  # set this variable
+
+
+[testenv:build-docs]
+# NOTE: Passing the `is_unversioned` tag speeds up rebuilds in dev env
+allowlist_externals =
+  git
+description = Build The Docs
+changedir = docs{/}
+commands_pre =
+  # Retrieve possibly missing commits:
+  -git fetch --unshallow
+  -git fetch --tags
+
+  # Clean up sphinxcontrib-apidoc generated RST files:
+  -git clean -x -f -- 'pkg{/}*.rst'
+commands =
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -m sphinx \
+      -j auto \
+      -b html \
+      {tty:--color} \
+      -a \
+      -n \
+      -W --keep-going \
+      -d '{temp_dir}{/}.doctrees' \
+      . \
+      {posargs:{envtmpdir}{/}html -t is_unversioned}
+commands_post =
+  # Print out the output docs dir and a way to serve html:
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -c\
+      'import os, pathlib;\
+      IS_RTD_ENV = os.getenv("READTHEDOCS", "False") == "True";\
+      docs_dir = pathlib.Path(r"{envdir}") / r"{envtmpdir}" / "html";\
+      index_file = docs_dir / "index.html";\
+      docs_url = os.environ["READTHEDOCS_CANONICAL_URL"] if IS_RTD_ENV \
+      else f"file://\{index_file\}";\
+      print(f"\nTo open the documentation, run\n\n\
+      \tpython3 -Im webbrowser \
+      \N\{QUOTATION MARK\}\{docs_url !s\}\N\{QUOTATION MARK\}\n");\
+      not IS_RTD_ENV and \
+      print(f"To serve \
+      the docs with a local web server, use\n\n\
+      \tpython3 -Im http.server --directory \
+      \N\{QUOTATION MARK\}\{docs_dir\}\N\{QUOTATION MARK\} 0\n")'
+deps =
+  -r{toxinidir}{/}requirements{/}doc.txt
+pass_env =
+  {[testenv]pass_env}
+  READTHEDOCS*  # Present @ RTD
+
+
+[testenv:spellcheck-docs]
+allowlist_externals =
+  {[testenv:build-docs]allowlist_externals}
+description = Spellcheck The Docs
+changedir = {[testenv:build-docs]changedir}
+commands_pre =
+  # Retrieve possibly missing commits:
+  -git fetch --unshallow
+  -git fetch --tags
+
+  # Clean up sphinxcontrib-apidoc generated RST files:
+  -git clean -x -f -- 'pkg{/}*.rst'
+commands =
+  {envpython} \
+    {[python-cli-options]byte-errors} \
+    {[python-cli-options]max-isolation} \
+    {[python-cli-options]warnings-to-errors} \
+    -m sphinx \
+      -j auto \
+      {tty:--color} \
+      -a \
+      -n \
+      -W --keep-going \
+      -b spelling --color \
+      -d "{temp_dir}{/}.doctrees" \
+      . "{toxworkdir}{/}spelling"
+commands_post =
+deps =
+  -r{toxinidir}{/}requirements{/}doc-spelling.txt
+pass_env =
+  {[testenv:build-docs]pass_env}
+
+
+[testenv:check]
+basepython = python3.13
+
+deps =
+  wheel
+  flake8
+  docutils
+  pygments
+  twine
+  build
+
+commands =
+  flake8 aiosignal tests
+  python -m build
+  python -m twine check --strict dist/*
+commands_post =
+
+[testenv:clean]
+basepython = python3.13
+
+deps = coverage
+skip_install = true
+
+commands =
+  coverage erase
+commands_post =
+
+[testenv:report]
+basepython = python3.13
+
+deps = coverage
+skip_install = true
+
+commands =
+  coverage report
+  coverage html
+  {envpython} -c '\
+    print("python -m webbrowser \
+    \N{Apostrophe}file://{toxinidir}/htmlcov/index.html\N{Apostrophe}")\
+  '
+commands_post =

Reply via email to