diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d17c9e85456..b4c78d1340d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -242,7 +242,7 @@ jobs: # The following command checks that all modules can be imported. # The output also includes a long list of modules together with the number of tests in each module. # This can be ignored. - ./sage -python -m pip install pytest-xdist + ./sage -python -m pip install -r requirements/test-requirements.txt ./sage -python -m pytest -c tox.ini -qq --doctest --collect-only || true shell: sh .ci/docker-exec-script.sh BUILD /sage {0} @@ -453,7 +453,7 @@ jobs: rm -rf /sage/.coverage ln -s $(pwd)/.coverage /sage/ cd /sage - ./sage -python -m pip install coverage + ./sage -python -m pip install -r requirements/test-requirements.txt ./sage -python -m coverage run --rcfile=src/tox.ini src/bin/sage-runtests --force-lib --long -p4 --format github --random-seed=286735480429121101562228604801325644303 ${{ matrix.tests }} shell: sh .ci/docker-exec-script.sh BUILD . {0} @@ -580,7 +580,7 @@ jobs: rm -rf /sage/.coverage ln -s $(pwd)/.coverage /sage/ cd /sage - ./sage -python -m pip install coverage + ./sage -python -m pip install -r requirements/test-requirements.txt ./sage -python -m coverage combine --rcfile=src/tox.ini .coverage/coverage-*/.coverage ./sage -python -m coverage xml --rcfile=src/tox.ini --omit="/tmp/*" mkdir -p .coverage/coverage-report diff --git a/pyproject.toml b/pyproject.toml index 47c125c4e26..ba26126d0fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,8 +90,8 @@ platforms = [ 'osx-64', 'linux-64', 'linux-aarch64', 'osx-arm64' ] -[external] # External dependencies in the format proposed by https://peps.python.org/pep-0725 +[external] build-requires = [ "virtual:compiler/c", "virtual:compiler/cpp", @@ -152,4 +152,22 @@ dependencies = [ "pkg:generic/tachyon", "pkg:generic/sagemath-polytopes-db", "pkg:generic/sagemath-elliptic-curves", + "pkg:generic/sagemath-graphs", +] + +[dependency-groups] +test = [ + "pytest", + "pytest-xdist", + "coverage", +] +docs = [ + "sphinx", + "sphinx-inline-tabs", + "furo", +] +lint = [ + "relint", + "pycodestyle", + "flake8-rst-docstrings", ] diff --git a/requirements/ .gitignore b/requirements/ .gitignore new file mode 100644 index 00000000000..14140f47af8 --- /dev/null +++ b/requirements/ .gitignore @@ -0,0 +1 @@ +*.in diff --git a/requirements/docs-requirements.txt b/requirements/docs-requirements.txt new file mode 100644 index 00000000000..87d3943bf83 --- /dev/null +++ b/requirements/docs-requirements.txt @@ -0,0 +1,64 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --no-build-isolation --output-file=requirements/docs-requirements.txt requirements/docs.in +# +alabaster==0.7.16 + # via sphinx +babel==2.14.0 + # via sphinx +beautifulsoup4==4.12.3 + # via furo +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +docutils==0.20.1 + # via sphinx +furo==2024.1.29 + # via -r requirements/docs.in +idna==3.6 + # via requests +imagesize==1.4.1 + # via sphinx +jinja2==3.1.3 + # via sphinx +markupsafe==2.1.5 + # via jinja2 +packaging==23.2 + # via sphinx +pygments==2.17.2 + # via + # furo + # sphinx +requests==2.31.0 + # via sphinx +snowballstemmer==2.2.0 + # via sphinx +soupsieve==2.5 + # via beautifulsoup4 +sphinx==7.2.6 + # via + # -r requirements/docs.in + # furo + # sphinx-basic-ng + # sphinx-inline-tabs +sphinx-basic-ng==1.0.0b2 + # via furo +sphinx-inline-tabs==2023.4.21 + # via -r requirements/docs.in +sphinxcontrib-applehelp==1.0.8 + # via sphinx +sphinxcontrib-devhelp==1.0.6 + # via sphinx +sphinxcontrib-htmlhelp==2.0.5 + # via sphinx +sphinxcontrib-jsmath==1.0.1 + # via sphinx +sphinxcontrib-qthelp==1.0.7 + # via sphinx +sphinxcontrib-serializinghtml==1.1.10 + # via sphinx +urllib3==2.2.1 + # via requests diff --git a/requirements/lint-requirements.txt b/requirements/lint-requirements.txt new file mode 100644 index 00000000000..0ebe096b21c --- /dev/null +++ b/requirements/lint-requirements.txt @@ -0,0 +1,36 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --no-build-isolation --output-file=requirements/lint-requirements.txt requirements/lint.in +# +docutils==0.20.1 + # via restructuredtext-lint +flake8==7.0.0 + # via flake8-rst-docstrings +flake8-rst-docstrings==0.3.0 + # via -r requirements/lint.in +markdown-it-py==3.0.0 + # via rich +mccabe==0.7.0 + # via flake8 +mdurl==0.1.2 + # via markdown-it-py +pycodestyle==2.11.1 + # via + # -r requirements/lint.in + # flake8 +pyflakes==3.2.0 + # via flake8 +pygments==2.17.2 + # via + # flake8-rst-docstrings + # rich +pyyaml==6.0.1 + # via relint +relint==3.1.0 + # via -r requirements/lint.in +restructuredtext-lint==1.4.0 + # via flake8-rst-docstrings +rich==13.7.0 + # via relint diff --git a/requirements/requirements.txt b/requirements/requirements.txt new file mode 100644 index 00000000000..c02c95169cc --- /dev/null +++ b/requirements/requirements.txt @@ -0,0 +1,211 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --no-build-isolation --output-file=requirements/requirements.txt pyproject.toml +# +alabaster==0.7.16 + # via sphinx +asttokens==2.4.1 + # via stack-data +babel==2.14.0 + # via sphinx +certifi==2024.2.2 + # via requests +charset-normalizer==3.3.2 + # via requests +comm==0.2.1 + # via + # ipykernel + # ipywidgets +contourpy==1.2.0 + # via matplotlib +conway-polynomials==0.9 + # via sagemath-standard (pyproject.toml) +cycler==0.12.1 + # via matplotlib +cypari2==2.1.4 + # via sagemath-standard (pyproject.toml) +cysignals==1.11.4 + # via + # cypari2 + # primecountpy + # sagemath-standard (pyproject.toml) +cython==3.0.8 + # via + # memory-allocator + # primecountpy + # sagemath-standard (pyproject.toml) +debugpy==1.8.1 + # via ipykernel +decorator==5.1.1 + # via ipython +docutils==0.20.1 + # via sphinx +executing==2.0.1 + # via stack-data +fonttools==4.49.0 + # via matplotlib +fpylll==0.6.1 + # via sagemath-standard (pyproject.toml) +gmpy2==2.1.5 + # via sagemath-standard (pyproject.toml) +idna==3.6 + # via requests +imagesize==1.4.1 + # via sphinx +importlib-metadata==7.0.1 + # via sagemath-standard (pyproject.toml) +importlib-resources==6.1.1 + # via sagemath-standard (pyproject.toml) +ipykernel==6.29.2 + # via sagemath-standard (pyproject.toml) +ipython==8.22.1 + # via + # ipykernel + # ipywidgets + # sagemath-standard (pyproject.toml) +ipywidgets==8.1.2 + # via sagemath-standard (pyproject.toml) +jedi==0.19.1 + # via ipython +jinja2==3.1.3 + # via sphinx +jupyter-client==8.6.0 + # via + # ipykernel + # sagemath-standard (pyproject.toml) +jupyter-core==5.7.1 + # via + # ipykernel + # jupyter-client + # sagemath-standard (pyproject.toml) +jupyterlab-widgets==3.0.10 + # via ipywidgets +kiwisolver==1.4.5 + # via matplotlib +lrcalc==2.1 + # via sagemath-standard (pyproject.toml) +markupsafe==2.1.5 + # via jinja2 +matplotlib==3.8.3 + # via sagemath-standard (pyproject.toml) +matplotlib-inline==0.1.6 + # via + # ipykernel + # ipython +memory-allocator==0.1.3 + # via sagemath-standard (pyproject.toml) +mpmath==1.3.0 + # via + # sagemath-standard (pyproject.toml) + # sympy +nest-asyncio==1.6.0 + # via ipykernel +networkx==3.2.1 + # via sagemath-standard (pyproject.toml) +numpy==1.26.4 + # via + # contourpy + # matplotlib + # sagemath-standard (pyproject.toml) + # scipy +packaging==23.2 + # via + # ipykernel + # matplotlib + # sphinx +parso==0.8.3 + # via jedi +pexpect==4.9.0 + # via + # ipython + # sagemath-standard (pyproject.toml) +pillow==10.2.0 + # via + # matplotlib + # sagemath-standard (pyproject.toml) +platformdirs==4.2.0 + # via jupyter-core +pplpy==0.8.9 + # via sagemath-standard (pyproject.toml) +primecountpy==0.1.0 + # via sagemath-standard (pyproject.toml) +prompt-toolkit==3.0.43 + # via ipython +psutil==5.9.8 + # via ipykernel +ptyprocess==0.7.0 + # via + # pexpect + # sagemath-standard (pyproject.toml) +pure-eval==0.2.2 + # via stack-data +pygments==2.17.2 + # via + # ipython + # sphinx +pyparsing==3.1.1 + # via matplotlib +python-dateutil==2.8.2 + # via + # jupyter-client + # matplotlib +pyzmq==25.1.2 + # via + # ipykernel + # jupyter-client +requests==2.31.0 + # via + # sagemath-standard (pyproject.toml) + # sphinx +scipy==1.11.4 + # via sagemath-standard (pyproject.toml) +six==1.16.0 + # via + # asttokens + # python-dateutil + # sagemath-standard (pyproject.toml) +snowballstemmer==2.2.0 + # via sphinx +sphinx==7.2.6 + # via sagemath-standard (pyproject.toml) +sphinxcontrib-applehelp==1.0.8 + # via sphinx +sphinxcontrib-devhelp==1.0.6 + # via sphinx +sphinxcontrib-htmlhelp==2.0.5 + # via sphinx +sphinxcontrib-jsmath==1.0.1 + # via sphinx +sphinxcontrib-qthelp==1.0.7 + # via sphinx +sphinxcontrib-serializinghtml==1.1.10 + # via sphinx +stack-data==0.6.3 + # via ipython +sympy==1.12 + # via sagemath-standard (pyproject.toml) +tornado==6.4 + # via + # ipykernel + # jupyter-client +traitlets==5.14.1 + # via + # comm + # ipykernel + # ipython + # ipywidgets + # jupyter-client + # jupyter-core + # matplotlib-inline +typing-extensions==4.9.0 + # via sagemath-standard (pyproject.toml) +urllib3==2.2.1 + # via requests +wcwidth==0.2.13 + # via prompt-toolkit +widgetsnbextension==4.0.10 + # via ipywidgets +zipp==3.17.0 + # via importlib-metadata diff --git a/requirements/test-requirements.txt b/requirements/test-requirements.txt new file mode 100644 index 00000000000..e9d2fda23fe --- /dev/null +++ b/requirements/test-requirements.txt @@ -0,0 +1,22 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --no-build-isolation --output-file=requirements/test-requirements.txt requirements/test.in +# +coverage==7.4.3 + # via -r requirements/test.in +execnet==2.0.2 + # via pytest-xdist +iniconfig==2.0.0 + # via pytest +packaging==23.2 + # via pytest +pluggy==1.4.0 + # via pytest +pytest==8.0.2 + # via + # -r requirements/test.in + # pytest-xdist +pytest-xdist==3.5.0 + # via -r requirements/test.in diff --git a/src/tox.ini b/src/tox.ini index b6548bc55a4..449b1c1b576 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -179,7 +179,7 @@ description = # W391: blank line at end of file # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes -deps = pycodestyle +deps = -r{toxinidir}/../requirements/lint-requirements.txt commands = pycodestyle --select E111,E21,E221,E222,E225,E227,E228,E25,E271,E275,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/} pycodestyle --select E111,E271,E301,E302,E303,E305,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/} @@ -192,7 +192,7 @@ description = check whether some forbidden patterns appear # https://github.com/codingjoe/relint # The patterns are in .relint.yml -deps = relint +deps = -r{toxinidir}/../requirements/lint-requirements.txt allowlist_externals = find commands = find {posargs:{toxinidir}/sage/} \ -name "*\#*" -prune -o \ @@ -248,7 +248,7 @@ commands = codespell \ [testenv:rst] description = validate Python docstrings markup as reStructuredText -deps = flake8-rst-docstrings +deps = -r{toxinidir}/../requirements/lint-requirements.txt commands = flake8 --select=RST {posargs:{toxinidir}/sage/} [testenv:cython-lint] diff --git a/tools/README.md b/tools/README.md index b0c2e4bb68b..d4be49af02a 100644 --- a/tools/README.md +++ b/tools/README.md @@ -6,8 +6,18 @@ This folder contains various command-line tools that are used to facilitate diff This command is used to updates the Meson build files in the project. It automatically adds new source files (py, pyx) to the Meson files and removes deleted source files. This command is useful when adding or removing source files from the project. -Within an active virtual environment where Meson is installed, run the following command: +Within an active virtual environment where `meson` is installed, run the following command: ```bash tools/update_meson.py ``` + +## Update Lock Files + +This command automatically updates the lock files for the project dependencies (the ones in the `./requirements/` directory). This command is useful when adding or removing dependencies from the project, or when updating the dependencies. + +Within an active virtual environment where `pip-tools` and `toml` are installed, run the following command: + +```bash +tools/update-lock.py +``` diff --git a/tools/update-lock.py b/tools/update-lock.py new file mode 100644 index 00000000000..d95a118d452 --- /dev/null +++ b/tools/update-lock.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# pyright: strict + +import argparse +import subprocess +from pathlib import Path + +import toml + + +def compile_requirements(pyproject_file: Path, groups: dict[str, list[str]]) -> None: + requirements_dir = pyproject_file.parent / "requirements" + if not requirements_dir.exists(): + requirements_dir.mkdir() + subprocess.run( + [ + "pip-compile", + "-v", + "--upgrade", + "--no-build-isolation", + "--output-file", + str(requirements_dir / "requirements.txt"), + pyproject_file, + ], + check=True, + ) + for group in groups: + requirements_file = requirements_dir / f"{group}-requirements.txt" + requirements_in_file = requirements_dir / f"{group}.in" + with requirements_in_file.open("w") as f: + f.write("\n".join(groups[group])) + subprocess.run( + [ + "pip-compile", + "-v", + "--upgrade", + "--no-build-isolation", + "--output-file", + str(requirements_file), + str(requirements_in_file), + ], + check=True, + ) + + +parser = argparse.ArgumentParser( + description="Update lock files under requirements using pip-tools compile." +) +parser.add_argument( + "sourcedir", help="Source directory", nargs="?", default=".", type=Path +) +options = parser.parse_args() + +pyproject_file = Path(options.sourcedir) / "pyproject.toml" +with pyproject_file.open("r") as f: + pyproject = toml.load(f) + +groups = pyproject.get("dependency-groups", {}) +compile_requirements(pyproject_file, groups)