# Research Project Template Configuration # # This is a comprehensive template for research projects with test-driven development, # automated PDF generation, and professional documentation structure. # # For documentation and usage, see: # - README.md - Project overview and quick start # - docs/RUN_GUIDE.md - Pipeline and command guide # - docs/architecture/two-layer-architecture.md - System design overview # - docs/architecture/thin-orchestrator-summary.md - Thin orchestrator pattern # - docs/usage/examples-showcase.md - Example projects and workflows # # Repository: https://github.com/docxology/template [build-system] requires = ["hatchling>=1.27"] build-backend = "hatchling.build" [project] name = "research-project-template" version = "3.6.0" description = "A comprehensive template for research projects with test-driven development, automated PDF generation, and professional documentation structure" authors = [{ name = "Research Template Team" }] readme = "README.md" requires-python = ">=3.10" keywords = ["research", "template", "academic", "scientific", "documentation", "pdf-generation", "test-driven-development"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Science/Research", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering", "Topic :: Software Development :: Documentation", "Topic :: Text Processing :: Markup", ] dependencies = [ # numpy and matplotlib are core because infrastructure/scientific/ uses them for # benchmarking and figure generation — not optional for projects using this template "numpy>=1.22", "pyyaml>=6.0", # Publication placeholder audits need CommonMark block source maps so only # actual code blocks are excluded from fail-closed rendered-text scanning. "markdown-it-py>=4.0.0", # tomllib was added to the stdlib in Python 3.11; backport for 3.10. "tomli>=2.0; python_version < '3.11'", # typing.NotRequired / datetime.UTC added in Python 3.11; backport for 3.10. "typing-extensions>=4.0; python_version < '3.11'", "pillow>=12.3.0", "matplotlib>=3.7", # scipy, scikit-learn, wordcloud, rdflib are in [scientific] optional group — install with: # uv sync --group scientific # requests is in the [llm] dep group — install with: uv sync --group llm # defusedxml: infrastructure/search/connectors parses XML/Atom feeds from # external APIs (arXiv, etc.) — the repo's XML parser policy # (tests/infra_tests/validation/test_xml_parser_policy.py) requires # defusedxml over stdlib xml.etree.ElementTree to avoid XXE-class issues. "defusedxml>=0.7.1", ] [project.scripts] research-template = "infrastructure.orchestration.cli:main" [project.optional-dependencies] scientific = ["pandas>=2.0.0", "rdflib>=7.0.0", "wordcloud>=1.9.0", "scikit-learn>=1.3.0"] llm = ["requests>=2.31.0"] rendering = ["reportlab>=4.0.0", "pypdf>=6.14.2", "pdfplumber>=0.11.0"] rendering-pptx = ["python-pptx>=1.0.0"] dashboard = ["plotly>=5.0.0"] dotenv = ["python-dotenv>=1.2.2"] steganography = ["cryptography>=48.0.1", "qrcode[pil]>=8.2", "python-barcode>=0.15"] monitoring = ["psutil>=5.9.0"] publishing = ["huggingface-hub>=0.35.0", "twine>=5.0.0"] deep-research = ["google-genai>=1.0", "openai>=1.60", "python-dotenv>=1.2.2"] # Repository development groups below are intentionally separate from the # installable package extras above. ``uv sync`` uses these groups for CI and # contributor tooling; pip users select extras such as ``[rendering]``. [dependency-groups] # Optional scientific computing (used by act_inf_metaanalysis project; not needed for core template) scientific = [ "scipy>=1.10.0", "pandas>=2.0.0", "rdflib>=7.0.0", "wordcloud>=1.9.0", "scikit-learn>=1.3.0", ] # Optional LLM integration (requires running Ollama server and requests HTTP library) llm = [ "requests>=2.31.0", ] # Optional rendering enhancements (lazily imported in manuscript_overview.py and pdf_validator.py) rendering = [ "reportlab>=4.0.0", "pypdf>=6.14.2", # >=6.14.2 fixes CVE-2026-59935/59936/59937/59938 (pip-audit gate) "pdfplumber>=0.11.0", ] # Optional dashboard generation (lazily imported in dashboard_generator.py) dashboard = [ "plotly>=5.0.0", ] # Optional PPTX rendering (infrastructure/rendering/pptx_deck.py; used by # template_pitch_deck). Opt-in — not in default-groups — so `uv sync` at repo # root does not pull in python-pptx for projects that never touch slide decks. rendering-pptx = [ "python-pptx>=1.0.0", ] # Optional .env file loading (used in infrastructure/core/credentials.py, gracefully absent) dotenv = [ "python-dotenv>=1.2.2", ] # Optional barcode generation for steganography module (gracefully absent) steganography = [ "cryptography>=48.0.1", # >=48.0.1 fixes GHSA-537c-gmf6-5ccf (pip-audit gate) "qrcode[pil]>=8.2", "python-barcode>=0.15", ] # Optional resource monitoring (gracefully absent via try/except in logging_utils.py) monitoring = [ "psutil>=5.9.0", ] # Optional publishing clients: huggingface_hub enables Git-LFS uploads of large/binary # artifacts (e.g. PDFs) in infrastructure/publishing/huggingface/adapter.py (the adapter # falls back to its dependency-free raw-HTTP path when this group is absent); twine is # the upload backend infrastructure/publishing/pypi/upload.py shells out to via # `sys.executable -m twine`. publishing = [ "huggingface-hub>=0.35.0", "twine>=5.0.0", ] # DisCoPy (string diagrams for projects/cognitive_case_diagrams; in default-groups so # `uv sync` at repo root matches CI `uv sync --group discopy` and those tests pass locally) discopy = [ "discopy>=1.0.0", # hypothesis: see [dependency-groups] dev (root pytest loads _hypothesis_pytestplugin) ] # Required by the public template_active_inference exemplar's real simulation, # validation-spine, and reproducibility replay tests. Keep this separate from # the reusable Layer-1 package extras: it is a repository development group. active-inference = [ "inferactively-pymdp==1.0.1", ] # Opt-in timeseries forecasting benchmarks (tests/integration/test_timeseries_benchmarks.py) timeseries-bench = [ "statsmodels>=0.14.0", "lightgbm>=4.0.0", "scikit-learn>=1.3.0", # required by lightgbm.sklearn for bench forecasts ] # Development dependencies (replaces deprecated tool.uv.dev-dependencies) dev = [ "hypothesis>=6.151.11", "pre-commit>=4.0.0", "pytest>=7.0", "pytest-cov>=4.0", "pytest-httpserver>=1.0.8", # Local HTTP test server for no-mocks policy "pytest-timeout>=2.0", # Test timeout plugin to prevent hanging tests "mypy>=1.17.1", "pip-audit>=2.0.0", "bandit>=1.7.0", "pytest-asyncio>=1.3.0", "pytest-benchmark>=5.0.0", # Opt-in performance benches under tests/infra_tests/benchmark/ (see MED6) "iniconfig>=2.3.0", "types-requests>=2.32.4.20260107", "types-pyyaml>=6.0.12.20250915", "ruff>=0.15.21", # Code-health scanner (Python 3.11+ only; template still supports 3.10 for projects) "desloppify @ git+https://github.com/peteromallet/desloppify.git; python_version >= '3.11'", "pytest-xdist>=3.5", ] deep-research = [ "google-genai>=1.0", "openai>=1.60", # Loads the repo .env (OPENAI_API_KEY / GEMINI_API_KEY) so `providers`, # `submit`, and `run-project` see credentials without a manual `export`. # config.ensure_dotenv_loaded() also has a built-in fallback parser, so the # package still finds keys if this extra is somehow absent. "python-dotenv>=1.2.2", ] # Deterministic runtime and test dependencies for every canonical public # exemplar. Network retrieval, model clients, and publishing remain separate # opt-in groups; this group is safe for a fresh offline local readiness run. public-exemplars = [ {include-group = "scientific"}, {include-group = "llm"}, {include-group = "monitoring"}, {include-group = "rendering-pptx"}, "bibtexparser>=1.4.0", "fuzzywuzzy>=0.18.0", "jsonschema>=4.0.0", "jinja2>=3.1.0", "networkx>=3.0", "python-dateutil>=2.8.0", "python-levenshtein>=0.21.0", "seaborn>=0.12.0", "tqdm>=4.65.0", "urllib3>=2.0.0", ] [tool.uv] # uv-specific configuration managed = true # Use uv's virtual environment management package = true # Install Layer 1 and the research-template console entry point default-groups = [ "dev", "rendering", "discopy", "steganography", "active-inference", "public-exemplars", ] # pip-api (via pip-audit) pins an older pip; keep the installer above its # published security floor as well as the runtime dependency set. override-dependencies = [ "pip>=26.1.2", "msgpack>=1.2.1", ] [tool.uv.workspace] # Workspace configuration for multi-project support # Supports both standalone projects and nested projects within program directories members = [] exclude = [ "projects/*/output", "projects/*/*/output", "output" ] [tool.hatch.build.targets.wheel] packages = ["infrastructure"] [tool.hatch.build.targets.sdist] include = [ "/infrastructure", "/README.md", "/LICENSE", "/pyproject.toml", ] [tool.pytest.ini_options] # Pytest configuration (consolidated from pytest.ini) # projects/templates/template_code_project/src is the canonical example project; add other project src/ paths here if needed pythonpath = [".", "infrastructure", "projects/templates/template_code_project/src"] testpaths = ["tests", "tests/integration"] python_files = ["test_*.py"] python_classes = ["Test*"] python_functions = ["test_*"] addopts = "--tb=short --strict-markers -W ignore::pytest.PytestDeprecationWarning --durations=10" timeout = 10 # Timeout individual tests after 10 seconds to prevent hanging timeout_method = "thread" # Use thread-based timeout (safer for network operations) markers = [ "requires_ollama: requires running Ollama server (auto-started first; FAILS with setup guidance if unavailable, by design). Opt out: -m 'not requires_ollama'. See docs/development/optional-dependencies.md", "requires_latex: requires a LaTeX engine (xelatex/pdflatex); SKIPS via skip_if_no_latex when absent. Opt out: -m 'not requires_latex'. See docs/development/optional-dependencies.md", "slow: mark test as slow running", "integration: mark test as integration test", "no_patch_llm_client: marks tests that need real default LLMClient behavior (opts out of autouse fixture)", "bench: performance benchmark tests", "private_project: tests for private sidecar project tooling", "external_fixture: tests requiring downloaded external fixture trees", ] # pytest-asyncio configuration asyncio_mode = "strict" asyncio_default_fixture_loop_scope = "function" # Filter specific warnings that may still appear # These warnings come from pytest plugins and need explicit filtering filterwarnings = [ "ignore::DeprecationWarning:pytest_asyncio", "ignore::PendingDeprecationWarning:pytest_asyncio", "ignore::pytest.PytestDeprecationWarning", "ignore::UserWarning", "ignore::FutureWarning", "ignore:The configuration option.*asyncio_default_fixture_loop_scope.*is unset", ] [tool.coverage.run] # Coverage configuration (consolidated from .coveragerc) # Note: Test runners use --cov-datafile flags for isolation: # - Infrastructure tests: --cov-datafile=.coverage.infra # - Project tests: --cov-datafile=.coverage.project branch = true source = ["infrastructure", "projects/templates/template_code_project/src"] omit = [ "*/tests/*", "*/test_*", "*/__pycache__/*", "*/tmp/*", "*/temp/*", "*/site-packages/*", "projects/templates/template_code_project/src/analysis.py", "projects/templates/template_code_project/src/dashboard.py", # figures.py is the figure-generator extraction split out of analysis.py; # the 6 generate_* functions are exercised only through # scripts/optimization_analysis.py (end-to-end pipeline run), not in the # gate-time unit test suite. "projects/templates/template_code_project/src/figures.py", "/private/var/folders/*", ] [tool.coverage.report] # fail_under = 70 is a global fallback setting # Individual test runs use 60% (infrastructure) and 90% (project) thresholds # See AGENTS.md#coverage-system and docs/BUILD_SYSTEM.md for details fail_under = 70 show_missing = true precision = 2 exclude_lines = [ "pragma: no cover", "def __repr__", "if self.debug:", "if settings.DEBUG", "raise AssertionError", "raise NotImplementedError", "if 0:", "if __name__ == .__main__.:", "class .*\\bProtocol\\):", "@(abc\\.)?abstractmethod", ] # ============================================================================ # Ruff Linter Configuration (v2.6.0) # ============================================================================ [tool.ruff] target-version = "py310" line-length = 120 [tool.ruff.format] exclude = ["docs/reference/api-reference.md"] [tool.ruff.lint] select = ["E", "F"] ignore = [ "E741", # Allow short math variable names in scientific code (l, I, O) ] [tool.ruff.lint.per-file-ignores] "infrastructure/core/logging/*.py" = ["E402"] # Conditional imports after setup "infrastructure/publishing/publish_cli.py" = ["E402"] # sys.path manipulation before imports "scripts/*" = ["E402", "E501"] # sys.path before imports; docstring line length "projects/*/scripts/**/*.py" = ["E402", "E501"] # Path bootstrap before imports; long CLI strings "projects/*/*/scripts/**/*.py" = ["E402", "E501", "N999"] # Nested public exemplar stage scripts "projects/templates/*/scripts/*.py" = ["E402", "E501", "N999"] # Direct files in nested script dirs "projects/templates/template_eda_notebook/notebooks/*.ipynb" = ["E402"] # Notebook path bootstrap cell "tests/**/*.py" = ["E712", "E402", "E501"] # Assertions, conditional imports, long fixture payloads "projects/*/*/tests/**/*.py" = ["E712", "E402", "E501"] # templates//tests: sys.path bootstrap before imports, long fixture payloads "__init__.py" = ["F401"] # Allow re-exports in __init__ files # E501 in docstrings/comments (ruff format handles code structure line length) "infrastructure/__init__.py" = ["E501"] # Module inventory docstring "infrastructure/documentation/counts_doc.py" = ["E501"] # Long markdown prose lines in the COUNTS.md template literal "projects/templates/template_search_project/src/__init__.py" = ["E402"] # Deferred exports after public __all__ "infrastructure/core/runtime/checkpoint.py" = ["E501"] "infrastructure/core/errors.py" = ["E501"] "infrastructure/core/exceptions.py" = ["E501"] "infrastructure/core/files/*.py" = ["E501"] "infrastructure/core/pipeline/*.py" = ["E501"] "infrastructure/llm/*" = ["E501"] "infrastructure/project/discovery.py" = ["E501"] "infrastructure/publishing/citations.py" = ["E501"] "infrastructure/rendering/slides_renderer.py" = ["E501"] "infrastructure/reporting/*" = ["E501"] "infrastructure/scientific/templates.py" = ["E501"] "infrastructure/validation/content/*.py" = ["E501"] "projects/*/src/**" = ["E501"] ".agents/skills/**/*.py" = ["E501", "F841"] # ============================================================================ # mypy Type Checking Configuration (v2.7.0) # ============================================================================ [tool.mypy] python_version = "3.12" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = false # Will tighten per-module as types are narrowed check_untyped_defs = true no_implicit_optional = true warn_redundant_casts = true warn_unused_ignores = true ignore_missing_imports = true explicit_package_bases = true # Per-module strict overrides (progressively tightened) [[tool.mypy.overrides]] module = "infrastructure.core.exceptions" disallow_untyped_defs = true # v2.13.0: Strict overrides for rendering package [[tool.mypy.overrides]] module = "infrastructure.rendering.*" disallow_untyped_defs = true [tool.bandit] # Exclusions for paths (mirrors `exclude_dirs` in `bandit.yaml`). CI and # pre-commit use `-c bandit.yaml`; this block keeps ad-hoc `bandit -c pyproject.toml` # runs aligned. exclude_dirs = [ "projects/working", "projects/published", "projects/archive", "projects/other", ".venv", "site-packages", "infrastructure/steganography/kmyth", ] # B101: assert_used — asserts are used as internal invariants in infrastructure code. # Suppressing B101 reduces noise without compromising security review of real issues. skips = ["B101"]