From c58e402ca2d1c84f9b2ad836ac7733fbb9204e2e Mon Sep 17 00:00:00 2001 From: Joseph Perez Date: Thu, 19 Oct 2023 01:32:51 +0200 Subject: [PATCH] build: use pyproject.toml (#598) * build: use pyproject.toml * fix: fix build * chore: pin build dependencies version and update dependabot * ci: do not use coverage for compiled test * ci: drop python 3.7 * fix: fix `apischema.typing.get_args` * fix: make flake8 happy with setup.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: fix scripts/generate_readme.py version retrieving * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/dependabot.yml | 7 ++ .github/workflows/cd.yml | 4 +- .github/workflows/ci.yml | 15 ++-- .github/workflows/doc.yml | 2 - apischema/typing.py | 2 +- pyproject.toml | 45 ++++++++++ scripts/cythonize.sh | 3 - scripts/generate_readme.py | 10 ++- scripts/requirements.cython.txt | 2 - setup.py | 146 ++++---------------------------- 10 files changed, 85 insertions(+), 151 deletions(-) create mode 100644 pyproject.toml delete mode 100755 scripts/cythonize.sh delete mode 100644 scripts/requirements.cython.txt diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 42a22af2..19c246fe 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,6 +7,13 @@ updates: groups: actions: patterns: ["*"] + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "monthly" + groups: + build: + patterns: ["*"] - package-ecosystem: "pip" directory: "tests" schedule: diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 5ab9cbb9..0eddfbb7 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -10,7 +10,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - run: scripts/cythonize.sh - run: pipx run build --sdist - uses: actions/upload-artifact@v3 with: @@ -20,12 +19,10 @@ jobs: name: Wheel on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v3 - - run: scripts/cythonize.sh - run: scripts/generate_tests_from_examples.py - uses: pypa/cibuildwheel@v2.16.2 env: @@ -34,6 +31,7 @@ jobs: # TODO execute tests on Windows (https://github.com/wyfo/apischema/runs/4622330189) CIBW_TEST_COMMAND_WINDOWS: python -c "import apischema" CIBW_BEFORE_TEST: pip install -r tests/requirements.txt + # TODO is skipping still necessary? CIBW_TEST_SKIP: "*universal2:arm64" - uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff7ab66a..f11f42b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,9 +24,9 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.9'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.9', 'pypy-3.10'] include: - - python-version: '3.11' + - python-version: '3.12' pytest-args: --cov=apischema --cov-branch --cov-report=xml --cov-report=html steps: - uses: actions/cache@v3.0.11 @@ -56,15 +56,12 @@ jobs: path: | coverage.xml htmlcov - - name: Cythonize - run: scripts/cythonize.sh - if: matrix.python-version != 'pypy-3.9' - name: Compile - run: python setup.py build_ext --inplace - if: matrix.python-version != 'pypy-3.9' + run: pip install -e . + if: matrix.python-version != 'pypy-3.9' && matrix.python-version != 'pypy-3.10' - name: Run tests (compiled) - run: pytest tests ${{ matrix.pytest-args }} - if: matrix.python-version != 'pypy-3.9' + run: pytest tests + if: matrix.python-version != 'pypy-3.9' && matrix.python-version != 'pypy-3.10' concurrency: group: ci-${{ github.head_ref }} diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index e3241fdc..fb6b411e 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -36,8 +36,6 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.11' - - name: Cythonize - run: scripts/cythonize.sh - name: Install apischema run: pip install -e . - name: Install requirements diff --git a/apischema/typing.py b/apischema/typing.py index 77ccfd5b..e399361d 100644 --- a/apischema/typing.py +++ b/apischema/typing.py @@ -51,7 +51,7 @@ def get_origin(tp): def get_args(tp): if isinstance(tp, _AnnotatedAlias): return () if tp.__args__ is None else (tp.__args__[0], *tp.__metadata__) - res = tp.__args__ + res = getattr(tp, "__args__", ()) if get_origin(tp) is Callable and res[0] is not Ellipsis: res = (list(res[:-1]), res[-1]) return res diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f1fbaf76 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,45 @@ +[build-system] +requires = ["setuptools==68.2.2", "wheel==0.41.2", "Cython==3.0.3"] +build-backend = "setuptools.build_meta" + +[project] +name = "apischema" +version = "0.18.1" +authors = [{ name = "Joseph Perez", email = "joperez@hotmail.fr" }] +license = { text = "MIT" } +description = "JSON (de)serialization, GraphQL and JSON schema generation using Python typing." +readme = "README.md" +requires-python = ">=3.7" +classifiers = [ + "Development Status == 4 - Beta", + "Intended Audience == Developers", + "License == OSI Approved == MIT License", + "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", + "Topic == Software Development == Libraries == Python Modules", +] + +[project.urls] +Repository = "https://github.com/wyfo/apischema" +Documentation = "https://wyfo.github.io/apischema" + +[project.optional-dependencies] +graphql = ["graphql-core>=3.0.0"] +examples = [ + "graphql-core>=3.0.0", + "attrs", + "docstring_parser", + "bson", + "orjson", + "pydantic", + "pytest", + "sqlalchemy", +] + +[tool.setuptools.package-data] +apischema = ["py.typed"] +scripts = ["cythonize.py"] diff --git a/scripts/cythonize.sh b/scripts/cythonize.sh deleted file mode 100755 index 15b99fef..00000000 --- a/scripts/cythonize.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -python3 -m pip install -r $(dirname $0)/requirements.cython.txt -$(dirname $0)/cythonize.py \ No newline at end of file diff --git a/scripts/generate_readme.py b/scripts/generate_readme.py index 61146b20..b79efe9f 100755 --- a/scripts/generate_readme.py +++ b/scripts/generate_readme.py @@ -6,14 +6,16 @@ ROOT_DIR = pathlib.Path(__file__).parent.parent README = ROOT_DIR / "README.md" INDEX = ROOT_DIR / "docs" / "index.md" -SETUP = ROOT_DIR / "setup.py" +PYPROJECT = ROOT_DIR / "pyproject.toml" QUICKSTART = ROOT_DIR / "examples" / "quickstart.py" -USED_FILES = {str(path.relative_to(ROOT_DIR)) for path in (INDEX, SETUP, QUICKSTART)} +USED_FILES = { + str(path.relative_to(ROOT_DIR)) for path in (INDEX, PYPROJECT, QUICKSTART) +} def main(): - version_match = re.search(r"version=\"(\d+\.\d+)", SETUP.read_text()) + version_match = re.search(r"version = \"(\d+\.\d+)", PYPROJECT.read_text()) assert version_match is not None version = version_match.group(1) content = INDEX.read_text() @@ -24,6 +26,7 @@ def main(): # Remove admonitions content = re.sub(r"!!! note\n\s*(.*)\n", lambda m: f"> {m.group(1)}\n", content) # Add chart + # TODO remove this unused part? content = content.replace( r"", "\n".join( @@ -34,6 +37,7 @@ def main(): ) # Uncomment content = re.sub(r"", lambda m: m.group(1), content) + # TODO remove this unused part? content = re.sub( r"(\d+\.\d+)/benchmark_chart\.svg", f"{version}/benchmark_chart.svg", content ) diff --git a/scripts/requirements.cython.txt b/scripts/requirements.cython.txt deleted file mode 100644 index 6b01b69f..00000000 --- a/scripts/requirements.cython.txt +++ /dev/null @@ -1,2 +0,0 @@ -Cython==3.0.3 -setuptools>=68.2; python_version>="3.12" diff --git a/setup.py b/setup.py index 94b6d59b..8e52ae4b 100644 --- a/setup.py +++ b/setup.py @@ -1,131 +1,21 @@ -import pathlib +import importlib +import os import platform import sys -import warnings -# The following code is copied from -# https://github.com/tornadoweb/tornado/blob/master/setup.py -# to support installing without the extension on platforms where -# no compiler is available. -from distutils.command.build_ext import build_ext - -from setuptools import Extension, find_packages, setup - - -class custom_build_ext(build_ext): - """Allow C extension building to fail. - The C extension speeds up (de)serialization, but is not essential. - """ - - warning_message = """ -******************************************************************** -WARNING: %s could not -be compiled. No C extensions are essential for apischema to run, -although they do result in significant speed improvements for -(de)serialization. -%s -Here are some hints for popular operating systems: -If you are seeing this message on Linux you probably need to -install GCC and/or the Python development package for your -version of Python. -Debian and Ubuntu users should issue the following command: - $ sudo apt-get install build-essential python-dev -RedHat and CentOS users should issue the following command: - $ sudo yum install gcc python-devel -Fedora users should issue the following command: - $ sudo dnf install gcc python-devel -MacOS users should run: - $ xcode-select --install -******************************************************************** -""" - - def run(self): - try: - build_ext.run(self) - except Exception: - e = sys.exc_info()[1] - sys.stdout.write("%s\n" % str(e)) - warnings.warn( - self.warning_message - % ( - "Extension modules", - "There was an issue with " - "your platform configuration" - " - see above.", - ) - ) - - def build_extension(self, ext): - name = ext.name - try: - build_ext.build_extension(self, ext) - except Exception: - e = sys.exc_info()[1] - sys.stdout.write("%s\n" % str(e)) - warnings.warn( - self.warning_message - % ( - "The %s extension " "module" % (name,), - "The output above " - "this warning shows how " - "the compilation " - "failed.", - ) - ) - - -ext_modules = None -# Cythonization makes apischema a lot slower using PyPy -if platform.python_implementation() != "PyPy": - ext_modules = [ - Extension( - f"apischema.{package}.methods", sources=[f"apischema/{package}/methods.c"] - ) - for package in ("deserialization", "serialization") - ] - -setup( - name="apischema", - version="0.18.1", - url="https://github.com/wyfo/apischema", - author="Joseph Perez", - author_email="joperez@hotmail.fr", - license="MIT", - packages=find_packages(include=["apischema*"]), - package_data={ - "apischema": ["py.typed"], - "apischema.deserialization": ["methods.pyx"], - "apischema.serialization": ["methods.pyx"], - }, - description="JSON (de)serialization, GraphQL and JSON schema generation using Python typing.", - long_description=pathlib.Path("README.md").read_text(), - long_description_content_type="text/markdown", - python_requires=">=3.7", - extras_require={ - "graphql": ["graphql-core>=3.0.0"], - "examples": [ - "graphql-core>=3.0.0", - "attrs", - "docstring_parser", - "bson", - "orjson", - "pydantic", - "pytest", - "sqlalchemy", - ], - }, - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "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", - "Topic :: Software Development :: Libraries :: Python Modules", - ], - cmdclass={"build_ext": custom_build_ext}, - ext_modules=ext_modules, -) +from setuptools import Extension, setup + +sys.path.append(os.path.dirname(__file__)) +importlib.import_module("scripts.cythonize").main() + +ext_modules = [ + Extension( + f"apischema.{package}.methods", + sources=[f"apischema/{package}/methods.c"], + optional=True, + ) + for package in ("deserialization", "serialization") + # Cythonization makes apischema slower using PyPy + if platform.python_implementation() != "PyPy" +] +setup(ext_modules=ext_modules)