From 1a7c096f8636dde46e35a9f6468954dcbf7b2742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Behmo?= Date: Fri, 22 Nov 2024 10:29:18 +0100 Subject: [PATCH] feat: migrate from setup.py to pyproject.toml This is an attempt both to modernize the packaging and to fix mypy testing in editable mode (issue #956). Packaging seems to work, but mypy testing still fails. Will now attempt to migrate from setuptools to hatch for building. --- .github/workflows/release.yml | 2 +- Makefile | 20 +++---- README.rst | 2 +- pyproject.toml | 49 ++++++++++++++++++ requirements/base.txt | 32 +++++------- requirements/docs.txt | 98 +++++++++++------------------------ setup.py | 78 ---------------------------- 7 files changed, 101 insertions(+), 180 deletions(-) delete mode 100644 setup.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c7069dfb22..8b980b178a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,7 @@ jobs: - name: Print info about the current python installation run: make ci-info - name: Install requirements - run: make bootstrap-dev-plugins + run: make bootstrap-dev ##### Run tests, generate bundle # - name: Run tests diff --git a/Makefile b/Makefile index b1755a5dc0..417af4bdbd 100644 --- a/Makefile +++ b/Makefile @@ -9,19 +9,17 @@ docs: ## Build HTML documentation $(MAKE) -C docs compile-requirements: ## Compile requirements files - pip-compile requirements/base.in - pip-compile requirements/dev.in - pip-compile requirements/docs.in + pip-compile ${COMPILE_OPTS} --output-file=requirements/base.txt + pip-compile ${COMPILE_OPTS} requirements/dev.in + pip-compile ${COMPILE_OPTS} --extra=docs --output-file=requirements/docs.txt upgrade-requirements: ## Upgrade requirements files - pip-compile --upgrade requirements/base.in - pip-compile --upgrade requirements/dev.in - pip-compile --upgrade requirements/docs.in + $(MAKE) compile-requirements COMPILE_OPTS="--upgrade" build-pythonpackage: build-pythonpackage-tutor ## Build Python packages ready to upload to pypi build-pythonpackage-tutor: ## Build the "tutor" python package for upload to pypi - python setup.py sdist + python -m build --sdist push-pythonpackage: ## Push python package to pypi twine upload --skip-existing dist/tutor-$(shell make version).tar.gz @@ -82,12 +80,8 @@ coverage-browse-report: coverage-html ## Open the HTML report in the browser bundle: ## Bundle the tutor package in a single "dist/tutor" executable pyinstaller tutor.spec -bootstrap-dev: ## Install dev requirements - pip install . - pip install -r requirements/dev.txt - -bootstrap-dev-plugins: bootstrap-dev ## Install dev requirements and all supported plugins - pip install -r requirements/plugins.txt +bootstrap-dev: ## Install dev requirements and all supported plugins + pip install .[full,dev] pull-base-images: # Manually pull base images docker image pull docker.io/ubuntu:20.04 diff --git a/README.rst b/README.rst index fc0d3b2504..28e112df54 100644 --- a/README.rst +++ b/README.rst @@ -51,7 +51,7 @@ Features .. _readme_intro_end: -.. image:: ./docs/img/launch.webp +.. image:: https://github.com/overhangio/tutor/raw/master/docs/img/launch.webp :alt: Tutor local launch :target: https://www.terminalizer.com/view/3a8d55835686 diff --git a/pyproject.toml b/pyproject.toml index d1e6ae6e56..e4863ed32b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,51 @@ +# https://packaging.python.org/en/latest/tutorials/packaging-projects/ +# https://setuptools.pypa.io/en/latest/userguide/quickstart.html + +[project] +dynamic = ["version", "dependencies", "optional-dependencies"] +name = "tutor" +license = {file = "LICENSE.txt"} +authors = [ + {name = "Edly", email = "hello@edly.io"}, +] +description = "The Docker-based Open edX distribution designed for peace of mind" +readme = {file = "README.rst", content-type = "text/x-rst"} +requires-python = ">= 3.9" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Affero General Public License v3", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] + +[project.scripts] +tutor = "tutor.commands.cli:main" + +# https://packaging.python.org/en/latest/specifications/well-known-project-urls/#well-known-labels +[project.urls] +Homepage = "https://docs.tutor.edly.io/" +Documentation = "https://docs.tutor.edly.io/" +Source = "https://github.com/overhangio/tutor" +Issues = "https://github.com/overhangio/tutor/issues" +Changelog = "https://github.com/overhangio/tutor/blob/master/CHANGELOG.md" +Community = "https://discuss.openedx.org/tag/tutor" + +# Setuptools-specific configuration [build-system] requires = ["setuptools", "wheel"] + +[tool.setuptools.dynamic] +version = {attr = "tutor.__about__.__package_version__"} +dependencies = {file = ["requirements/base.in"] } + +[tool.setuptools.dynamic.optional-dependencies] +dev = {file = ["requirements/dev.txt"]} +full = {file = ["requirements/plugins.txt"]} + +[tool.setuptools.packages.find] +exclude = ["tests*"] diff --git a/requirements/base.txt b/requirements/base.txt index f748abba40..1dfb795ab4 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,11 +1,11 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.12 # by the following command: # -# pip-compile requirements/base.in +# pip-compile --output-file=requirements/base.txt # appdirs==1.4.4 - # via -r requirements/base.in + # via tutor (pyproject.toml) cachetools==5.5.0 # via google-auth certifi==2024.8.30 @@ -15,7 +15,7 @@ certifi==2024.8.30 charset-normalizer==3.4.0 # via requests click==8.1.7 - # via -r requirements/base.in + # via tutor (pyproject.toml) durationpy==0.9 # via kubernetes google-auth==2.35.0 @@ -23,17 +23,17 @@ google-auth==2.35.0 idna==3.10 # via requests importlib-metadata==8.5.0 - # via -r requirements/base.in + # via tutor (pyproject.toml) importlib-resources==6.4.5 - # via -r requirements/base.in + # via tutor (pyproject.toml) jinja2==3.1.4 - # via -r requirements/base.in + # via tutor (pyproject.toml) kubernetes==31.0.0 - # via -r requirements/base.in + # via tutor (pyproject.toml) markupsafe==3.0.2 # via jinja2 mypy==1.13.0 - # via -r requirements/base.in + # via tutor (pyproject.toml) mypy-extensions==1.0.0 # via mypy oauthlib==3.2.2 @@ -41,7 +41,7 @@ oauthlib==3.2.2 # kubernetes # requests-oauthlib packaging==24.1 - # via -r requirements/base.in + # via tutor (pyproject.toml) pyasn1==0.6.1 # via # pyasn1-modules @@ -49,13 +49,13 @@ pyasn1==0.6.1 pyasn1-modules==0.4.1 # via google-auth pycryptodome==3.21.0 - # via -r requirements/base.in + # via tutor (pyproject.toml) python-dateutil==2.9.0.post0 # via kubernetes pyyaml==6.0.2 # via - # -r requirements/base.in # kubernetes + # tutor (pyproject.toml) requests==2.32.3 # via # kubernetes @@ -68,12 +68,10 @@ six==1.16.0 # via # kubernetes # python-dateutil -tomli==2.0.2 - # via mypy typing-extensions==4.12.2 # via - # -r requirements/base.in # mypy + # tutor (pyproject.toml) urllib3==2.2.3 # via # kubernetes @@ -81,6 +79,4 @@ urllib3==2.2.3 websocket-client==1.8.0 # via kubernetes zipp==3.20.2 - # via - # importlib-metadata - # importlib-resources + # via importlib-metadata diff --git a/requirements/docs.txt b/requirements/docs.txt index 055c683a98..7c137fa92b 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -1,134 +1,105 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.12 # by the following command: # -# pip-compile requirements/docs.in +# pip-compile --extra=docs --output-file=requirements/docs.txt # alabaster==0.7.16 # via sphinx appdirs==1.4.4 - # via -r requirements/base.txt + # via tutor (pyproject.toml) babel==2.16.0 # via sphinx cachetools==5.5.0 - # via - # -r requirements/base.txt - # google-auth + # via google-auth certifi==2024.8.30 # via - # -r requirements/base.txt # kubernetes # requests charset-normalizer==3.4.0 - # via - # -r requirements/base.txt - # requests + # via requests click==8.1.7 # via - # -r requirements/base.txt # sphinx-click + # tutor (pyproject.toml) docutils==0.21.2 # via # sphinx # sphinx-click # sphinx-rtd-theme durationpy==0.9 - # via - # -r requirements/base.txt - # kubernetes + # via kubernetes google-auth==2.35.0 - # via - # -r requirements/base.txt - # kubernetes + # via kubernetes idna==3.10 - # via - # -r requirements/base.txt - # requests + # via requests imagesize==1.4.1 # via sphinx importlib-metadata==8.5.0 - # via - # -r requirements/base.txt - # sphinx + # via tutor (pyproject.toml) importlib-resources==6.4.5 - # via -r requirements/base.txt + # via tutor (pyproject.toml) jinja2==3.1.4 # via - # -r requirements/base.txt # sphinx + # tutor (pyproject.toml) kubernetes==31.0.0 - # via -r requirements/base.txt + # via tutor (pyproject.toml) markupsafe==3.0.2 - # via - # -r requirements/base.txt - # jinja2 + # via jinja2 mypy==1.13.0 - # via -r requirements/base.txt + # via tutor (pyproject.toml) mypy-extensions==1.0.0 - # via - # -r requirements/base.txt - # mypy + # via mypy oauthlib==3.2.2 # via - # -r requirements/base.txt # kubernetes # requests-oauthlib packaging==24.1 # via - # -r requirements/base.txt # sphinx + # tutor (pyproject.toml) pyasn1==0.6.1 # via - # -r requirements/base.txt # pyasn1-modules # rsa pyasn1-modules==0.4.1 - # via - # -r requirements/base.txt - # google-auth + # via google-auth pycryptodome==3.21.0 - # via -r requirements/base.txt + # via tutor (pyproject.toml) pygments==2.18.0 # via sphinx python-dateutil==2.9.0.post0 - # via - # -r requirements/base.txt - # kubernetes + # via kubernetes pyyaml==6.0.2 # via - # -r requirements/base.txt # kubernetes + # tutor (pyproject.toml) requests==2.32.3 # via - # -r requirements/base.txt # kubernetes # requests-oauthlib # sphinx requests-oauthlib==2.0.0 - # via - # -r requirements/base.txt - # kubernetes + # via kubernetes rsa==4.9 - # via - # -r requirements/base.txt - # google-auth + # via google-auth six==1.16.0 # via - # -r requirements/base.txt # kubernetes # python-dateutil snowballstemmer==2.2.0 # via sphinx sphinx==7.4.7 # via - # -r requirements/docs.in # sphinx-click # sphinx-rtd-theme # sphinxcontrib-jquery + # tutor (pyproject.toml) sphinx-click==6.0.0 - # via -r requirements/docs.in + # via tutor (pyproject.toml) sphinx-rtd-theme==3.0.1 - # via -r requirements/docs.in + # via tutor (pyproject.toml) sphinxcontrib-applehelp==2.0.0 # via sphinx sphinxcontrib-devhelp==2.0.0 @@ -143,26 +114,15 @@ sphinxcontrib-qthelp==2.0.0 # via sphinx sphinxcontrib-serializinghtml==2.0.0 # via sphinx -tomli==2.0.2 - # via - # -r requirements/base.txt - # mypy - # sphinx typing-extensions==4.12.2 # via - # -r requirements/base.txt # mypy + # tutor (pyproject.toml) urllib3==2.2.3 # via - # -r requirements/base.txt # kubernetes # requests websocket-client==1.8.0 - # via - # -r requirements/base.txt - # kubernetes + # via kubernetes zipp==3.20.2 - # via - # -r requirements/base.txt - # importlib-metadata - # importlib-resources + # via importlib-metadata diff --git a/setup.py b/setup.py deleted file mode 100644 index 50a182bf59..0000000000 --- a/setup.py +++ /dev/null @@ -1,78 +0,0 @@ -import io -import os -from typing import Dict, List - -from setuptools import find_packages, setup - -HERE = os.path.abspath(os.path.dirname(__file__)) - - -def load_readme() -> str: - with io.open(os.path.join(HERE, "README.rst"), "rt", encoding="utf8") as f: - readme = f.read() - # Replace img src for publication on pypi - return readme.replace( - "./docs/img/", "https://github.com/overhangio/tutor/raw/master/docs/img/" - ) - - -def load_about() -> Dict[str, str]: - about: Dict[str, str] = {} - with io.open( - os.path.join(HERE, "tutor", "__about__.py"), "rt", encoding="utf-8" - ) as f: - exec(f.read(), about) # pylint: disable=exec-used - return about - - -def load_requirements(filename: str) -> List[str]: - with io.open( - os.path.join(HERE, "requirements", filename), "rt", encoding="utf-8" - ) as f: - return [line.strip() for line in f if is_requirement(line)] - - -def is_requirement(line: str) -> bool: - return not (line.strip() == "" or line.startswith("#")) - - -ABOUT = load_about() - -setup( - name="tutor", - version=ABOUT["__package_version__"], - url="https://docs.tutor.edly.io/", - project_urls={ - "Documentation": "https://docs.tutor.edly.io/", - "Code": "https://github.com/overhangio/tutor", - "Issue tracker": "https://github.com/overhangio/tutor/issues", - "Community": "https://discuss.openedx.org/tag/tutor", - }, - license="AGPLv3", - author="Edly", - author_email="hello@edly.io", - description="The Docker-based Open edX distribution designed for peace of mind", - long_description=load_readme(), - long_description_content_type="text/x-rst", - packages=find_packages(exclude=["tests*"]), - include_package_data=True, - python_requires=">=3.9", - install_requires=load_requirements("base.in"), - extras_require={ - "dev": load_requirements("dev.txt"), - "full": load_requirements("plugins.txt"), - }, - entry_points={"console_scripts": ["tutor=tutor.commands.cli:main"]}, - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: GNU Affero General Public License v3", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - ], - test_suite="tests", -)