[project] name = "jentic-one" version = "0.44.0" description = "Jentic One" readme = "README.md" requires-python = ">=3.12" license = { text = "Apache-2.0" } authors = [{ name = "Jentic" }] dependencies = [ "aiosqlite>=0.20.0", "alembic>=1.16.0", "argon2-cffi>=25.1.0", "asyncpg>=0.31.0", "fastapi>=0.138.0", "httpx>=0.28.1", "jentic-problem-details>=0.1.0", "opentelemetry-api>=1.20", "opentelemetry-exporter-otlp-proto-grpc>=1.20", "opentelemetry-exporter-prometheus>=0.44b0", "opentelemetry-instrumentation-fastapi>=0.65b0", "opentelemetry-instrumentation-sqlalchemy>=0.44b0", "opentelemetry-sdk>=1.20", "prometheus-client>=0.20.0", "pydantic>=2.13.4", "pyjwt[crypto]>=2.13.0", "pyyaml>=6.0.3", "sqlalchemy[asyncio]>=2.1.5", "structlog>=24.1", "uvicorn[standard]>=0.49.0", "starlette>=1.3.1", "cryptography>=50.0.0", "h2>=4.4.1", "opentelemetry-instrumentation-httpx>=0.44b0", "python-multipart>=0.0.32", "mcp>=2.1.1", "publicsuffixlist>=1.0.2", # Not imported by jentic_one: httpcore's current_async_library() does # `try: import sniffio` on every lock/event/shield it constructs (several # per outbound request). anyio>=4 no longer pulls sniffio in, and a failed # import is not cached, so without it each call stats every sys.path entry. "sniffio>=1.3.1", "certifi>=2026.10.19", ] [project.optional-dependencies] redis = [ "redis>=8.0.0", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/jentic_one"] # The ui/dist placeholder (.gitkeep, kept so a clean checkout has the dir for # force-include) is build scaffolding, not a runtime asset — keep it out of the wheel. # (mockServiceWorker.js — the MSW dev mock Vite copies into dist — is kept out of # the prod bundle at build time by a Vite plugin; hatchling `exclude` does not # apply to force-included paths, so it can't be dropped here.) exclude = ["**/.gitkeep"] # Bundle OpenAPI specs and the built UI inside the package so they're reachable # via importlib.resources.files("jentic_one") / "openapi" and / "static". [tool.hatch.build.targets.wheel.force-include] "openapi" = "jentic_one/openapi" "ui/dist" = "jentic_one/static" [tool.hatch.build.targets.sdist] include = [ "src/jentic_one", "openapi", "README.md", "pyproject.toml", ] [project.scripts] jentic-one = "jentic_one.__main__:main" [dependency-groups] dev = [ "ruff>=0.15.18", "mypy>=1.11.0", "pytest>=9.1.1", "pytest-cov>=5.0.0", "pytest-asyncio>=1.4.0", "types-pyyaml>=6.0.12.20260518", "openapi-spec-validator>=0.9.0", "lefthook>=2.1.8", "detect-secrets>=1.5.0", "commitizen>=4.16.4", "click>=8.1", "respx>=0.21", # redis is an optional runtime extra (see [project.optional-dependencies]); # the dev env always installs it so the state-backend unit tests (which build # the redis backend) run without needing the extra enabled explicitly. "redis>=8.0.0", "jsonschema>=4.26.0", "types-jsonschema>=4.26.0.20261006", "pytest-xdist>=3.8.0", "filelock>=3.16", ] # Conventional-commit enforcement (pure Python — no Node needed). Fires for # every contributor via the commit-msg hook regardless of whether they touch # the UI. Mandatory scope per .cursor/rules/git-conventions.mdc: the scope # group `\(\S+\)` has no trailing `?`, so `fix: subject` (no scope) is rejected # while `fix(scope): subject` passes. [tool.commitizen] name = "cz_customize" [tool.commitizen.customize] schema = "(): " schema_pattern = "(?s)(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\(\\S+\\))!?: ([^\\n\\r]+)((\\n\\n.*)|(\\s*))?$" example = "feat(ui): add SPA static serving to the admin surface" info = "Commits must be conventional with a mandatory scope: type(scope): subject" [tool.ruff] line-length = 100 target-version = "py312" src = ["src", "tests"] [tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "I", # isort "B", # flake8-bugbear "C4", # flake8-comprehensions "UP", # pyupgrade "N", # pep8-naming "SIM", # flake8-simplify "RUF", # ruff-specific ] ignore = [] [tool.ruff.lint.per-file-ignores] "tests/**" = ["S101"] "src/jentic_one/**/web/**" = ["B008"] [tool.ruff.format] quote-style = "double" indent-style = "space" [tool.mypy] python_version = "3.12" strict = true warn_unused_configs = true warn_redundant_casts = true warn_unused_ignores = true disallow_untyped_defs = true disallow_incomplete_defs = true check_untyped_defs = true no_implicit_optional = true files = ["src", "tests"] [[tool.mypy.overrides]] module = "tests.*" disallow_untyped_defs = false disallow_incomplete_defs = false [[tool.mypy.overrides]] module = "asyncpg.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "alembic.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "openapi_spec_validator.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "uvicorn.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "structlog.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "publicsuffixlist.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "opentelemetry.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "prometheus_client.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "jentic.problem_details.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "cryptography.*" ignore_missing_imports = true # The perf suite's locustfile runs only inside the locustio/locust image; locust # is deliberately not a project dependency (it pins gevent), so it's untyped here. [[tool.mypy.overrides]] module = "locust.*" ignore_missing_imports = true [[tool.mypy.overrides]] module = "tests.perf.locustfile" disallow_subclassing_any = false disallow_untyped_decorators = false [tool.pytest.ini_options] minversion = "8.0" testpaths = ["tests"] addopts = [ "-ra", "--strict-markers", "--strict-config", ] markers = [ "integration: marks tests requiring running database fixtures", "arch: architecture enforcement tests (no external services required)", "smoke: lightweight liveness checks against a deployed environment", ] asyncio_mode = "auto" # A SQLAlchemy deprecation warning fails the test that triggers it: each one # walks the stack on every call, so a deprecated API on a hot path is a silent # per-request CPU cost (#1820). This entry raises at the call site during # collection and session/module fixture setup; inside each test the # `tests/harness/sqlalchemy_deprecations.py` recorder takes over, so a broad # `except Exception` on the path cannot swallow it. filterwarnings = [ "error::sqlalchemy.exc.SADeprecationWarning", ] [tool.bandit] exclude_dirs = ["tests"] skips = ["B101"] [tool.coverage.run] source = ["src/jentic_one"] branch = true omit = ["src/jentic_one/migrations/env.py", "src/jentic_one/*/repos/*.py", "src/jentic_one/admin/services/*.py", "src/jentic_one/admin/services/_support/*.py", "src/jentic_one/auth/services/*.py"] [tool.coverage.report] show_missing = true skip_covered = false fail_under = 70 [tool.uv] override-dependencies = ["torch>=2.0"] [tool.uv.sources] torch = [{ index = "pytorch-cpu" }] [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true