[build-system]
requires = ["hatchling", "babel", "setuptools >= 50.0.0; python_version<'3.13'"]
build-backend = "hatchling.build"

[project]
name = "Canaille"
version = "0.0.56"
description = "Lightweight identity and authorization management software"
license = {file = "LICENSE.rst"}
readme = "README.md"
keywords = ["oidc", "oauth", "oauth2", "openid", "identity", "authentication"]
classifiers = [
    "Intended Audience :: Developers",
    "Development Status :: 3 - Alpha",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Programming Language :: Python :: Implementation :: CPython",
    "License :: OSI Approved :: MIT License",
    "Environment :: Web Environment",
    "Programming Language :: Python",
    "Operating System :: OS Independent",
    "Topic :: System :: Systems Administration :: Authentication/Directory",
    "Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP",

]
authors = [{name="Yaal Coop", email="contact@yaal.coop"}]
maintainers = [{name="Éloi Rivard", email="eloi@yaal.coop"}]

requires-python = ">=3.10"
dependencies = [
    "flask >= 3.0.0",
    "flask-wtf >= 1.2.1",
    "httpx>=0.28.1",
    "pydantic-settings >= 2.0.3",
    "requests>=2.32.3",
    "wtforms >= 3.1.1",
]

[project.optional-dependencies]
front = [
    "email_validator >= 2.0.0",
    "flask-babel >= 4.0.0",
    "flask-themer >= 2.0.0",
    "pycountry >= 23.12.7",
    "pytz >= 2022.7",
    "toml >= 0.10.0",
    "zxcvbn-rs-py >= 0.2.0",
]

oidc = [
    "authlib >= 1.3.0",
]

scim = [
    "scim2-models>=0.2.2",
    "authlib >= 1.3.0",
]

ldap = [
    "python-ldap >= 3.4.0",
]

sentry = [
    "sentry-sdk >= 2.0.0",
]

sqlite = [
    "passlib >= 1.7.4",
    "sqlalchemy >= 2.0.23",
    "sqlalchemy-json >= 0.7.0",
    "sqlalchemy-utils >= 0.41.1",
]

postgresql = [
    "passlib >= 1.7.4",
    "sqlalchemy[postgresql-psycopg2binary] >= 2.0.23",
    "sqlalchemy-json >= 0.7.0",
    "sqlalchemy-utils >= 0.41.1",
]

mysql = [
    "passlib >= 1.7.4",
    "sqlalchemy[mysql-connector] >= 2.0.23",
    "sqlalchemy-json >= 0.7.0",
    "sqlalchemy-utils >= 0.41.1",
]

otp = [
    "otpauth>=2.1.1",
    "pillow>=11.0.0",
    "qrcode>=8.0",
]

sms = [
    "smpplib>=2.2.3",
]

[project.urls]
homepage = "https://canaille.yaal.coop"
documentation = "https://canaille.readthedocs.io/en/latest/"
repository = "https://gitlab.com/yaal/canaille"

[dependency-groups]
dev = [
    "babel >= 2.14.0",
    "coverage[toml] >= 6.0.0",
    "faker >= 30.0.0",
    "flask-webtest >= 0.1.6",
    # pre-commit version is fixed until docformatted hook is compatible
    # https://github.com/PyCQA/docformatter/issues/293
    "pre-commit < 4.0.0",
    "pre-commit-uv>=4.1.4",
    "pyquery >= 2.0.0",
    "pytest >= 8.0.0",
    "pytest-cov >= 6.0.0",
    "pytest-httpserver >= 1.1.0",
    "pytest-lazy-fixtures >= 1.0.7",
    "pytest-smtpd >= 0.1.0",
    "pytest-xdist >= 3.3.1",
    "scim2-tester>=0.1.7",
    "slapd >= 0.1.5",
    "time-machine >= 2.14.1",
    "toml >= 0.10.0",
    "tox-uv >= 1.16.0",
    # Babel 2.14 does not directly depend on setuptools
    # https://github.com/python-babel/babel/blob/40e60a1f6cf178d9f57fcc14f157ea1b2ab77361/CHANGES.rst?plain=1#L22-L24
    # and neither python 3.12 due to PEP 632
    # https://peps.python.org/pep-0632/
    "setuptools >= 50.0.0; python_version>='3.12'",
]
doc = [
    "autodoc-pydantic >= 2.0.1",
    "shibuya >= 2024.3.1",
    "sphinx >= 7.0.0",
    "sphinx-design >= 0.6.0",
    "sphinx-issues >= 5.0.0",
    "sphinx-click >= 6.0.0",
    "sphinx-intl>=2.3.0",
]
demo = [
    "faker",
    "honcho",
    "slapd",
    "requests",
    "watchdog >= 4.0.0",
]

