[tool.poetry]

name = "lalib"
version = "0.5.0.dev0"

authors = [
    "Alexander Hess <alexander@webartifex.biz>",
]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "Intended Audience :: Education",
    "Intended Audience :: Science/Research",
    "License :: OSI Approved :: MIT License",
    "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",
]
description = "A Python library to study linear algebra"
license = "MIT"
readme = "README.md"

documentation = "https://lalib.readthedocs.io"
homepage = "https://github.com/webartifex/lalib"
repository = "https://github.com/webartifex/lalib"


[tool.poetry.dependencies]

python = "^3.9"

typing-extensions = [ { python = "<3.11", version = "^4.12" } ]  # to support Python 3.9 & 3.10


[tool.poetry.group.dev.dependencies]

pre-commit = "^3.8"

# Code formatters
autoflake = "^2.3"
black = "^24.8"
isort = "^5.13"

# Code linters
flake8 = "^7.1"
flake8-annotations = "^3.1"
flake8-bandit = "^4.1"
flake8-black = "^0.3"
flake8-broken-line = "^1.0"
flake8-bugbear = "^24.8"
flake8-commas = "^4.0"
flake8-comprehensions = "^3.15"
flake8-debugger = "^4.1"
flake8-docstrings = "^1.7"
flake8-eradicate = "^1.5"
flake8-isort = "^6.1"
flake8-quotes = "^3.4"
flake8-string-format = "^0.3"
flake8-todos = "^0.3"
flake8-pyproject = "^1.2"
flake8-pytest-style = "^2.0"
flake8-unused-arguments = "^0.0"
mypy = "^1.11"
pep8-naming = "^0.14"  # flake8 plug-in
pydoclint = { extras = ["flake8"], version = "^0.5" }
ruff = "^0.6"

# Documentation
sphinx = [
    { python = "=3.9", version = "^7.4" },
    { python = ">=3.10", version = "^8.0" },
]
sphinx-autodoc-typehints = "^2.3"

# Test suite
coverage = "^7.6"
packaging = "^24.1"  # to test the version identifier
pytest = "^8.3"
pytest-cov = "^5.0"
pytest-randomly = "^3.15"
pytest-repeat = "^0.9"
semver = "^3.0"  # to test the version identifier
tomli = [ { python = "<3.11", version = "^2.0" } ]
xdoctest = { extras = ["colors"], version = "^1.2" }


[tool.poetry.urls]

"Issues Tracker" = "https://github.com/webartifex/lalib/issues"



[tool.autoflake]
# Source: https://github.com/PyCQA/autoflake#configuration

in-place = true
recursive = true
expand-star-imports = true
remove-all-unused-imports = true
ignore-init-module-imports = true  # modifies "remove-all-unused-imports"
remove-duplicate-keys = true
remove-unused-variables = true



[tool.black]
# Source: https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html

line-length = 88
target-version = ["py312", "py311", "py310", "py39"]



[tool.coverage]
# Source: https://coverage.readthedocs.io/en/latest/config.html


[tool.coverage.paths]

source = ["src/", "*/site-packages/"]


[tool.coverage.report]

show_missing = true

skip_covered = true
skip_empty = true

exclude_lines = [

    # "pragma: no cover"
    # => Intentionally commented out as we thrive for 100% test coverage

    # PyPI's "typing-extensions" are needed to make `mypy` work
    "pragma: no cover ${PRAGMA_SUPPORT_39_N_310}",

]


[tool.coverage.run]

data_file = ".cache/coverage/data"

branch = true
parallel = true

source = ["lalib"]


[tool.coverage.xml]

output = ".cache/coverage/report.xml"



[tool.flake8]

