diff --git a/.gitignore b/.gitignore index 1d465a2..0a2761c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,150 @@ -*.swp +# Project-specific generated files +docs/build/ + +# Temp file during Mypy processing +mypy_annotate.dat -/.pytest_cache/ -/.coverage -/.cache/ -/.eggs/ -/.pybuild/ -/build/ -/docs/build/ -__pycache__/ -/trio_asyncio.egg-info/ -/.pybuild/ -/dist /empty + +# MacOS files +**/.DS_Store + +# Byte-compiled / optimized / DLL files / editor temp files +__pycache__/ +*.py[cod] +*$py.class +*~ +\#* +.#* +*.swp + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +pip-wheel-metadata/ + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +pyvenv.cfg +.venv/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +.mypy_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Ruff +.ruff_cache + +# Sphinx documentation +doc/_build/ + +# PyCharm +.idea/ diff --git a/CHEATSHEET.rst b/CHEATSHEET.rst index 2deead4..7328447 100644 --- a/CHEATSHEET.rst +++ b/CHEATSHEET.rst @@ -14,12 +14,12 @@ To run yapf ----------- * Show what changes yapf wants to make: - ``yapf3 -rpd setup.py trio_asyncio tests`` + ``yapf3 -rpd trio_asyncio tests`` * Apply all changes directly to the source tree: - ``yapf -rpi setup.py trio_asyncio tests`` + ``yapf -rpi trio_asyncio tests`` -* Find semantic problems: ``flake8 setup.py trio_asyncio tests`` +* Find semantic problems: ``flake8 trio_asyncio tests`` To make a release diff --git a/Makefile b/Makefile index 9079954..cf8af7b 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ .PHONY: doc test update all tag pypi upload all: - @echo "Please use 'python setup.py'." + @echo "Please use 'pip install -e .'." @exit 1 # need to use python3 sphinx-build @@ -43,12 +43,14 @@ test: tag: - @-git tag v$(shell python3 setup.py -V) + @-git tag v$(shell python3 -c "from trio_asyncio._version import __version__; print(__version__)") pypi: tag - @if python3 setup.py -V 2>/dev/null | grep -qs + >/dev/null 2>&1 ; \ + @if python3 python3 -c "from trio_asyncio._version import __version__; print(__version__)" 2>/dev/null | grep -qs + >/dev/null 2>&1 ; \ then echo "You need a clean, tagged tree" >&2; exit 1 ; fi - python3 setup.py sdist upload + python3 -m pip install uv + uv build + uv publish ## version depends on tag, so re-tagging doesn't make sense upload: pypi diff --git a/ci.sh b/ci.sh index ba194d6..5587fd0 100755 --- a/ci.sh +++ b/ci.sh @@ -41,8 +41,8 @@ BLACK_VERSION=24.1.0 if [ "$CHECK_FORMATTING" = "1" ]; then pip install black==${BLACK_VERSION} - if ! black --check setup.py tests trio_asyncio; then - black --diff setup.py tests trio_asyncio + if ! black --check tests src; then + black --diff tests src cat < None: + """Substitute the modified history file.""" + if docname == "history" and history_new is not None: + # This is a 1-item list with the file contents. + content[0] = history_new + # Warn about all references to unknown targets nitpicky = True @@ -94,8 +152,9 @@ # built documents. # # The short X.Y version. -import trio_asyncio -version = trio_asyncio.__version__ +import importlib.metadata + +version = importlib.metadata.version("trio_asyncio") # The full version, including alpha/beta/rc tags. release = version diff --git a/pyproject.toml b/pyproject.toml index cac350a..9e0e9de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,73 @@ +[build-system] +# setuptools v77 adds PEP 639 support +requires = ["setuptools >= 77"] +build-backend = "setuptools.build_meta" + +[project] +name = "trio_asyncio" +description = "A re-implementation of the asyncio mainloop on top of Trio" +authors = [{name = "Matthias Urlichs", email = "matthias@urlichs.de"}] +license = "MIT OR Apache-2.0" +license-files = ["LICENSE*"] +keywords = [ + "async", + "io", + "trio", + "asyncio", + "trio-asyncio" +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX :: BSD", + "Operating System :: Microsoft :: Windows", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: System :: Networking", + "Framework :: Trio", + "Framework :: AsyncIO", +] +requires-python = ">=3.9" +dependencies = [ + "trio >= 0.22.0", + "outcome", + "sniffio >= 1.3.0", + "exceptiongroup >= 1.0.0; python_version < '3.11'", + "greenlet", +] +dynamic = ["version"] + +[project.readme] +file = "README.rst" +content-type = "text/x-rst" + +[project.urls] +Homepage = "https://github.com/python-trio/trio-asyncio" +Documentation = "http://trio-asyncio.readthedocs.io" +Changelog = "https://trio-asyncio.readthedocs.io/en/latest/history.html" + +[tool.setuptools] +# This means, just install *everything* you see under trio/, even if it +# doesn't look like a source file, so long as it appears in MANIFEST.in: +include-package-data = true + +[tool.setuptools.dynamic] +version = {attr = "trio_asyncio._version.__version__"} + +[project.optional-dependencies] +tests = [ + "pytest>=5.4", + "pytest-trio >= 0.6", +] + [tool.pytest.ini_options] addopts = ["-p", "no:asyncio"] filterwarnings = [ diff --git a/setup.py b/setup.py deleted file mode 100644 index 1d2a44e..0000000 --- a/setup.py +++ /dev/null @@ -1,100 +0,0 @@ -from setuptools import setup -import sys - -exec(open("trio_asyncio/_version.py", encoding="utf-8").read()) - -LONG_DESC = """\ -``trio-asyncio`` is a re-implementation of the ``asyncio`` mainloop on top of -Trio. - -Rationale -========= - -There are quite a few asyncio-compatible libraries. - -On the other hand, Trio has native concepts of tasks and task cancellation. -Asyncio, on the other hand, is based on chaining Future objects, albeit -with nicer syntax. - -Thus, being able to use asyncio libraries from Trio is useful. - -Principle of operation -====================== - -The core of the "normal" asyncio main loop is the repeated execution of -synchronous code that's submitted to ``call_soon`` or -``add_reader``/``add_writer``. - -Everything else within ``asyncio``, i.e. Futures and ``async``/``await``, -is just syntactic sugar. There is no concept of a task; while a Future can -be cancelled, that in itself doesn't affect the code responsible for -fulfilling it. - -On the other hand, trio has genuine tasks with no separation between -returning a value asynchronously, and the code responsible for providing -that value. - -``trio_asyncio`` implements a task which runs (its own version of) the -asyncio main loop. It also contains shim code which translates between these -concepts as transparently and correctly as possible, and it supplants a few -of the standard loop's key functions. - -This works rather well: ``trio_asyncio`` consists of just ~700 lines of -code (asyncio: ~8000) but passes the complete Python 3.6 test suite with no -errors. - -``trio_asyncio`` requires Python 3.9 or later. - -Author -====== - -Matthias Urlichs - -""" - -setup( - name="trio_asyncio", - version=__version__, # noqa: F821 - description="A re-implementation of the asyncio mainloop on top of Trio", - long_description=LONG_DESC, - author="Matthias Urlichs", - author_email="matthias@urlichs.de", - url="https://github.com/python-trio/trio-asyncio", - license="MIT -or- Apache License 2.0", - packages=["trio_asyncio"], - install_requires=[ - "trio >= 0.22.0", - "outcome", - "sniffio >= 1.3.0", - "exceptiongroup >= 1.0.0; python_version < '3.11'", - "greenlet", - ], - # This means, just install *everything* you see under trio/, even if it - # doesn't look like a source file, so long as it appears in MANIFEST.in: - include_package_data=True, - python_requires=">=3.9", - keywords=["async", "io", "trio", "asyncio", "trio-asyncio"], - setup_requires=["pytest-runner"], - tests_require=["pytest >= 5.4", "pytest-trio >= 0.6", "outcome"], - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "License :: OSI Approved :: Apache Software License", - "Operating System :: POSIX :: Linux", - "Operating System :: MacOS :: MacOS X", - "Operating System :: POSIX :: BSD", - "Operating System :: Microsoft :: Windows", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Topic :: System :: Networking", - "Framework :: Trio", - "Framework :: AsyncIO", - ], -) diff --git a/trio_asyncio/__init__.py b/src/trio_asyncio/__init__.py similarity index 100% rename from trio_asyncio/__init__.py rename to src/trio_asyncio/__init__.py diff --git a/trio_asyncio/_adapter.py b/src/trio_asyncio/_adapter.py similarity index 100% rename from trio_asyncio/_adapter.py rename to src/trio_asyncio/_adapter.py diff --git a/trio_asyncio/_async.py b/src/trio_asyncio/_async.py similarity index 100% rename from trio_asyncio/_async.py rename to src/trio_asyncio/_async.py diff --git a/trio_asyncio/_base.py b/src/trio_asyncio/_base.py similarity index 99% rename from trio_asyncio/_base.py rename to src/trio_asyncio/_base.py index 1bd4ddb..f151081 100644 --- a/trio_asyncio/_base.py +++ b/src/trio_asyncio/_base.py @@ -432,7 +432,7 @@ async def _make_subprocess_transport( stderr, bufsize, extra=None, - **kwargs + **kwargs, ): """Make a subprocess transport. Asyncio context.""" @@ -448,7 +448,7 @@ async def _make_subprocess_transport( bufsize, waiter=waiter, extra=extra, - **kwargs + **kwargs, ) async def child_wait(transp): diff --git a/trio_asyncio/_child.py b/src/trio_asyncio/_child.py similarity index 100% rename from trio_asyncio/_child.py rename to src/trio_asyncio/_child.py diff --git a/trio_asyncio/_deprecate.py b/src/trio_asyncio/_deprecate.py similarity index 100% rename from trio_asyncio/_deprecate.py rename to src/trio_asyncio/_deprecate.py diff --git a/trio_asyncio/_handles.py b/src/trio_asyncio/_handles.py similarity index 100% rename from trio_asyncio/_handles.py rename to src/trio_asyncio/_handles.py diff --git a/trio_asyncio/_loop.py b/src/trio_asyncio/_loop.py similarity index 100% rename from trio_asyncio/_loop.py rename to src/trio_asyncio/_loop.py diff --git a/trio_asyncio/_sync.py b/src/trio_asyncio/_sync.py similarity index 100% rename from trio_asyncio/_sync.py rename to src/trio_asyncio/_sync.py diff --git a/trio_asyncio/_util.py b/src/trio_asyncio/_util.py similarity index 100% rename from trio_asyncio/_util.py rename to src/trio_asyncio/_util.py diff --git a/trio_asyncio/_version.py b/src/trio_asyncio/_version.py similarity index 100% rename from trio_asyncio/_version.py rename to src/trio_asyncio/_version.py diff --git a/tests/test_trio_asyncio.py b/tests/test_trio_asyncio.py index dfbb7e5..4c8acdf 100644 --- a/tests/test_trio_asyncio.py +++ b/tests/test_trio_asyncio.py @@ -249,7 +249,10 @@ async def iterate_one(label, extra=""): with trio.move_on_after(loop_timeout) as scope: if loop_timeout == 0: scope.cancel() - async with trio_asyncio.open_loop() as loop, trio_asyncio.open_loop() as loop2: + async with ( + trio_asyncio.open_loop() as loop, + trio_asyncio.open_loop() as loop2, + ): assert sys.get_asyncgen_hooks() != before_hooks async with trio.open_nursery() as nursery: # Make sure the iterate_one aio tasks don't get