[tox]
envlist = update, fetch, compile, autopep8, docformatter, docformatter_check, isort, isort_check, pylint, pylint_tests, flake8, flake8_tests, pydocstyle, mypy, codespell, format, lint, docs, verify_tables, py{38, 39, 310, 311, 312, 313, 314}
skip_missing_interpreters = true

[base]
pip_compile_command = pip-compile --resolver=backtracking --strip-extras --no-emit-index-url --allow-unsafe --upgrade

[testenv]
deps = -r requirements-tests39.txt
setenv = TERM=
         TERM_PROGRAM=
commands = {envpython} -m pytest --cov-config={toxinidir}/tox.ini {posargs:\
             --verbose \
             --junit-xml=.tox/results.{envname}.xml \
             --durations=10 \
             } \
          --log-format='%(levelname)s %(relativeCreated)2.2f %(filename)s:%(lineno)d %(message)s' \
          tests
passenv = TEST_QUICK,TEST_KEYBOARD,TEST_RAW


[isort]
line_length = 100
indent = '    '
multi_line_output = 1
length_sort = 1
import_heading_stdlib = std imports
import_heading_thirdparty = 3rd party
import_heading_firstparty = local
import_heading_localfolder = local
sections=FUTURE,STDLIB,TYPING_STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
no_lines_before=LOCALFOLDER
known_first_party = wcwidth
known_third_party = codecov,blessed
known_typing_stdlib = typing,typing_extensions
atomic = true

[pytest]
norecursedirs = .git .tox build
addopts = --disable-pytest-warnings
          --cov-append --cov-report=html --color=yes --ignore=.tox
          --cov=wcwidth
filterwarnings =
    error
junit_family = xunit1

[flake8]
exclude = .tox,build
max-line-length = 100
ignore = E402,E501,F401,W503,W504

[coverage:run]
branch = True
source = wcwidth
parallel = True
relative_files = True

