# content of pyproject.toml
[build-system]
requires = ["setuptools>=64.0.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "anta"
version = "v1.3.0"
readme = "docs/README.md"
authors = [{ name = "Arista Networks ANTA maintainers", email = "anta-dev@arista.com" }]
maintainers = [
  { name = "Arista Networks ANTA maintainers", email = "anta-dev@arista.com" },
  { name = "Khelil Sator", email = "ksator@arista.com" },
  { name = "Matthieu Tâche", email = "mtache@arista.com" },
  { name = "Thomas Grimonet", email = "tgrimonet@arista.com" },
  { name = "Guillaume Mulocher", email = "gmulocher@arista.com" },
  { name = "Carl Baillargeon", email = "carl.baillargeon@arista.com" },
]
description = "Arista Network Test Automation (ANTA) Framework"
license = { file = "LICENSE" }
dependencies = [
  "asyncssh>=2.16",
  "cvprac>=1.3.1",
  "eval-type-backport>=0.1.3",  # Support newer typing features in older Python versions (required until Python 3.9 support is removed)
  "httpx>=0.27.0",
  "Jinja2>=3.1.2",
  "pydantic>=2.7",
  "pydantic-extra-types>=2.3.0",
  "PyYAML>=6.0",
  "requests>=2.31.0",
  "rich>=13.5.2,<14",
  "typing_extensions>=4.12"  # required for deprecated before Python 3.13
]
keywords = ["test", "anta", "Arista", "network", "automation", "networking", "devops", "netdevops"]
classifiers = [
    "License :: OSI Approved :: Apache Software License",
    "Operating System :: OS Independent",
    "Development Status :: 5 - Production/Stable",
    "Intended Audience :: Developers",
    "Intended Audience :: System Administrators",
    "Intended Audience :: Information Technology",
    "Programming Language :: Python",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Programming Language :: Python :: 3 :: Only",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Software Development :: Testing",
    "Topic :: System :: Networking",
]
requires-python = ">=3.9"

[project.optional-dependencies]
cli = [
  "click~=8.1.6",
  "click-help-colors>=0.9",
]
dev = [
  "bumpver>=2023.1129",
  "codespell>=2.2.6,<2.5.0",
  "mypy-extensions~=1.0",
  "mypy~=1.10",
  "pre-commit>=3.3.3",
  "pylint-pydantic>=0.2.4",
  "pylint>=2.17.5",
  "pytest-asyncio>=0.21.1",
  "pytest-cov>=4.1.0",
  "pytest-dependency",
  "pytest-codspeed>=2.2.0",
  "pytest-html>=3.2.0",
  "pytest-httpx>=0.30.0",
  "pytest-metadata>=3.0.0",
  "pytest>=7.4.0",
  "respx>=0.22.0",
  "ruff>=0.5.4,<0.11.0",
  "tox>=4.10.0,<5.0.0",
  "types-PyYAML",
  "types-pyOpenSSL",
  "types-requests",
  "typing-extensions",
  "yamllint>=1.32.0",
]
doc = [
  "fontawesome_markdown>=0.2.6",
  "griffe >=1.2.0",
  "griffe-warnings-deprecated>=1.1.0",
  "mike==2.1.3",
  "mkdocs>=1.6.1",
  "mkdocs-autorefs>=1.2.0",
  "mkdocs-bootswatch>=1.1",
  "mkdocs-git-revision-date-localized-plugin>=1.2.8",
  "mkdocs-git-revision-date-plugin>=0.3.2",
  "mkdocs-glightbox>=0.4.0",
  "mkdocs-material-extensions>=1.3.1",
  "mkdocs-material>=9.5.34",
  "mkdocstrings[python]>=0.26.0",
  "mkdocstrings-python>=1.11.0",
  "black>=24.10.0",
  "mkdocs-github-admonitions-plugin>=0.0.3"
]

[project.urls]
Homepage = "https://anta.arista.com"
"Bug Tracker" = "https://github.com/aristanetworks/anta/issues"
Contributing = "https://anta.arista.com/main/contribution/"

[project.scripts]
anta = "anta.cli:cli"

[tool.setuptools.packages.find]
include = ["anta*", "asynceapi*"]
namespaces = false

[tool.setuptools.package-data]
"anta" = ["py.typed"]

################################
# Version
################################
[tool.bumpver]
current_version = "1.3.0"
version_pattern = "MAJOR.MINOR.PATCH"
commit_message  = "bump: Version {old_version} -> {new_version}"
commit = true
# No tag
tag = false
push = false

[tool.bumpver.file_patterns]
"pyproject.toml" = ['current_version = "{version}"', 'version = "v{version}"']
"docs/contribution.md" = ["anta    {version}"]
"docs/requirements-and-installation.md " = ["anta, version v{version}"]

################################
# Typing
# mypy as per https://pydantic-docs.helpmanual.io/mypy_plugin/#enabling-the-plugin
################################
[tool.mypy]
plugins = [
  "pydantic.mypy",
  ]
# Comment below for better type checking
#follow_imports = "skip"
# Make it false if we implement stubs using stubgen from mypy for asynceapi, cvprac
# and configure mypy_path to generated stubs e.g.: mypy_path = "./out"
ignore_missing_imports = true
warn_redundant_casts = true
# Note: tox find some unused type ignore which are required for pre-commit
# To investigate
warn_unused_ignores = true
disallow_any_generics = true
check_untyped_defs = true
no_implicit_reexport = true
strict_optional = true

# for strict mypy: (this is the tricky one :-))
disallow_untyped_defs = true

[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true
warn_untyped_fields = true

################################
# Testing
################################
[tool.pytest.ini_options]
# When run from anta directory this will read cov-config from pyproject.toml
addopts = "-ra -q -vv --cov --cov-report term:skip-covered --color yes"
log_level = "WARNING"
render_collapsed = true
testpaths = ["tests"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "function"
norecursedirs = ["tests/benchmark"] # Do not run performance testing outside of Codspeed
filterwarnings = [
  # cvprac is raising the next warning
  "default:pkg_resources is deprecated:DeprecationWarning",
  # Need to investigate the following - only occuring when running the full pytest suite
  "ignore:Exception ignored in.*:pytest.PytestUnraisableExceptionWarning",
  # Ignore cryptography >=43.0.0 warnings until asyncssh issue is fixed
  "ignore:ARC4:cryptography.utils.CryptographyDeprecationWarning",
  "ignore:TripleDES:cryptography.utils.CryptographyDeprecationWarning",

]


[tool.coverage.run]
branch = true
# https://community.sonarsource.com/t/python-coverage-analysis-warning/62629/7
include = ["anta/*", "asynceapi/*"]
parallel = true
relative_files = true

[tool.coverage.report]
# Regexes for lines to exclude from consideration
exclude_lines = [
    # Have to re-enable the standard pragma
    "pragma: no cover",

    # Don't complain about missing debug-only code:
    "def __repr__",
    "if self\\.debug",

    # Don't complain if tests don't hit defensive assertion code:
    "raise AssertionError",
    "raise NotImplementedError",

    # Don't complain if non-runnable code isn't run:
    "if 0:",
    "if __name__ == .__main__.:",

    # Don't complain about abstract methods, they aren't run:
    "@(abc\\.)?abstractmethod",

    # Don't complain about TYPE_CHECKING blocks
    "if TYPE_CHECKING:",
]

ignore_errors = true

[tool.coverage.html]
directory = "coverage_html_report"

[tool.coverage.xml]
output = ".coverage.xml"

################################
# Tox
################################
[tool.tox]
legacy_tox_ini = """
[tox]
min_version = 4.0
envlist =
  clean,
  lint,
  type,
  py{39,310,311,312,313},
  report

[gh-actions]
python =
  3.9: py39
  3.10: py310
  3.11: erase, py311, report
  3.12: py312
  3.13: py313

[testenv]
description = Run pytest with {basepython}
extras =
  dev
  cli
# posargs allows to run only a specific test using
# tox -e <env> -- path/to/my/test::test
commands =
   pytest {posargs}
# To test on non-POSIX system
# https://github.com/tox-dev/tox/issues/1455
passenv = USERNAME

[testenv:lint]
description = Check the code style
commands =
  ruff check .
  ruff format . --check
  pylint anta
  pylint tests
  pylint asynceapi

[testenv:type]
description = Check typing
commands =
  mypy --config-file=pyproject.toml anta
  mypy --config-file=pyproject.toml tests
  mypy --config-file=pyproject.toml asynceapi

[testenv:clean]
description = Erase previous coverage reports
deps = coverage[toml]
skip_install = true
commands = coverage erase

[testenv:report]
description = Generate coverage report
deps = coverage[toml]
commands =
  coverage --version
  coverage html --rcfile=pyproject.toml
  coverage xml --rcfile=pyproject.toml
# add the following to make the report fail under some percentage
# commands = coverage report --fail-under=80
depends = py311
"""

################################
# Ruff
################################
[tool.ruff]

# Exclude a variety of commonly ignored directories.
exclude = [
    ".bzr",
    ".direnv",
    ".eggs",
    ".git",
    ".git-rewrite",
    ".hg",
    ".ipynb_checkpoints",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".pyenv",
    ".pytest_cache",
    ".pytype",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    ".vscode",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "site-packages",
    "venv",
    ".github",
]

line-length = 165

# Assume Python 3.9 as this is the lowest supported version for ANTA
target-version = "py39"

[tool.ruff.lint]
# select all cause we like being suffering
select = ["ALL",
    # By enabling a convention for docstrings, ruff automatically ignore some rules that need to be
    # added back if we want them.
    # https://docs.astral.sh/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings
    "D212",
    "D415",
    "D417",
]
ignore = [
  "COM812",  # Ignoring conflicting rules that may cause conflicts when used with the formatter
  "ISC001",  # Ignoring conflicting rules that may cause conflicts when used with the formatter
  "TD002",   # We don't have require authors in TODO
  "TD003",   # We don't have an issue link for all TODOs today
  "FIX002",  # Line contains TODO - ignoring for ruff for now
  "F821",    # Disable undefined-name until resolution of #10451
]

# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[tool.ruff.lint.pydocstyle]
convention = "numpy"

[tool.ruff.lint.pylint]
# These settings are used to configure pylint rules run in ruff. In order to keep sane and while
# we have not removed pylint completely, these settings should be kept in sync with our pylintrc file.
# https://github.com/astral-sh/ruff/issues/970
max-branches = 13
max-args = 10

[tool.ruff.lint.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 10

[tool.ruff.lint.pep8-naming]
"ignore-names" = [
  "RICH_COLOR_PALETTE"
]


[tool.ruff.lint.flake8-type-checking]
# These classes require that type annotations be available at runtime
runtime-evaluated-base-classes = ["pydantic.BaseModel", "anta.models.AntaTest.Input"]


[tool.ruff.lint.per-file-ignores]
"tests/*" = [
  "S101",    # Complains about asserts in units and libs.
  "SLF001",  # Lots of private member accessed for test purposes
]
"tests/units/*" = [
  "ARG002",  # Sometimes we need to declare unused arguments when a parameter is not used but declared in @pytest.mark.parametrize
  "FBT001",  # Boolean-typed positional argument in function definition
  "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable
  "S105",    # Passwords are indeed hardcoded in tests
  "S106",    # Passwords are indeed hardcoded in tests
  "S108",    # Probable insecure usage of temporary file or directory
]
"tests/units/anta_tests/test_interfaces.py" = [
  "S104",  # False positive for 0.0.0.0 bindings in test inputs
]
"tests/units/anta_tests/*" = [
  "F401",  # In this module, we import tests.units.anta_tests.test without using it to auto-generate tests
]
"anta/*" = [
  "TRY400",  # Use `logging.exception` instead of `logging.error` - we know what we are doing
]
"anta/cli/exec/utils.py" = [
  "SLF001",  # TODO: some private members, lets try to fix
]
"anta/cli/__init__.py" = [
  "T201",  # Allow print statements
]
"anta/cli/*" = [
  "PLR0913",  # CLI has many arguments defined in functions
  "ANN401",   # TODO: Check if we can update the Any type hints in the CLI
]
"anta/tests/field_notices.py" = [
  "PLR2004",  # Magic value used in comparison, consider replacing 2131 with a constant variable - Field notice IDs are magic values
  "C901",     # TODO: test function is too complex, needs a refactor
  "PLR0911",  # TODO: Too many return statements, same as above needs a refactor
]
"anta/tests/routing/isis.py" = [
  "C901",     # TODO: test function is too complex, needs a refactor
  "PLR0912"   # Too many branches (15/12) (too-many-branches), needs a refactor
]
"anta/tests/logging.py" = [
  "A005",  # TODO: Module `logging` shadows a Python standard-library module
]
"anta/input_models/logging.py" = [
  "A005",  # TODO: Module `logging` shadows a Python standard-library module
]
"anta/decorators.py" = [
  "ANN401",  # Ok to use Any type hint in our decorators
]
"anta/tools.py" = [
  "ANN401",   # Ok to use Any type hint in our custom get functions
]
"examples/*.py" = [  # These are example scripts and linked in snippets
  "S105",   # Possible hardcoded password
  "S106",   # Possible hardcoded password assigned to argument
  "S108",   # Probable insecure usage of temporary file or directory
  "INP001", # Implicit packages
  "T201",   # `print` found
  "T203",   # `pprint` found

]

################################
# Pylint
################################
[tool.pylint]
disable = [ # Any rule listed here can be disabled: https://github.com/astral-sh/ruff/issues/970
    "invalid-name",
    "fixme",
    "unused-import",
    "unused-argument",
    "keyword-arg-before-vararg",
    "protected-access",
    "too-many-arguments",
    "too-many-positional-arguments",
    "wrong-import-position",
    "pointless-statement",
    "broad-exception-caught",
    "line-too-long",
    "unused-variable",
    "redefined-builtin",
    "global-statement",
    "reimported",
    "wrong-import-order",
    "wrong-import-position",
    "unnecessary-lambda",
    "abstract-class-instantiated", # Overlap with https://mypy.readthedocs.io/en/stable/error_code_list.html#check-instantiation-of-abstract-classes-abstract
    "unexpected-keyword-arg", # Overlap with https://mypy.readthedocs.io/en/stable/error_code_list.html#check-arguments-in-calls-call-arg and other rules
    "no-value-for-parameter", # Overlap with https://mypy.readthedocs.io/en/stable/error_code_list.html#check-arguments-in-calls-call-arg
    "import-outside-toplevel"
]
max-statements=61
max-returns=8
max-locals=23
max-line-length=165
max-module-lines=1700
# making similarity lines limit a bit higher than default 4
min-similarity-lines=10
# https://stackoverflow.com/questions/49680191/click-and-pylint
signature-mutators="click.decorators.option"
load-plugins="pylint_pydantic"
extension-pkg-whitelist="pydantic"