## Configuration for tox.1## Needs tox installed in the system python.2##3## doctest: Run the sage doctests. From the SAGE_ROOT/src directory:4##5## $ tox6##7## Arguments are passed on to "sage -t":8##9## $ tox sage/geometry10##11## To pass on options to "sage -t", use -- to separate it from tox options:12##13## $ tox -- --verbose --optional=sage,pynormaliz --long sage/geometry14##15## pycodestyle:16##17## $ tox -e pycodestyle18##19## Note that on the first run, tox automatically installs pycodestyle20## in a virtual environment.21##22[tox]23envlist = doctest, coverage, startuptime, pycodestyle-minimal, relint, codespell, rst, ruff-minimal24# When adding environments above, also update the delegations in SAGE_ROOT/tox.ini25skipsdist = true2627requires =28# For the renamed "allowlist_externals" keyword, need >= 3.1829# Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.130tox>=3.1831tox<4.14.13233[sagedirect]34# Base for tox environments that bypass the virtual environment set up by tox,35# calling sage directly.36passenv =37HOME38setenv =39SAGE={toxinidir}/../sage40envdir={toxworkdir}/sagedirect41allowlist_externals =42{env:SAGE}4344[testenv:doctest]45description =46run the Sage doctester (same as "sage -t")47## This toxenv bypasses the virtual environment set up by tox.48passenv = {[sagedirect]passenv}49setenv = {[sagedirect]setenv}50envdir = {[sagedirect]envdir}51allowlist_externals = {[sagedirect]allowlist_externals}52commands =53{env:SAGE} -t -p 0 {posargs:--all}5455[testenv:coverage.py]56# https://coverage.readthedocs.io/en/latest/index.html57description =58run the Sage doctester with Coverage.py59## This toxenv bypasses the virtual environment set up by tox.60passenv = {[sagedirect]passenv}61setenv = {[sagedirect]setenv}62envdir = {[sagedirect]envdir}63allowlist_externals = {[sagedirect]allowlist_externals}64commands_pre =65{env:SAGE} -pip install -U coverage66commands =67{env:SAGE} --python -m coverage run "{toxinidir}/../venv/bin/sage-runtests" -p 0 {posargs:--all}68commands_post =69{env:SAGE} --python -m coverage combine70{env:SAGE} --python -m coverage report7172[testenv:coverage.py-html]73# https://coverage.readthedocs.io/en/latest/index.html74description =75run the Sage doctester with Coverage.py, generate HTML report76## This toxenv bypasses the virtual environment set up by tox.77passenv = {[sagedirect]passenv}78setenv = {[sagedirect]setenv}79envdir = {[sagedirect]envdir}80allowlist_externals = {[sagedirect]allowlist_externals}81commands_pre =82{env:SAGE} -pip install -U coverage83commands =84{env:SAGE} --python -m coverage run "{toxinidir}/../venv/bin/sage-runtests" -p 0 {posargs:--all}85commands_post =86{env:SAGE} --python -m coverage combine87{env:SAGE} --python -m coverage report88{env:SAGE} --python -m coverage html -d "{envdir}"8990[testenv:coverage.py-xml]91# https://coverage.readthedocs.io/en/latest/index.html92description =93run the Sage doctester with Coverage.py, generate XML report94## This toxenv bypasses the virtual environment set up by tox.95passenv = {[sagedirect]passenv}96setenv = {[sagedirect]setenv}97envdir = {[sagedirect]envdir}98allowlist_externals = {[sagedirect]allowlist_externals}99commands_pre =100{env:SAGE} -pip install -U coverage101commands =102{env:SAGE} --python -m coverage run "{toxinidir}/../venv/bin/sage-runtests" -p 0 {posargs:--all}103commands_post =104{env:SAGE} --python -m coverage combine105{env:SAGE} --python -m coverage report106{env:SAGE} --python -m coverage xml -o "{envdir}/coverage.xml"107108[testenv:coverage]109description =110give information about doctest coverage of files111(same as "sage --coverage[all]")112## This toxenv bypasses the virtual environment set up by tox.113passenv = {[sagedirect]passenv}114setenv = {[sagedirect]setenv}115envdir = {[sagedirect]envdir}116allowlist_externals = {[sagedirect]allowlist_externals}117commands =118{env:SAGE} --coverage {posargs:--all}119120[testenv:startuptime]121description =122display how long each component of Sage takes to start up123(same as "sage --startuptime")124## This toxenv bypasses the virtual environment set up by tox.125passenv = {[sagedirect]passenv}126setenv = {[sagedirect]setenv}127envdir = {[sagedirect]envdir}128allowlist_externals = {[sagedirect]allowlist_externals}129commands =130{env:SAGE} --startuptime {posargs}131132[testenv:pyright]133description =134run the static typing checker pyright135deps = pyright136setenv =137{[sagedirect]setenv}138HOME={envdir}139# Fix version, see .github/workflows/build.yml140PYRIGHT_PYTHON_FORCE_VERSION=1.1.232141allowlist_externals = {[sagedirect]allowlist_externals}142## We run pyright from within the sage-env so that SAGE_LOCAL is available.143## pyright is already configured via SAGE_ROOT/pyrightconfig.json to use our venv.144##145## Running pyright on the whole Sage source tree takes very long146## and may run out of memory. When no files/directories are given, just run it147## on the packages that already have typing annotations.148commands =149{env:SAGE} -sh -c 'pyright {posargs:{toxinidir}/sage/combinat {toxinidir}/sage/manifolds}'150151[testenv:pycodestyle]152description =153check against the Python style conventions of PEP8154deps = pycodestyle155commands = pycodestyle {posargs:{toxinidir}/sage/}156157[testenv:pycodestyle-minimal]158description =159check against Sage minimal style conventions160# Check for the following issues:161# E111: indentation is not a multiple of four162# E211: whitespace before '('163# E271: multiple spaces after keyword164# E305: expected 2 blank lines after class or function definition, found 1165# E306: expected 1 blank line before a nested definition, found 0166# E401: multiple imports on one line167# E502 the backslash is redundant between brackets168# E701: multiple statements on one line (colon)169# E702: multiple statements on one line (semicolon)170# E703: statement ends with a semicolon171# E711: comparison to None should be ‘if cond is None:’172# E712: comparison to True should be ‘if cond is True:’ or ‘if cond:’173# E713 test for membership should be ’not in’174# E721: do not compare types, use isinstance()175# E722: do not use bare except, specify exception instead176# W291: trailing whitespace177# W293: blank line contains whitespace178# W391: blank line at end of file179# W605: invalid escape sequence ‘x’180# See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes181deps = pycodestyle182commands =183pycodestyle --select E111,E115,E21,E221,E222,E225,E227,E228,E25,E271,E272,E275,E302,E303,E305,E306,E401,E502,E701,E702,E703,E71,E72,W291,W293,W391,W605 {posargs:{toxinidir}/sage/}184pycodestyle --select E111,E222,E271,E301,E302,E303,E305,E306,E401,E502,E703,E712,E713,E714,E72,W29,W391,W605, --filename *.pyx {posargs:{toxinidir}/sage/}185186[pycodestyle]187max-line-length = 160188statistics = True189190[testenv:relint]191description =192check whether some forbidden patterns appear193# https://github.com/codingjoe/relint194# The patterns are in .relint.yml195deps = relint196allowlist_externals = find197commands = find {posargs:{toxinidir}/sage/} \198-name "*\#*" -prune -o \199-name "*.a" -prune -o \200-name "*.bak" -prune -o \201-name "*.bz2" -prune -o \202-name "*.dia" -prune -o \203-name "*.gz" -prune -o \204-name "*.ico" -prune -o \205-name "*.inv" -prune -o \206-name "*.JPEG" -prune -o \207-name "*.jpeg" -prune -o \208-name "*.JPG" -prune -o \209-name "*.jpg" -prune -o \210-name "*.log" -prune -o \211-name "*.o" -prune -o \212-name "*.orig" -prune -o \213-name "*.PDF" -prune -o \214-name "*.pdf" -prune -o \215-name "*.PNG" -prune -o \216-name "*.png" -prune -o \217-name "*.pyc" -prune -o \218-name "*.so" -prune -o \219-name "*.sobj" -prune -o \220-name "*.sws" -prune -o \221-name "*.tar" -prune -o \222-name "*.tgz" -prune -o \223-name "*.xz" -prune -o \224-name "*.zip" -prune -o \225-name "*~*" -prune -o \226-name ".DS_Store" -prune -o \227-exec relint -c {toxinidir}/.relint.yml \{\} +228229[testenv:codespell]230description =231check for misspelled words in source code232# https://pypi.org/project/codespell/233deps = codespell234commands = codespell \235--skip="*\#*,*.a,*.bak,*.bz2,*.dia,*.gz,*.ico,*.inv,*.JPEG,*.jpeg" \236--skip="*.JPG,*.jpg,*.log,*.o,*.orig,*.PDF,*.pdf,*.PNG,*.png,*.pyc" \237--skip="*.so,*.sobj,*.sws,*.tar,*.tgz,*.xz,*.zip,*~*,.DS_Store" \238--skip="doc/ca,doc/de,doc/es,doc/fr,doc/hu,doc/it,doc/ja,doc/pt,doc/ru,doc/tr" \239--skip="src/doc/ca,src/doc/de,src/doc/es,src/doc/fr,src/doc/hu" \240--skip="src/doc/it,src/doc/ja,src/doc/pt,src/doc/ru,src/doc/tr" \241--skip=".git,.tox,autom4te.cache,cythonized,dist,lib.*,local" \242--skip="logs,scripts-3,tmp,upstream,worktree*,*.egg-info" \243--dictionary=- \244--dictionary={toxinidir}/.codespell-dictionary.txt \245--ignore-words={toxinidir}/.codespell-ignore.txt \246{posargs:{toxinidir}/sage/}247248[testenv:rst]249description =250validate Python docstrings markup as reStructuredText251deps = flake8-rst-docstrings252commands = flake8 --select=RST {posargs:{toxinidir}/sage/}253254[testenv:cython-lint]255description =256check Cython files for code style257deps = cython-lint258commands = cython-lint --no-pycodestyle {posargs:{toxinidir}/sage/}259260[testenv:ruff]261description =262check against Python style conventions263deps = ruff264passenv = RUFF_OUTPUT_FORMAT265commands = ruff check {posargs:{toxinidir}/sage/}266267[testenv:ruff-minimal]268description =269check against Sage minimal style conventions270deps = ruff271# https://github.com/ChartBoost/ruff-action/issues/7#issuecomment-1887780308272passenv = RUFF_OUTPUT_FORMAT273# Output of currently failing, from "./sage -tox -e ruff -- --statistics":274#275# 3579 PLR2004 [ ] Magic value used in comparison, consider replacing `- 0.5` with a constant variable276# 3498 I001 [*] Import block is un-sorted or un-formatted277# 2146 F401 [*] `.PyPolyBoRi.Monomial` imported but unused278# 1964 E741 [ ] Ambiguous variable name: `I`279# 1676 F821 [ ] Undefined name `AA`280# 1513 PLR0912 [ ] Too many branches (102 > 12)281# 1159 PLR0913 [ ] Too many arguments in function definition (10 > 5)282# 815 E402 [ ] Module level import not at top of file283# 671 PLR0915 [ ] Too many statements (100 > 50)284# 483 PLW2901 [ ] Outer `for` loop variable `ext` overwritten by inner `for` loop target285# 433 PLR5501 [*] Use `elif` instead of `else` then `if`, to reduce indentation286# 428 PLR0911 [ ] Too many return statements (10 > 6)287# 404 E731 [*] Do not assign a `lambda` expression, use a `def`288# 351 F405 [ ] `ComplexField` may be undefined, or defined from star imports289# 306 PLR1714 [*] Consider merging multiple comparisons. Use a `set` if the elements are hashable.290# 236 F403 [ ] `from .abelian_gps.all import *` used; unable to detect undefined names291# 116 PLR0402 [*] Use `from matplotlib import cm` in lieu of alias292# 111 PLW0603 [ ] Using the global statement to update `AA_0` is discouraged293# 78 F841 [*] Local variable `B` is assigned to but never used294# 48 PLW0602 [ ] Using global for `D` but no assignment is done295# 33 PLR1711 [*] Useless `return` statement at end of function296# 24 E714 [*] Test for object identity should be `is not`297# 20 PLR1701 [*] Merge `isinstance` calls298# 17 PLW3301 [ ] Nested `max` calls can be flattened299# 16 PLW1510 [*] `subprocess.run` without explicit `check` argument300# 14 E721 [ ] Do not compare types, use `isinstance()`301# 14 PLW0120 [*] `else` clause on loop without a `break` statement; remove the `else` and dedent its contents302# 12 F811 [*] Redefinition of unused `CompleteDiscreteValuationRings` from line 49303# 8 PLC0414 [*] Import alias does not rename original package304# 7 E743 [ ] Ambiguous function name: `I`305# 7 PLR0124 [ ] Name compared with itself, consider replacing `a == a`306# 5 PLW0127 [ ] Self-assignment of variable `a`307# 4 PLW1508 [ ] Invalid type for environment variable default; expected `str` or `None`308# 3 PLC3002 [ ] Lambda expression called directly. Execute the expression inline instead.309# 2 E742 [ ] Ambiguous class name: `I`310# 2 PLE0302 [ ] The special method `__len__` expects 1 parameter, 3 were given311# 1 F402 [ ] Import `factor` from line 259 shadowed by loop variable312# 1 PLC0208 [*] Use a sequence type instead of a `set` when iterating over values313#314commands =315ruff check --ignore E402,E721,E731,E741,E742,E743,F401,F402,F403,F405,F821,F841,I001,PLC0206,PLC0208,PLC2401,PLC3002,PLE0302,PLR0124,PLR0402,PLR0911,PLR0912,PLR0913,PLR0915,PLR1704,PLR1711,PLR1714,PLR1716,PLR1736,PLR2004,PLR5501,PLW0120,PLW0211,PLW0602,PLW0603,PLW0642,PLW1508,PLW1510,PLW2901,PLW3301 {posargs:{toxinidir}/sage/}316ruff check --preview --select E111,E115,E21,E221,E222,E225,E227,E228,E25,E271,E272,E275,E302,E303,E305,E306,E401,E502,E701,E702,E703,E71,W291,W293,W391,W605 {posargs:{toxinidir}/sage/}317318[flake8]319rst-roles =320# Sphinx321doc,322file,323ref,324# Sphinx - https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#the-standard-domain (selection)325envvar,326# Sphinx - https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing-python-objects327attr,328class,329const,330data,331exc,332func,333kbd,334meth,335mod,336obj,337# from src/sage/misc/sagedoc.py338arxiv,339doi,340mathscinet,341oeis,342pari,343python,344issue,345wikipedia,346common_lisp,347ecl,348gap,349gap_package,350giac_cascmd,351giac_us,352maxima,353meson,354polymake,355ppl,356qepcad,357scip,358singular,359soplex360rst-directives =361attribute,362automethod,363autofunction,364toctree,365MODULEAUTHOR,366ONLY,367PLOT,368SEEALSO,369TODO370extend-ignore =371# Ignore RST306 Unknown target name -- because of references to the global bibliography372RST306373exclude =374# Avoid errors by exclude files with generated docstring portions such as {PLOT_OPTIONS_TABLE}375sage/combinat/designs/database.py376sage/graphs/graph_plot.py377sage/misc/sagedoc.py378379[coverage:run]380source = sage381concurrency = multiprocessing,thread382data_file = .coverage/.coverage383disable_warnings =384no-data-collected385module-not-measured386387[coverage:report]388ignore_errors = True389skip_empty = True390391392