diff --git a/.github/workflows/nix-ci.yml b/.github/workflows/nix-ci.yml index 75ab837f7..eed2b1907 100644 --- a/.github/workflows/nix-ci.yml +++ b/.github/workflows/nix-ci.yml @@ -25,4 +25,4 @@ jobs: nix_path: nixpkgs=channel:${{ matrix.os == 'macos-latest' && 'nixpkgs-23.11-darwin' || 'nixos-23.11' }} - name: Run tests - run: nix-shell --run "pytest" + run: nix-shell --run "hatch run testing:test" diff --git a/.github/workflows/pyaleph-ci.yml b/.github/workflows/pyaleph-ci.yml index e6d5f4ea1..c8dc4dee7 100644 --- a/.github/workflows/pyaleph-ci.yml +++ b/.github/workflows/pyaleph-ci.yml @@ -34,11 +34,13 @@ jobs: with: # Fetch the whole history for all tags and branches (required for aleph.__version__) fetch-depth: 0 + - name: Set up Python 3.11 id: setup-python uses: actions/setup-python@v2 with: python-version: 3.11 + - name: Install latest Rust nightly toolchain uses: actions-rs/toolchain@v1 with: @@ -49,33 +51,36 @@ jobs: id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" + + - name: "install hatch" + run: | + pip install hatch + - uses: actions/cache@v2 with: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ runner.os }}-python-${{ steps.setup-python.outputs.python-version }}-pip-${{ hashFiles('setup.cfg') }} + - name: Install Python dependencies run: | rustup default nightly # Required to build some dependencies pip install wheel pip install --upgrade .[testing] - - name: Check types - run: | - mypy src/ - - name: Check types in tests - run: | - mypy tests/ + - name: Run unit tests run: | sudo cp .github/openssl-ci.cnf /etc/ssl/openssl.cnf export OPENSSL_CONF=/etc/ssl/openssl.cnf touch config.yml # Fake config file for alembic # TODO: determine why ResourceWarning warnings occur in some tests. - pytest -Werror -Wignore::ResourceWarning -v --cov . + hatch run testing:cov + - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 with: token: ${{ secrets.CODECOV_TOKEN }} slug: aleph-im/pyaleph + build: runs-on: ubuntu-22.04 needs: tests diff --git a/README.md b/README.md index 6cd32356d..5d8ade04b 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ This will provide you with a shell with PostgreSQL, Redis, and IPFS running. To run test you can run: ```bash -nix-shell --run "pytest" +nix-shell --run "hatch run testing:test" ``` Or you can run the command in the nix shell: @@ -63,7 +63,7 @@ Or you can run the command in the nix shell: nix-shell # inside of nix shell -pytest +hatch run testing:test ``` ## Software used diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index f17126a72..000000000 --- a/mypy.ini +++ /dev/null @@ -1,72 +0,0 @@ -# Global options: - -[mypy] -python_version = 3.11 -plugins = sqlalchemy.ext.mypy.plugin - -mypy_path = src - -exclude = conftest.py - - -show_column_numbers = True - -# Suppressing errors -# Shows errors related to strict None checking, if the global strict_optional flag is enabled -strict_optional = True -no_implicit_optional = True - -# Import discovery -# Suppresses error messages about imports that cannot be resolved -ignore_missing_imports = True -# Forces import to reference the original source file -no_implicit_reexport = True -# show error messages from unrelated files -follow_imports = silent -follow_imports_for_stubs = False - - -# Disallow dynamic typing -# Disallows usage of types that come from unfollowed imports -disallow_any_unimported = False -# Disallows all expressions in the module that have type Any -disallow_any_expr = False -# Disallows functions that have Any in their signature after decorator transformation. -disallow_any_decorated = False -# Disallows explicit Any in type positions such as type annotations and generic type parameters. -disallow_any_explicit = False -# Disallows usage of generic types that do not specify explicit type parameters. -disallow_any_generics = False -# Disallows subclassing a value of type Any. -disallow_subclassing_any = False - -# Untyped definitions and calls -# Disallows calling functions without type annotations from functions with type annotations. -disallow_untyped_calls = False -# Disallows defining functions without type annotations or with incomplete type annotations -disallow_untyped_defs = False -# Disallows defining functions with incomplete type annotations. -check_untyped_defs = False -# Type-checks the interior of functions without type annotations. -disallow_incomplete_defs = False -# Reports an error whenever a function with type annotations is decorated with a decorator without annotations. -disallow_untyped_decorators = False - -# Prohibit comparisons of non-overlapping types (ex: 42 == "no") -strict_equality = True - -# Configuring warnings -# Warns about unneeded # type: ignore comments. -warn_unused_ignores = True -# Shows errors for missing return statements on some execution paths. -warn_no_return = True -# Shows a warning when returning a value with type Any from a function declared with a non- Any return type. -warn_return_any = False - -# Miscellaneous strictness flags -# Allows variables to be redefined with an arbitrary type, as long as the redefinition is in the same block and nesting level as the original definition. -allow_redefinition = True - -# Ignore the imported code from py-libp2p -[mypy-aleph.toolkit.libp2p_stubs.*] -ignore_errors = True diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..0f88c4c18 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,309 @@ +[build-system] +requires = ["hatchling", "hatch-vcs"] +build-backend = "hatchling.build" + +[project] +name = "pyaleph" +dynamic = ["version"] +description = "Reference implementation of Aleph.im next generation network of decentralized big data applications." +requires-python = ">=3.11,<3.12" +readme = "README.md" +license = { file = "LICENSE.txt" } +authors = [ + { name = "Moshe Malawach", email = "moshe.malawach@protonmail.com" }, +] +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python", +] +dependencies = [ + "aio_pika==9.1.3", + "aiocache==0.12.2", + "aiohttp-cors==0.7.0", + "aiohttp-jinja2==1.5", + "aiohttp==3.8.4", + "aioipfs@git+https://github.com/aleph-im/aioipfs.git@d671c79b2871bb4d6c8877ba1e7f3ffbe7d20b71", + "alembic==1.12.1", + "aleph-message==0.4.2", + "aleph-p2p-client@git+https://github.com/aleph-im/p2p-service-client-python@2c04af39c566217f629fd89505ffc3270fba8676", + "aleph-pytezos@git+https://github.com/aleph-im/aleph-pytezos.git@32dd1749a4773da494275709060632cbeba9a51b", + "asyncpg==0.28.0", + "base58>=1.0.3", + "coincurve==18.0.0", + "configmanager==1.35.1", + "configparser==6.0.0", + "cosmospy==6.0.0", + "dataclasses_json==0.5.6", + "eth_account==0.10.0", + "eth-typing~=4.0", + "gunicorn==21.2.0", + "hexbytes==0.2.2", + "msgpack==1.0.3", # required by aiocache + "multiaddr==0.0.9", # for libp2p-stubs + "aleph-nuls2==0.1.0", + "orjson>=3.7.7", # Minimum version for Python 3.11 + "psycopg2-binary==2.9.5", # Note: psycopg3 is not yet supported by SQLAlchemy + "pycryptodome==3.17.0", # for libp2p-stubs + "pymultihash==0.8.2", # for libp2p-stubs + "pynacl==1.5.0", + "python-dateutil==2.8.2", + "pytz==2023.3", + "pyyaml==6.0", + "redis[hiredis]==5.0.1", + "requests==2.31.0", + "secp256k1==0.14.0", + "sentry-sdk==1.34.0", + "setproctitle==1.3.3", + "sqlalchemy[mypy]==1.4.41", + "sqlalchemy-utils==0.38.3", + "substrate-interface==1.7.4", + "ujson==5.1.0", # required by aiocache + "urllib3==2.0.7", + "uvloop==0.19.0", + "web3==6.11.2", + "aiofiles==23.2.1", + "types-aiofiles==23.2.0.20240403", +] +dependency_links = [ + "https://github.com/aleph-im/py-libp2p/tarball/0.1.4-1-use-set#egg=libp2p", + "https://github.com/aleph-im/aioipfs/tarball/hoh-more-exceptions#egg=aioipfs", +] + +[project.optional-dependencies] +nuls2 = [ + "aleph-nuls2==0.1.0", + "py-ed25519-bindings==1.0.2; python_version >=\"3.11\"", +] +polkadot = [ + "substrate-interface>=0.9.27", +] +cosmos = [ + "cosmospy", +] +docs = [ + "sphinxcontrib-plantuml", +] + +[tool.hatch.metadata] +allow-direct-references = true + +[project.urls] +Documentation = "https://aleph.im/" +Homepage = "https://github.com/aleph-im/pyaleph" + +[tool.hatch.version] +source = "vcs" + +[tool.hatch.build.targets.wheel] +packages = [ + "alembic.ini", + "src/aleph", + "pyproject.toml", + "AUTHORS.rst", + "CHANGELOG.rst", + "README.md", + "LICENSE.txt", + "code-of-conduct.md", +] + +[tool.hatch.build.targets.sdist] +include = [ + "alembic.ini", + "src/aleph", + "pyproject.toml", + "AUTHORS.rst", + "CHANGELOG.rst", + "README.md", + "LICENSE.txt", + "code-of-conduct.md", +] + +[tool.isort] +profile = "black" + +[tool.hatch.envs.testing] +features = [ + "cosmos", + "nuls2", + "polkadot", +] +dependencies = [ + "ipfshttpclient==0.8.0a2", # eth/web3 test dependency, for some reason" + "more-itertools==8.14.0", + "mypy==1.2.0", + "pytest", + "pytest-cov", + "pytest-aiohttp", + "pytest-asyncio", + "pytest-mock", + "types-pytz", + "types-redis", + "types-requests", + "types-setuptools", +] + +# XXX see https://github.com/aleph-im/pyaleph/blob/main/.github/workflows/pyaleph-ci.yml +[tool.hatch.envs.testing.scripts] +test = "pytest {args:} ." +test-cov = "pytest --cov {args:} ." +cov-report = [ + "- coverage combine", + "coverage report", +] +cov = [ + "test-cov", + "cov-report", +] + +[tool.hatch.envs.linting] +detached = true +dependencies = [ + "black==24.2.0", + "mypy==1.9.0", + "mypy-extensions==1.0.0", + "ruff==0.4.8", + "isort==5.13.2", +] +[tool.hatch.envs.linting.scripts] +typing = "mypy --config-file=pyproject.toml {args:} ./src/ ./tests/" +style = [ + "ruff check {args:.} ./src/ ./tests/", + "black --check --diff {args:} ./src/ ./tests/", + "isort --check-only --profile black {args:} ./src/ ./tests/", +] +fmt = [ + "black {args:} ./src/ ./tests/", + "ruff check --fix {args:.} ./src/ ./tests/", + "isort --profile black {args:} ./src/ ./tests/", + "style", +] +all = [ + "style", + "typing", +] + +[tool.mypy] +python_version = 3.11 +mypy_path = "src" +plugins = [ + "sqlalchemy.ext.mypy.plugin", +] +exclude = [ + "conftest.py" +] +show_column_numbers = true + +# Import discovery +# Install types for third-party library stubs (e.g. from typeshed repository) +install_types = true +non_interactive = true + +# Import discovery +# Suppresses error messages about imports that cannot be resolved +ignore_missing_imports = true +# Forces import to reference the original source file +no_implicit_reexport = true +# show error messages from unrelated files +follow_imports = "silent" +follow_imports_for_stubs = false + +# Suppressing errors +# Shows errors related to strict None checking, if the global strict_optional +# flag is enabled +strict_optional = true +no_implicit_optional = true + +# Disallow dynamic typing +# Disallows usage of types that come from unfollowed imports +disallow_any_unimported = false +# Disallows all expressions in the module that have type Any +disallow_any_expr = false +# Disallows functions that have Any in their signature after decorator transformation. +disallow_any_decorated = false +# Disallows explicit Any in type positions such as type annotations and generic type parameters. +disallow_any_explicit = false +# Disallows usage of generic types that do not specify explicit type parameters. +disallow_any_generics = false +# Disallows subclassing a value of type Any. +disallow_subclassing_any = false + +# Untyped definitions and calls +# Disallows calling functions without type annotations from functions with type annotations. +disallow_untyped_calls = false +# Disallows defining functions without type annotations or with incomplete type annotations +disallow_untyped_defs = false +# Disallows defining functions with incomplete type annotations. +check_untyped_defs = false +# Type-checks the interior of functions without type annotations. +disallow_incomplete_defs = false +# Reports an error whenever a function with type annotations is decorated with a decorator without annotations. +disallow_untyped_decorators = false + +# Prohibit comparisons of non-overlapping types (ex: 42 == "no") +strict_equality = true + +# Configuring warnings +# Warns about unneeded # type: ignore comments. +warn_unused_ignores = true +# Shows errors for missing return statements on some execution paths. +warn_no_return = true +# Shows a warning when returning a value with type Any from a function declared with a non- Any return type. +warn_return_any = false + +# Miscellaneous strictness flags +# Allows variables to be redefined with an arbitrary type, as long as the redefinition is in the same block and nesting level as the original definition. +allow_redefinition = true + +# XXX does this thing really works? +# Ignore the imported code from py-libp2p +# [mypy-aleph.toolkit.libp2p_stubs.""] +# ignore_errors = True + +[tool.pytest.ini_options] +minversion = "6.0" +pythonpath = ["src"] +addopts = "-vv -m \"not ledger_hardware\"" +norecursedirs = ["*.egg", "dist", "build", ".tox", ".venv", "*/site-packages/*"] +testpaths = ["tests/unit"] +markers = {ledger_hardware = "marks tests as requiring ledger hardware"} + +[tool.coverage.run] +branch = true +parallel = true +source = [ + "src/", +] +omit = [ + "*/site-packages/*", +] + +[tool.coverage.paths] +source = [ + "src/", +] +omit = [ + "*/site-packages/*", +] + +[tool.coverage.report] +show_missing = true +skip_empty = true +exclude_lines = [ + # Have to re-enable the standard pragma + "pragma: no cover", + + # Don't complain about missing debug-only code: + "def __repr__", + "if self\\.debug", + + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + + # Don't complain if non-runnable code isn't run: + "if 0:", + "if __name__ == .__main__.:", + + # Don't complain about ineffective code: + "pass", +] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 933510ef5..000000000 --- a/setup.cfg +++ /dev/null @@ -1,189 +0,0 @@ -# This file is used to configure your project. -# Read more about the various options under: -# http://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files - -[metadata] -name = pyaleph -description = Reference implementation of Aleph.im next generation network of decentralized big data applications. -author = Moshe Malawach -author_email = moshe.malawach@protonmail.com -license = mit -url = https://aleph.im -long_description = file: README.md -long_description_content_type = text/markdown -# Change if running only on Windows, Mac or Linux (comma-separated) -platforms = any -# Add here all kinds of additional classifiers as defined under -# https://pypi.python.org/pypi?%3Aaction=list_classifiers -classifiers = - Development Status :: 4 - Beta - Programming Language :: Python - -[options] -zip_safe = False -packages = find: -include_package_data = True -package_dir = - =src -# DON'T CHANGE THE FOLLOWING LINE! IT WILL BE UPDATED BY PYSCAFFOLD! -setup_requires = - setuptools_scm>=8.0.4 - pytest-runner>=2.0,<3dev - -# Note: eth/web3 dependencies updates are sensitive and can trigger a lot of dependency conflicts. -# Update with care. Dependencies that were added to make it all work are annotated accordingly. -install_requires = - aio_pika==9.1.3 - aiocache==0.12.2 - aiohttp-cors==0.7.0 - aiohttp-jinja2==1.5 - aiohttp==3.8.4 - aioipfs@git+https://github.com/aleph-im/aioipfs.git@d671c79b2871bb4d6c8877ba1e7f3ffbe7d20b71 - alembic==1.12.1 - aleph-message==0.4.2 - aleph-p2p-client@git+https://github.com/aleph-im/p2p-service-client-python@2c04af39c566217f629fd89505ffc3270fba8676 - aleph-pytezos@git+https://github.com/aleph-im/aleph-pytezos.git@32dd1749a4773da494275709060632cbeba9a51b - asyncpg==0.28.0 - base58>=1.0.3 - coincurve==18.0.0 - configmanager==1.35.1 - configparser==6.0.0 - cosmospy==6.0.0 - dataclasses_json==0.5.6 - eth_account==0.10.0 - eth-typing~=4.0 - gunicorn==21.2.0 - hexbytes==0.2.2 - msgpack==1.0.3 # required by aiocache - multiaddr==0.0.9 # for libp2p-stubs - aleph-nuls2==0.1.0 - orjson>=3.7.7 # Minimum version for Python 3.11 - psycopg2-binary==2.9.5 # Note: psycopg3 is not yet supported by SQLAlchemy - pycryptodome==3.17.0 # for libp2p-stubs - pymultihash==0.8.2 # for libp2p-stubs - pynacl==1.5.0 - python-dateutil==2.8.2 - pytz==2023.3 - pyyaml==6.0 - redis[hiredis]==5.0.1 - requests==2.31.0 - secp256k1==0.14.0 - sentry-sdk==1.34.0 - setproctitle==1.3.3 - sqlalchemy[mypy]==1.4.41 - sqlalchemy-utils==0.38.3 - substrate-interface==1.7.4 - ujson==5.1.0 # required by aiocache - urllib3==2.0.7 - uvloop==0.19.0 - web3==6.11.2 - aiofiles==23.2.1 - types-aiofiles==23.2.0.20240403 - -dependency_links = - https://github.com/aleph-im/py-libp2p/tarball/0.1.4-1-use-set#egg=libp2p - https://github.com/aleph-im/aioipfs/tarball/hoh-more-exceptions#egg=aioipfs - -# The usage of test_requires is discouraged, see `Dependency Management` docs -# tests_require = pytest; pytest-cov -# Require a specific Python version, e.g. Python 2.7 or >= 3.4 -# python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* - -[options.packages.find] -where = src -exclude = - tests - -[options.extras_require] -# Add here additional requirements for extra features, to install with: -# `pip install pyaleph[PDF]` like: -# PDF = ReportLab; RXP -# Add here test requirements (semicolon/line-separated) -testing = - ipfshttpclient==0.8.0a2 # eth/web3 test dependency, for some reason - more-itertools==8.14.0 - mypy==1.2.0 - pytest - pytest-cov - pytest-aiohttp - pytest-asyncio - pytest-mock - types-pytz - types-redis - types-requests - types-setuptools -nuls2 = - aleph-nuls2==0.1.0 - py-ed25519-bindings==1.0.2 # Python 3.11 -polkadot = - substrate-interface>=0.9.27 -cosmos = - cosmospy -docs = - sphinxcontrib-plantuml - -[options.entry_points] -# Add here console scripts like: -console_scripts = - pyaleph = aleph.commands:run -# For example: -# console_scripts = -# fibonacci = pyaleph.skeleton:run -# And any other entry points, for example: -# pyscaffold.cli = -# awesome = pyscaffoldext.awesome.extension:AwesomeExtension - -[test] -# py.test options when running `python setup.py test` -# addopts = --verbose -extras = True - -[tool:pytest] -# Options for py.test: -# Specify command line options as you would do when invoking py.test directly. -# e.g. --cov-report html (or xml) for html/xml output or --junitxml junit.xml -# in order to write a coverage file that can be read by Jenkins. -# --cov aleph --cov-report term-missing -addopts = - --disable-pytest-warnings - --verbose -norecursedirs = - dist - build - .tox -testpaths = tests - -[aliases] -build = bdist_wheel -release = build upload - -[bdist_wheel] -# Use this option if your package is pure-python -universal = 1 - -[build_sphinx] -source_dir = docs -build_dir = docs/_build - -[devpi:upload] -# Options for the devpi: PyPI server and packaging tool -# VCS export must be deactivated since we are using setuptools-scm -no-vcs = 1 -formats = bdist_wheel - -[flake8] -# Some sane defaults for the code style checker flake8 -exclude = - .tox - build - dist - .eggs - docs/conf.py - -[pyscaffold] -# PyScaffold's parameters when the project was created. -# This will be used when updating. Do not change! -version = 3.1 -package = aleph -extensions = - markdown diff --git a/setup.py b/setup.py deleted file mode 100644 index d12b5ca22..000000000 --- a/setup.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" - Setup file for pyaleph. - Use setup.cfg to configure your project. - - This file was generated with PyScaffold 3.1. - PyScaffold helps you to put up the scaffold of your new Python project. - Learn more under: https://pyscaffold.org/ -""" -import sys - -from pkg_resources import require, VersionConflict -from setuptools import setup - -try: - require('setuptools>=38.3') -except VersionConflict: - print("Error: version of setuptools is too old (<38.3)!") - sys.exit(1) - - -if __name__ == "__main__": - setup(use_pyscaffold=True)