[project.scripts]
canaille = "canaille.commands:cli"

[options.packages.find]
exclude = [
    "tests",
    "tests.*",
    "doc",
    "doc.*",
]

[tool.hatch.build]
include = [
    "canaille/",
    "doc/",
    "tests/",
    "CHANGES.rst",
    "CONTRIBUTING.rst",
    "README.md",
]
exclude = [
    "docs/_build/",
]
artifacts = ["canaille/translations/*/LC_MESSAGES/*.mo"]

[tool.hatch.build.hooks.custom]
dependencies = [
    "Babel>=2.6.0",
    "setuptools >= 50.0.0; python_version>='3.12'",
]

[tool.coverage.run]
source = [
    "canaille",
    "tests",
]
omit = [".tox/*"]
branch = true

[tool.coverage.report]
exclude_lines = [
    "@pytest.mark.skip",
    "pragma: no cover",
    "raise NotImplementedError",
    "except ImportError",
    "if app.debug",
]

[tool.ruff.lint]
select = [
    "B", # flake8-bugbear
    "E", # pycodestyle
    "F", # pyflakes
    "I", # isort
    "UP", # pyupgrade
]
ignore = [
    "E501", # line-too-long
    "E722", # bare-except
]

[tool.ruff.lint.isort]
force-single-line = true

[tool.ruff.format]
docstring-code-format = true

[tool.tox]
requires = ["tox>=4.19"]
env_list = [
    "style",
    "py310",
    "py311",
    "py312",
    "py313",
    "minversions",
    "doc",
    "coverage",
]

[tool.tox.env_run_base]
runner = "uv-venv-lock-runner"
dependency_groups = ["dev"]
uv_sync_flags = ["--extra", "front", "--extra", "oidc", "--extra", "sqlite", "--extra", "ldap"]
commands = [
    ["pytest", "--showlocals", "--full-trace", "{posargs}"],
]

[tool.tox.env.style]
skip_install = true
runner = "uv-venv-runner"
commands = [
    ["pre-commit", "run", "--all-files", "--show-diff-on-failure"],
]

[tool.tox.env.minversions]
uv_resolution = "lowest-direct"
basepython = ["python3.10"]

[tool.tox.env.doc]
dependency_groups = ["doc"]
commands = [
    ["sphinx-build", "--builder", "html", "--fail-on-warning", "doc", "build/sphinx/html"],
    ["sphinx-build", "--builder", "man", "doc", "build/sphinx/html"],
]

[tool.tox.env.coverage]
commands = [
    ["pytest", "--cov", "--cov-fail-under=100", "--cov-report", "term:skip-covered", "{posargs}"],
    ["coverage", "html"],
]

[[tool.babel.mappings]]
method = "python"
pattern = "**.py"

[[tool.babel.mappings]]
method = "jinja2"
pattern = "**/templates/**.html"
trimmed = "true"

[[tool.babel.mappings]]
method = "jinja2"
pattern = "**/themes/**.html"
trimmed = "true"

[[tool.babel.mappings]]
method = "jinja2"
pattern = "**/templates/**.txt"
trimmed = "true"