select = [

    # violations also covered by `ruff` below

    "ANN",     # flake8-annotations => enforce type checking for functions
    "B",       # flake8-bugbear => bugs and design flaws
    "C4",      # flake8-comprehensions => better comprehensions
    "C8",      # flake8-commas => better comma placements ("COM" for `ruff`)
    "C90",     # mccabe => cyclomatic complexity (Source: https://github.com/pycqa/mccabe#plugin-for-flake8)
    "D",       # flake8-docstrings / pydocstyle => PEP257 compliance
    "E", "W",  # pycodestyle => PEP8 compliance (Source: https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes)
    "E800",    # flake8-eradicate / eradicate => no commented out code ("ERA" for `ruff`)
    "F",       # pyflakes => basic errors (Source: https://flake8.pycqa.org/en/latest/user/error-codes.html)
    "I",       # flake8-isort => isort would make changes
    "N",       # pep8-naming
    "PT",      # flake8-pytest-style => enforce a consistent style with pytest
    "Q",       # flake8-quotes => use double quotes everywhere (complying with black)
    "S",       # flake8-bandit => common security issues
    "T00",     # flake8-todos => unify TODOs
    "T10",     # flake8-debugger => no debugger usage
    "U100",    # flake8-unused-arguments => declared function arguments must be used

    # violations not covered by `ruff` below

    "BLK",     # flake8-black => complain if black wants to make changes
    "DOC",     # pydoclint (replaces "darglint") => docstring matches implementation
    "N400",    # flake8-broken-line => no "\" to end a line
    "P",       # flake8-string-format => unify usage of `str.format()` ("FMT" in the future)

]

ignore = []

extend-ignore = [  # never check the following codes

    "ANN101", "ANN102",  # `self` and `cls` in methods need no annotation

    "ANN401",  # allow dynamically typed expressions with `typing.Any`

    "DOC301",  # PEP257 => class constructor's docstring go in `.__init__()`

    # Comply with black's style
    # Sources: https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#pycodestyle
    "E203", "E701", "E704", "W503",

]

per-file-ignores = [

    # Linting rules for the test suite:
    # - type hints are not required
    # - `assert`s are normal
    "tests/*.py:ANN,S101",

]

# Explicitly set mccabe's maximum complexity to 10 as recommended by
# Thomas McCabe, the inventor of the McCabe complexity, and the NIST
# Source: https://en.wikipedia.org/wiki/Cyclomatic_complexity#Limiting_complexity_during_development
max-complexity = 10

# Whereas black and isort break the line at 88 characters,
# make flake8 not complain about anything (e.g., comments) until 100
max-line-length = 99

# Preview the code lines that cause errors
show-source = true

# Plug-in: flake8-docstrings
# Source: https://www.pydocstyle.org/en/latest/error_codes.html#default-conventions
docstring-convention = "google"

# Plug-in: flake8-eradicate
# Source: https://github.com/wemake-services/flake8-eradicate#options
eradicate-aggressive = true

# Plug-in: flake8-pytest-style
#
# Aligned with [tool.ruff.lint.flake8-pytest-style] below
#
# Prefer `@pytest.fixture` over `@pytest.fixture()`
pytest-fixture-no-parentheses = true
#
# Prefer `@pytest.mark.foobar` over `@pytest.mark.foobar()`
pytest-mark-no-parentheses = true
#
# Prefer `@pytest.mark.parametrize(['param1', 'param2'], [(1, 2), (3, 4)])`
# over `@pytest.mark.parametrize(('param1', 'param2'), ([1, 2], [3, 4]))`
pytest-parametrize-names-type = "list"
pytest-parametrize-values-row-type = "tuple"
pytest-parametrize-values-type = "list"

# Plug-in: flake8-quotes
# Source: https://github.com/zheller/flake8-quotes#configuration
avoid-escape = true
docstring-quotes = "double"
inline-quotes = "double"
multiline-quotes = "double"

# Plug-in: flake8-unused-arguments
# Source: https://github.com/nhoad/flake8-unused-arguments
# Make flake8-unused-arguments behave like ruff's "ARG" error code
unused-arguments-ignore-abstract-functions = true
unused-arguments-ignore-stub-functions = true



[tool.isort]  # aligned with [tool.ruff.lint.isort] below
# Source: https://pycqa.github.io/isort/docs/configuration/options.html

known_first_party = ["lalib"]

atomic = true
case_sensitive = true
combine_star = true
force_alphabetical_sort_within_sections = true
lines_after_imports = 2
remove_redundant_aliases = true

# Comply with black's style => Instead of: 'profile = "black"'
# Source: https://pycqa.github.io/isort/docs/configuration/profiles.html
ensure_newline_before_comments = true
force_grid_wrap = 0
include_trailing_comma = true
line_length = 88
multi_line_output = 3
split_on_trailing_comma = true
use_parentheses = true

