diff --git a/.flake8 b/.flake8 index 0eff6d6..52e1789 100644 --- a/.flake8 +++ b/.flake8 @@ -3,4 +3,3 @@ extend-select = B901, B902, B903, B904 extend-ignore = ANN101, ANN102 per-file-ignores = test/*: ANN - tasks/*: ANN, B028 diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-check.yml similarity index 55% rename from .github/workflows/python-package.yml rename to .github/workflows/python-check.yml index b407d7d..960596d 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-check.yml @@ -1,4 +1,4 @@ -name: build +name: check on: push: @@ -8,8 +8,8 @@ on: jobs: build: - runs-on: ubuntu-latest + strategy: fail-fast: false matrix: @@ -23,33 +23,13 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install build tools run: | - python -m pip install --upgrade pip invoke coveralls - - name: Install package and dependencies - run: | - invoke install + python -m pip install hatch coveralls - name: Run test suites, type checks, and linters run: | - invoke validate + hatch run check - name: Report test coverage to Coveralls if: success() env: GITHUB_TOKEN: ${{ github.token }} run: | coveralls --service=github - - docs: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - name: Install build tools - run: | - python -m pip install --upgrade pip invoke - - name: Build the Sphinx documentation - run: | - invoke install doc.install doc.build diff --git a/.github/workflows/python-doc.yml b/.github/workflows/python-doc.yml new file mode 100644 index 0000000..900a5e2 --- /dev/null +++ b/.github/workflows/python-doc.yml @@ -0,0 +1,32 @@ +name: doc + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + release: + types: [ published ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + - name: Install build tools + run: | + python -m pip install hatch + - name: Build the Sphinx documentation + run: | + hatch run doc:build + - name: Deploy to GitHub Pages + if: github.event_name == 'release' + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ github.token }} + publish_dir: ./doc/build/html diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 68373bb..fd570d2 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -6,7 +6,6 @@ on: jobs: deploy: - runs-on: ubuntu-latest steps: @@ -17,34 +16,13 @@ jobs: python-version: '3.11' - name: Install build tools run: | - python -m pip install --upgrade pip build twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - python -m build - twine upload dist/* - - docs: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - name: Install build tools + python -m pip install hatch + - name: Build distributions run: | - python -m pip install --upgrade pip invoke - - name: Build the Sphinx documentation + hatch build + - name: Publish distributions + env: + HATCH_INDEX_USER: ${{ secrets.PYPI_USERNAME }} + HATCH_INDEX_AUTH: ${{ secrets.PYPI_PASSWORD }} run: | - invoke install doc.install doc.build - - name: Deploy to GitHub Pages - if: success() - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ github.token }} - publish_dir: ./doc/build/html + hatch publish diff --git a/README.md b/README.md index 641a835..3610d4f 100644 --- a/README.md +++ b/README.md @@ -130,11 +130,20 @@ synchronize larger amounts of metadata. ## Development -First off, I suggest activating a [venv][3]. Then, install the development -requirements and a local link to the *swim-protocol* package: +You will need to do some additional setup to develop and test plugins. Install +[Hatch][3] to use the CLI examples below. +Run all tests and linters: + +```console +$ hatch run check ``` -$ pip install -r requirements-dev.txt + +Because this project supports several versions of Python, you can use the +following to run the checks on all versions: + +```console +$ hatch run all:check ``` ### Type Hinting @@ -152,7 +161,7 @@ hinting to the extent possible and common in the rest of the codebase. [0]: https://www.cs.cornell.edu/projects/Quicksilver/public_pdfs/SWIM.pdf [1]: https://docs.python.org/3/library/asyncio.html [2]: https://github.com/icgood/swim-protocol/blob/main/swimprotocol/demo/__init__.py -[3]: https://docs.python.org/3/library/venv.html +[3]: https://hatch.pypa.io/latest/install/ [4]: https://docs.python.org/3/library/typing.html [5]: http://mypy-lang.org/ [6]: https://en.wikipedia.org/wiki/Eventual_consistency diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index eb6efc6..0000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -sphinx -sphinx-autodoc-typehints -cloud_sptheme diff --git a/pyproject.toml b/pyproject.toml index a953999..554b954 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ build-backend = 'hatchling.build' [project] name = 'swim-protocol' -version = '0.3.11' +dynamic = ['version'] authors = [ { name = 'Ian Good', email = 'ian@icgood.net' }, ] @@ -47,20 +47,24 @@ dependencies = [ 'importlib-metadata; python_version < "3.10"', ] -[project.optional-dependencies] -crc32c = ['crc32c ~= 2.2'] - [project.urls] -homepage = 'https://github.com/icgood/swim-protocol/' +'Homepage' = 'https://github.com/icgood/swim-protocol/' +'API Documentation' = 'https://icgood.github.io/swim-protocol/' [project.scripts] swim-protocol-demo = 'swimprotocol.demo:main' +[tool.hatch.version] +path = 'swimprotocol/__about__.py' + [project.entry-points.'swimprotocol.transport'] udp = 'swimprotocol.udp:UdpTransport' [tool.hatch.build] -exclude = ['/tasks', '/doc', '/.github'] +exclude = ['/doc', '/.github'] + +[tool.hatch.build.targets.wheel] +packages = ['swimprotocol'] [tool.mypy] strict = true @@ -74,10 +78,43 @@ testpaths = 'test' norecursedirs = 'doc' [tool.coverage.report] -omit = ['*/main.py', '*/demo.py'] +omit = ['*/__about__.py', '*/main.py', '*/demo.py'] exclude_lines = [ 'pragma: no cover', 'NotImplemented', '^\s*...\s*$', 'def __repr__', ] + +[tool.hatch.envs.default] +dependencies = [ + 'mypy', + 'pytest', + 'pytest-asyncio', + 'pytest-cov', + 'flake8', + 'flake8-annotations', + 'flake8-bugbear', + 'bandit[toml]', +] + +[tool.hatch.envs.default.scripts] +run-pytest = 'py.test --cov-report=term-missing --cov=swimprotocol' +run-mypy = 'mypy swimprotocol test' +run-flake8 = 'flake8 swimprotocol test' +run-bandit = 'bandit -c pyproject.toml -qr swimprotocol' +check = ['run-pytest', 'run-mypy', 'run-flake8', 'run-bandit'] + +[[tool.hatch.envs.all.matrix]] +python = ['3.9', '3.10', '3.11'] + +[tool.hatch.envs.doc] +dependencies = [ + 'sphinx', + 'sphinx-autodoc-typehints', + 'cloud_sptheme', +] + +[tool.hatch.envs.doc.scripts] +build = 'make -C doc html' +browse = ['build', 'open doc/build/html/index.html'] diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index eed55cd..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,12 +0,0 @@ -mypy -flake8 -flake8-annotations -flake8-bugbear -autopep8 -pytest -pytest-asyncio -pytest-cov -bandit[toml] -rope - --e '.' diff --git a/swimprotocol/__about__.py b/swimprotocol/__about__.py new file mode 100644 index 0000000..93caf30 --- /dev/null +++ b/swimprotocol/__about__.py @@ -0,0 +1,2 @@ +#: The package version string. +__version__ = '0.3.12' diff --git a/swimprotocol/__init__.py b/swimprotocol/__init__.py index f919e44..e69de29 100644 --- a/swimprotocol/__init__.py +++ b/swimprotocol/__init__.py @@ -1,12 +0,0 @@ - -from __future__ import annotations - -from importlib.metadata import distribution - -__all__ = ['__version__'] - -#: The package version string. -#: -#: See Also: -#: `PEP 396 `_ -__version__: str = distribution('swim-protocol').version diff --git a/swimprotocol/sign.py b/swimprotocol/sign.py index 811cb0c..9efc498 100644 --- a/swimprotocol/sign.py +++ b/swimprotocol/sign.py @@ -7,7 +7,7 @@ import uuid from typing import Final, Union -from . import __version__ +from .__about__ import __version__ __all__ = ['Signatures'] diff --git a/tasks/__init__.py b/tasks/__init__.py deleted file mode 100644 index 1bcde82..0000000 --- a/tasks/__init__.py +++ /dev/null @@ -1,71 +0,0 @@ -# type: ignore - -import inspect -import os -import os.path -from shlex import join - -if not hasattr(inspect, 'getargspec'): - # https://github.com/pyinvoke/invoke/issues/833 - inspect.getargspec = inspect.getfullargspec - -from invoke import task, Collection - -from . import check, doc, lint, test, types - - -@task -def clean(ctx, full=False): - """Delete all the standard build and validate artifacts.""" - if full: - ctx.run('git clean -dfX') - else: - anywhere = ['__pycache__'] - top_level = [ - '.coverage', - '.mypy_cache', - '.pytest_cache', - 'dist', - 'doc/build/'] - for name in anywhere: - for path in [ctx.package, 'test']: - subpaths = [os.path.join(subpath, name) - for subpath, dirs, names in os.walk(path) - if name in dirs or name in names] - for subpath in subpaths: - ctx.run(join(['rm', '-rf', subpath])) - for name in top_level: - ctx.run(join(['rm', '-rf', name])) - - -@task -def install(ctx, dev=True, update=False): - """Install the library and all development tools.""" - choice = 'dev' if dev else 'all' - if update: - ctx.run('pip install -U -r requirements-{}.txt'.format(choice)) - else: - ctx.run('pip install -r requirements-{}.txt'.format(choice)) - - -@task(test.all, types.all, lint.all) -def validate(ctx): - """Run all tests, type checks, and linters.""" - pass - - -ns = Collection(clean, install) -ns.add_task(validate, default=True) -ns.add_collection(check) -ns.add_collection(test) -ns.add_collection(types) -ns.add_collection(lint) -ns.add_collection(doc) - -ns.configure({ - 'package': 'swimprotocol', - 'run': { - 'echo': True, - 'pty': True, - } -}) diff --git a/tasks/check.py b/tasks/check.py deleted file mode 100644 index 43d7052..0000000 --- a/tasks/check.py +++ /dev/null @@ -1,19 +0,0 @@ -# type: ignore - -import warnings - -from invoke import task, Collection - - -@task -def check_import(ctx): - """Check that the library can be imported.""" - try: - __import__(ctx.package) - except Exception: - warnings.warn('Could not import {!r}, ' - 'task may fail'.format(ctx.package)) - - -ns = Collection() -ns.add_task(check_import, default=True) diff --git a/tasks/doc.py b/tasks/doc.py deleted file mode 100644 index 71112e9..0000000 --- a/tasks/doc.py +++ /dev/null @@ -1,34 +0,0 @@ -# type: ignore - -from invoke import task, Collection - - -@task -def install(ctx, update=False): - """Install the tools needed to build the docs.""" - if update: - ctx.run('pip install -U -r doc/requirements.txt') - elif not ctx.run('which sphinx-build', hide=True, warn=True): - ctx.run('pip install -r doc/requirements.txt') - - -@task(install) -def clean(ctx): - """Clean up the doc build directory.""" - ctx.run('make -C doc clean') - - -@task(install) -def build(ctx): - """Build the HTML docs.""" - ctx.run('make -C doc html') - - -@task(install, build) -def open(ctx): - """Open the docs in a browser (on macOS).""" - ctx.run('open doc/_build/html/index.html') - - -ns = Collection(install, clean, open) -ns.add_task(build, default=True) diff --git a/tasks/lint.py b/tasks/lint.py deleted file mode 100644 index fac4cfe..0000000 --- a/tasks/lint.py +++ /dev/null @@ -1,27 +0,0 @@ -# type: ignore - -from invoke import task, Collection - -from .check import check_import - - -@task(check_import) -def flake8(ctx): - """Run the flake8 linter.""" - ctx.run('flake8 {} test {}'.format(ctx.package, __package__)) - - -@task(check_import) -def bandit(ctx): - """Run the bandit linter.""" - ctx.run('bandit -c pyproject.toml -qr {}'.format(ctx.package)) - - -@task(flake8, bandit) -def all(ctx): - """Run all linters.""" - pass - - -ns = Collection(flake8, bandit) -ns.add_task(all, default=True) diff --git a/tasks/test.py b/tasks/test.py deleted file mode 100644 index 92645d4..0000000 --- a/tasks/test.py +++ /dev/null @@ -1,21 +0,0 @@ -# type: ignore - -from invoke import task, Collection - -from .check import check_import - - -@task(check_import) -def pytest(ctx): - """Run the unit tests with py.test.""" - ctx.run('py.test --cov={} --cov-report=term-missing'.format(ctx.package)) - - -@task(pytest) -def all(ctx): - """Run all test utilities.""" - pass - - -ns = Collection(pytest) -ns.add_task(all, default=True) diff --git a/tasks/types.py b/tasks/types.py deleted file mode 100644 index 2d1a0ab..0000000 --- a/tasks/types.py +++ /dev/null @@ -1,21 +0,0 @@ -# type: ignore - -from invoke import task, Collection - -from .check import check_import - - -@task(check_import) -def mypy(ctx): - """Run the mypy type checker.""" - ctx.run('mypy {} test'.format(ctx.package)) - - -@task(mypy) -def all(ctx): - """Run all the type checker tools.""" - pass - - -ns = Collection(mypy) -ns.add_task(all, default=True)