[coverage:report]
omit = tests/*
       wcwidth/table_grapheme_overrides/_known_*
       wcwidth/table_grapheme_overrides/_registry.py
exclude_lines = pragma: no cover
precision = 1

[coverage:paths]
source = wcwidth/

[testenv:compile]
basepython = python3.14
commands = python -m compileall {toxinidir}/wcwidth {toxinidir}/bin {toxinidir}/tests {toxinidir}/docs

# wcwidth itself has no 3rd party dependencies, but to ensure the best available
# version for the newest to oldest python versions for testing, must also use some
# targeted versions to 'compile' those requirements into their frozen form,
# otherwise incompatible packages can be pinned.
[testenv:update_requirements_update]
basepython = python3.14
deps = pip-tools
commands = {[base]pip_compile_command} requirements-update.in -o requirements-update.txt

[testenv:update_requirements_docs]
basepython = python3.14
deps = pip-tools
commands = {[base]pip_compile_command} requirements-docs.in -o docs/requirements.txt

[testenv:update_requirements39]
basepython = python3.9
deps = pip-tools
commands = {[base]pip_compile_command} requirements-tests39.in -o requirements-tests39.txt

[testenv:update_requirements38]
basepython = python3.8
deps = pip-tools
commands = {[base]pip_compile_command} requirements-tests38.in -o requirements-tests38.txt

[testenv:py38]
deps = -r requirements-tests38.txt

[testenv:update]
# it is best to use latest python for latest 'unicodedata' for named table items in generated code
basepython = python3.15
usedevelop = true
deps = -r requirements-update.txt
commands = python {toxinidir}/bin/update-tables.py {posargs:--fetch-all-versions}

[testenv:fetch]
basepython = python3.14
usedevelop = true
deps = -r requirements-update.txt
commands = - python {toxinidir}/bin/update-tables.py {posargs:--only-fetch}

[testenv:autopep8]
basepython = python3.14
deps = autopep8
commands =
    autopep8 \
        --in-place \
        --recursive \
        --aggressive \
        --aggressive \
        --max-line-length 100 \
        wcwidth/ bin/ tests/

[testenv:isort]
deps = isort
basepython = python3.13
commands = isort --quiet --apply --recursive wcwidth tests bin

[testenv:pylint]
basepython = python3.13
deps = pylint
commands = pylint --rcfile={toxinidir}/.pylintrc \
           --ignore=tests,docs,conf.py,build,distutils,.pyenv,.git,.tox \
           {posargs:{toxinidir}}/wcwidth

[testenv:flake8]
basepython = python3.13
deps = flake8
commands = flake8 --exclude=tests docs/ wcwidth/ bin/ tests/

[testenv:docs]
# matches .readthedocs.yaml and environment
basepython = python3.14
deps = -r {toxinidir}/docs/requirements.txt
commands = sphinx-build -W docs/ build/sphinx

[testenv:verify_tables]
basepython = python3.13
commands = python {toxinidir}/bin/verify-table-integrity.py

[testenv:linkcheck]
basepython = python3.11
deps = -r {toxinidir}/docs/requirements.txt
commands = sphinx-build -v -W -d {toxinidir}/docs/_build/doctrees -b linkcheck docs docs/_build/linkcheck

[testenv:codecov]
basepython = python{env:TOXPYTHON:{env:TRAVIS_PYTHON_VERSION:3.10}}
passenv = TOXENV,CI,TRAVIS,TRAVIS_*,CODECOV_*
deps = codecov
commands = codecov -e TOXENV

[testenv:pydocstyle]
basepython = python3.13
deps = pydocstyle
       doc8
       pygments
commands = pydocstyle --source --explain {toxinidir}/wcwidth
           doc8 --ignore-path docs/_build --ignore-path docs/requirements.txt --ignore D000 --max-line-length 100 docs

[testenv:docformatter]
basepython = python3.13
deps = docformatter==1.7.7
commands = docformatter --in-place --recursive --pre-summary-newline \
             --wrap-summaries=100 --wrap-descriptions=100 \
             {toxinidir}/wcwidth/ {toxinidir}/bin {toxinidir}/tests/

[testenv:docformatter_check]
basepython = python3.13
deps = {[testenv:docformatter]deps}
commands = docformatter --check --diff --recursive --pre-summary-newline \
           --wrap-summaries=100 --wrap-descriptions=100 \
           {toxinidir}/wcwidth/ {toxinidir}/bin {toxinidir}/tests/

[testenv:isort_check]
basepython = python3.14
deps = isort
commands = isort --diff --check-only wcwidth tests bin

[testenv:flake8_tests]
basepython = python3.14
deps = flake8
commands = flake8 --ignore=E501,W504,F401 tests/

[testenv:pylint_tests]
basepython = python3.13
deps = pytest
       pylint
commands = pylint --rcfile={toxinidir}/.pylintrc \
           --disable=protected-access,unused-argument,missing-function-docstring,line-too-long \
           tests

[testenv:mypy]
basepython = python3.14
deps = mypy
commands = mypy --strict wcwidth

[mypy]
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true

[testenv:codespell]
basepython = python3.13
deps = codespell
commands = codespell --skip="*.pyc,htmlcov,_build,build,*.egg-info,.tox,data,./tests/*.txt,*.csv,*.ods,table_*.py,docs/specs.rst,*.isorted,ucs-detect" \
                     --ignore-words-list="thirdparty,claus,oclock,womens,aprox" \
                     --uri-ignore-words-list '*' \
                     --summary --count

[testenv:format]
basepython = python3.13
deps = {[testenv:isort]deps}
       {[testenv:docformatter]deps}
       {[testenv:autopep8]deps}
commands = {[testenv:isort]commands}
           {[testenv:docformatter]commands}
           {[testenv:autopep8]commands}

[testenv:lint]
basepython = python3.14
deps = {[testenv:flake8]deps}
       {[testenv:mypy]deps}
       {[testenv:isort_check]deps}
       {[testenv:pydocstyle]deps}
       {[testenv:pylint_tests]deps}
       {[testenv:codespell]deps}
commands = {[testenv:compile]commands}
	   {[testenv:flake8]commands}
           {[testenv:mypy]commands}
           {[testenv:pylint]commands}
           {[testenv:flake8_tests]commands}
           {[testenv:pylint_tests]commands}
           {[testenv:codespell]commands}
           {[testenv:pydocstyle]commands}
           {[testenv:isort_check]commands}