# Comply with Google's Python style guide
# => All imports go on a single line (with some exceptions)
# Source: https://google.github.io/styleguide/pyguide.html#313-imports-formatting
force_single_line = true
single_line_exclusions = ["collections.abc", "typing"]



[tool.mypy]
# Source: https://mypy.readthedocs.io/en/latest/config_file.html

cache_dir = ".cache/mypy"


[[tool.mypy.overrides]]

module = [
    "nox",
    "pytest",
    "semver",
    "tomli",
    "xdoctest",
]
ignore_missing_imports = true



[tool.pytest.ini_options]
# Source: https://docs.pytest.org/en/stable/

cache_dir = ".cache/pytest"

addopts = "--strict-markers"
console_output_style = "count"



[tool.ruff]
# Source: https://docs.astral.sh/ruff/

cache-dir = ".cache/ruff"

target-version = "py39"  # minimum supported Python version

indent-width = 4
line-length = 88


[tool.ruff.format]

# Align with black
indent-style = "space"
line-ending = "lf"
quote-style = "double"
skip-magic-trailing-comma = false

# Format docstrings as well
docstring-code-format = true
docstring-code-line-length = "dynamic"


[tool.ruff.lint]  # aligned with [tool.flake8] above

select = [

    # violations also covered by `flake8` above

    "ANN",     # flake8-annotations => enforce type checking for functions
    "ARG",     # flake8-unused-arguments => declared function arguments must be used
    "B",       # flake8-bugbear => bugs and design flaws
    "C4",      # flake8-comprehensions => better comprehensions
    "C90",     # mccabe => cyclomatic complexity
    "COM",     # "C8" for flake8-commas => better comma placements
    "D",       # flake8-docstrings / pydocstyle => PEP257 compliance
    "E", "W",  # pycodestyle => PEP8 compliance
    "ERA",     # "E800" for flake8-eradicate / eradicate => no commented out code
    "F",       # pyflakes => basic errors
    "I",       # flake8-isort => isort would make changes
    "N",       # pep8-naming
    "PT",      # flake8-pytest-style => enforce a consistent style with pytest
    "Q",       # flake8-quotes => use double quotes everywhere
    "S",       # flake8-bandit => common security issues
    "TD",      # flake8-todos => unify TODOs
    "T10",     # flake8-debugger => no debugger usage

    # violations not covered by `flake8` above

    "T20",     # flake8-print => forbid `[p]print`

]

ignore = []

extend-ignore = [  # never check the following codes

    "ANN101", "ANN102",  # `self` and `cls` in methods need no annotation

    "ANN401",  # allow dynamically typed expressions with `typing.Any`

    # Comply with black's style
    # Sources: https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#pycodestyle
    "E203", "E701",  # "E704" and "W503" do not exist for `ruff`

]

allowed-confusables = ["ℂ", "ℝ", "ℚ"]


[tool.ruff.lint.flake8-pytest-style]  # aligned with [tool.flake8] above

# Prefer `@pytest.fixture` over `@pytest.fixture()`
fixture-parentheses = false

# Prefer `@pytest.mark.foobar` over `@pytest.mark.foobar()`
mark-parentheses = false

# Prefer `@pytest.mark.parametrize(['param1', 'param2'], [(1, 2), (3, 4)])`
# over `@pytest.mark.parametrize(('param1', 'param2'), ([1, 2], [3, 4]))`
parametrize-names-type = "list"
parametrize-values-row-type = "tuple"
parametrize-values-type = "list"


[tool.ruff.lint.isort]  # aligned with [tool.isort] above

case-sensitive = true
force-single-line = true
single-line-exclusions = ["collections.abc", "typing"]
lines-after-imports = 2
split-on-trailing-comma = true
known-first-party = ["lalib"]


[tool.ruff.lint.per-file-ignores]

# The "docs/" folder is not a package
"docs/conf.py" = ["INP001"]

"tests/*.py" = [  # Linting rules for the test suite:
    "ANN",        # - type hints are not required
    "S101",       # - `assert`s are normal
    "W505",       # - docstrings may be longer than 72 characters
]


[tool.ruff.lint.pycodestyle]

max-doc-length = 72
max-line-length = 99


[tool.ruff.lint.pydocstyle]

convention = "google"



[build-system]

requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"