diff --git a/.github/actions/test/Dockerfile b/.github/actions/test/Dockerfile deleted file mode 100644 index 9870878..0000000 --- a/.github/actions/test/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM python:3.7-slim -RUN apt-get update \ - && apt-get install --yes --no-install-recommends git \ - && rm -fr /var/lib/apt/lists/* diff --git a/.github/main.workflow b/.github/main.workflow deleted file mode 100644 index 785d579..0000000 --- a/.github/main.workflow +++ /dev/null @@ -1,21 +0,0 @@ -workflow "Test and Publish" { - on = "push" - resolves = ["Release"] -} - -action "Test" { - uses = "./.github/actions/test/" - args = "python setup.py test" -} - -action "Filter release" { - needs = ["Test"] - uses = "actions/bin/filter@95c1a3b" - args = "tag v*" -} - -action "Release" { - uses = "docker://code0x58/action-python-publish:master" - needs = ["Filter release"] - secrets = ["TWINE_PASSWORD", "TWINE_USERNAME"] -} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..2b578a4 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,28 @@ +name: Publish + +on: + release: + types: [published] + +jobs: + build-and-publish: + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/modello + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Build package + run: | + python -m pip install --upgrade pip build + python -m build + # the publishing uses PyPI's trusted publishing, so configured on pypi instead of using secrets + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3acf1ae --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +jobs: + test: + runs-on: ubuntu-22.04 + strategy: + matrix: + python-version: ["3.7", "3.13", "3.14-dev"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --editable .[test] + - name: Run tests + run: | + pytest + - name: Upload coverage + uses: actions/upload-artifact@v4 + with: + name: coverage-${{ matrix.python-version }} + path: .coverage \ No newline at end of file diff --git a/README.md b/README.md index 1171e0a..043f656 100644 --- a/README.md +++ b/README.md @@ -40,15 +40,15 @@ pipenv install git+https://github.com/Code0x58/modello.git#egg=modello pip install --user git+https://github.com/Code0x58/modello.git#egg=modello ``` -Currently this requires Python 3.6+ but the version requirements can drop a couple of minor versions easily if there is interest. Python 2.7 isn't planned to be supported as the Modello class relies on [PEP-3115](https://www.python.org/dev/peps/pep-3115/). +Currently this requires Python 3.8+ but the version requirements can drop a couple of minor versions easily if there is interest. Python 2.7 isn't planned to be supported as the Modello class relies on [PEP-3115](https://www.python.org/dev/peps/pep-3115/). ## Development -Run the tests and linting with `python setup.py test`. Pushes have the test suite run against them, and will also publish a release if tagged thanks to GitHub Actions. You can reproduce the Actions locally using [act](https://github.com/nektos/act), e.g. `TWINE_USERNAME= TWINE_PASSWORD= act`. +Run the tests and linting with `pytest`. Pushes have the test suite run against them, and will also publish a release if tagged thanks to GitHub Actions. You can reproduce the Actions locally using [act](https://github.com/nektos/act), e.g. `TWINE_USERNAME= TWINE_PASSWORD= act`. -You can run all the tests with `python setup.py test`. If you want to run a subset of tests or otherwise pass arguments to pytest, use `./pytest …` e.g.: +If you want to run a subset of tests or otherwise pass arguments to pytest, just invoke `pytest` directly, e.g.: ```sh -./pytest examples/jobs.py::jobs.Job +pytest examples/jobs.py::jobs.Job ``` ## TODO: diff --git a/modello.py b/modello.py index 86a6b7c..8e77728 100644 --- a/modello.py +++ b/modello.py @@ -33,6 +33,7 @@ class ModelloMetaNamespace(dict): def __init__(self, name: str, bases: typing.Tuple[type, ...]) -> None: """Create a namespace for a Modello class to use.""" + super().__init__() self.name = name # map of attributes to sympy Basic (e.g expression, value) objects self.attrs: typing.Dict[str, Basic] = {} @@ -96,7 +97,7 @@ class ModelloMeta(type): @classmethod def __prepare__( metacls, __name: str, __bases: typing.Tuple[type, ...], **kwds: typing.Any - ) -> typing.Mapping[str, typing.Any]: + ) -> typing.MutableMapping[str, typing.Any]: """Return a ModelloMetaNamespace instead of a plain dict to accumlate attributes on.""" return ModelloMetaNamespace(__name, __bases) diff --git a/pyproject.toml b/pyproject.toml index 4ba7d43..333fa11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,80 @@ +[build-system] +requires = ["setuptools>=61", "setuptools_scm[toml]>=7"] +build-backend = "setuptools.build_meta" + +[project] +name = "modello" +description = "sympy expressions in models" +readme = "README.md" +requires-python = ">=3.7" +license = {text = "MIT"} +authors = [{name = "Oliver Bristow", email = "github+pypi@oliverbristow.co.uk"}] +dependencies = ["sympy"] +keywords = ["symbolic modeling"] +urls = {homepage = "https://github.com/Code0x58/modello/"} +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dynamic = ["version"] + +[project.optional-dependencies] +test = [ + "flake8-black", + "flake8-docstrings", + "flake8-isort", + "pytest-cov>=2.6.1", + "pytest-ruff", + "pytest-mypy", + "pytest>=4.1", +] + +[tool.setuptools] +py-modules = ["modello"] + +[tool.setuptools_scm] + +[tool.pytest.ini_options] +addopts = "--doctest-modules --doctest-report=udiff --cov=modello --cov-report=html --cov-report=term --mypy --ruff" +python_files = ["test_modello.py", "examples/*.py"] +cache_dir = "artefacts/reports/.pytest_cache" + +[tool.coverage.run] +data_file = "artefacts/reports/.coverage" +source = ["modello.py"] +branch = true + +[tool.coverage.html] +directory = "artefacts/reports/coverage-html" + +[tool.mypy] +ignore_missing_imports = true +cache_dir = "artefacts/reports/.mypy_cache" + +[tool.mypy-modello] +disallow_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true + +[tool.isort] +line_length = 120 +force_grid_wrap = 0 +use_parentheses = true +include_trailing_comma = true +combine_as_imports = true +multi_line_output = 5 + [tool.ruff] line-length = 120 -target-version = "py311" \ No newline at end of file +target-version = "py311" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 1b09eca..0000000 --- a/setup.cfg +++ /dev/null @@ -1,44 +0,0 @@ -[aliases] -test = pytest - -[tool:pytest] -addopts = - --doctest-modules --doctest-report=udiff - --cov=modello --cov-report=html --cov-report=term - --mypy - --ruff -python_files = - test_modello.py - examples/*.py -cache_dir = artefacts/reports/.pytest_cache - -[coverage:run] -data_file = artefacts/reports/.coverage -source = modello.py -branch = True - -[coverage:html] -directory = artefacts/reports/coverage-html - -[mypy] -ignore_missing_imports = True -cache_dir = artefacts/reports/.mypy_cache - -[mypy-modello] -disallow_untyped_defs = True -disallow_incomplete_defs = True -disallow_untyped_decorators = True - -[isort] -# Import Style -line_length = 120 -force_grid_wrap = false -use_parentheses = true -include_trailing_comma = true -combine_as_imports = true - -# Multiline Import Style: hanging grid grouped -multi_line_output = 5 - -[bdist_wheel] -python-tag=3 diff --git a/setup.py b/setup.py index 341ea54..0cdfa65 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,6 @@ "pytest-cov>=2.6.1", "pytest-ruff", "pytest-mypy", - "pytest-pudb", "pytest>=4.1", ] setup( @@ -33,7 +32,7 @@ tests_require=TEST_REQUIRES, extras_require={"test": TEST_REQUIRES}, py_modules=["modello"], - python_requires=">=3.3", + python_requires=">=3.7", license="MIT", classifiers=dedent( """ @@ -43,6 +42,13 @@ License :: OSI Approved :: MIT License Operating System :: OS Independent Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 """ ) .strip()