[build-system]
requires = [
    "hatchling",
    # 0.9.3 disables editable mode for hatch, forked it to allow editable mode
    "scikit-build-core @ git+https://github.com/atopile/scikit-build-core.git@feature/allow_editable",
    "nanobind>=2.6.1,<3.0",
    "hatch-vcs",
    "ziglang==0.14.1",
]
build-backend = "hatchling.build"

[project]
name = "atopile"
description = "A toolchain bringing the best of software development to the real-world!"
readme = "README.md"
requires-python = ">=3.14,<3.15"
dynamic = ["version"]
license = { file = "LICENSE" }
authors = [
    { name = "Ioannis Papamanoglou", email = "ioannis@atopile.io" },
    { name = "Narayan Powderly", email = "narayan@atopile.io" },
]
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]
dependencies = [
    "antlr4-python3-runtime==4.13.2", # Strict version because the generated (but versioned) files must match
    "atopile-easyeda2kicad>=0.9.7",
    "black>=24.4.2",
    "case-converter>=1.2.0",
    "cookiecutter>=2.6.0",
    "dataclasses-json>=0.6.7",
    "deprecated~=1.2.14",
    "fastapi>=0.110.0",
    "fastapi-github-oidc>=0.2.1",
    "freetype-py>=2.4",
    "gitpython>=3.1.41",
    "httpx>=0.28.1",
    "jinja2>=3.1.6",
    "atopile-kicad-python>=0.5.1",
    "kicadcliwrapper>=1.1.1",
    "matplotlib>=3.7.1",
    "mcp[cli]>=1.10.1",
    "more-itertools>=10.4",
    "nanobind>=2.6.1",
    "natsort>=8.4.0",
    "numpy>=2.2.0",
    "ordered-set==4.1.0",
    "pathvalidate>=3.2.1",
    "platformdirs>=4.3.6",
    "posthog==7.8.0",                 # exact version
    "prompt-toolkit==3.0.51",         # pinned to avoid questionary compatibility issues
    "psutil>=6.0.0",
    "pydantic-settings>=2.7.1",
    "pytest>=9.0.0,<10.0.0",          # needed for imports
    "pygls>=2.0.0,<3.0.0",
    "questionary>=2.0.1",
    "rich>=13.7.1",
    "ruamel.yaml>=0.18.5",
    "ruff>=0.11.0",
    "semver>=3.0.2",
    "sexpdata>=1.0.2",
    "shapely>=2.0.1",
    "truststore>=0.10.4",
    "typer>=0.12",
    "typing-extensions>=4.6.3",
    "urllib3>=2.2.1",
    "uvicorn>=0.29.0",
    "watchdog>=4.0.0",
    "websockets>=12.0",
    "zstd>=1.5.6.7",
]

[project.urls]
Homepage = "https://github.com/atopile/atopile"
"Bug Tracker" = "https://github.com/atopile/atopile/issues"


[dependency-groups]
dev = [
    "hypothesis>=6.124.7",
    "antlr4-tools>=0.2.1",
    "coverage>=7.6.8",
    "debugpy>=1.8.11",
    "gprof2dot~=2024.6.6",
    "mkdocs-material>=9.5.49",
    "mkdocs>=1.6.1",
    "nanobind>=2.2,<3.0",
    "pre-commit>=2.20,<4.0",
    "viztracer>=1.0.0,<2.0.0",
    "pytest-timeout>=2.3.1",
    "mkdocstrings[python]>=0.27.0",
    "griffe-typingdoc>=0.2.7",
    "mike>=2.1.3",
    "hatch>=1.14.0",
    "atopile-mkdocs-plugin",
    "ziglang==0.14.1",
    "graphviz>=0.21",
    "py-spy>=0.4.1",
    "pydantic-to-typescript>=2.0.0", # Generate TypeScript types from Pydantic models
    #"austin-dist>=4.0.0",
    #"austin-python>=2.1.2",
]


[project.scripts]
ato = "atopile.cli.cli:main"
faebryk = "faebryk.tools.main:__main__"

[project.entry-points."pygments.lexers"]
atopile_lexer = "atopile.compiler.parse_utils:PygmentsLexer"

[tool.hatch]

[tool.hatch.metadata]
allow-direct-references = true

# Add the built viewer and dashboard to the python package
[tool.hatch.build.targets.wheel]
artifacts = [
    "**/viewer/dist",
    "**/visualizer/web/dist",
    "**/dashboard/web/dist",
]
packages = ["src/faebryk", "src/atopile"]
exclude = ["**/.zig-cache", "**/.zig-cache/**", "**/zig-out", "**/zig-out/**"]

[tool.hatch.build.targets.wheel.hooks.scikit-build]
experimental = true
cmake.source-dir = "src/faebryk/core"

[tool.hatch.build.targets.sdist]
artifacts = [
    "**/viewer/dist",
    "**/visualizer/web/dist",
    "**/dashboard/web/dist",
]

[tool.hatch.version]
source = "vcs"

[tool.hatch.version.raw-options]
# Passed through to setuptools-scm
# See: https://setuptools-scm.readthedocs.io/en/latest/config/
# https://setuptools-scm.readthedocs.io/en/latest/extending/
version_scheme = "no-guess-dev"
local_scheme = "node-and-timestamp"

[tool.pytest]
[tool.pytest.ini_options]
addopts = [
    "--import-mode=importlib",
    "--ignore=test/runtest.py",
    "--ignore=test/conftest.py",
    # Ensure co-located tests in `src/` are imported by package name, not by path.
    "-p",
    "atopile.pytest_import_by_name",
]
testpaths = ["test", "src"]
# Don't treat every `src/**/*.py` file as a test module. Co-located tests are discovered
# via `atopile.pytest_import_by_name` instead.
python_files = ["test_*.py", "*_test.py", "*.py"]
python_functions = ["test_*"]

# Exclude problematic directories from test discovery
norecursedirs = [
    "templates",
    "project-template",
    "build",
    "dist",
    "node_modules",
    ".git",
    ".zig-cache",
    "__pycache__",
    "lsp",
    "kicad_plugin",
    "project-template",
    "resources",
    ".hypothesis",
]
log_file_format = "%(asctime)s %(levelname)s %(name)s %(message)s"
# log_file_level = "DEBUG"  # <-- you can set this in a PR if you want to debug tests in CI
markers = [
    "slow: marks tests as slow (deselect with '-m \"not slow\"')",
    "not_in_ci: marks tests that should not be run in CI",
    "regression: marks tests that are used for deeper regression testing. May be slow and flaky.",
    "smoke: marks quick tests that are used to check for basline functionality.",
]

[tool.ruff]
line-length = 88         # Same as Black.
target-version = "py313"

exclude = [
    "src/atopile/compiler/parser/AtoLexer.py",
    "src/atopile/compiler/parser/AtoLexerBase.py",
    "src/atopile/compiler/parser/AtoParser.py",
    "src/atopile/compiler/parser/AtoParserBase.py",
    "src/atopile/compiler/parser/AtoParserListener.py",
    "src/atopile/compiler/parser/AtoParserVisitor.py",
    "docs/",
    "sandbox/",
]

[tool.ruff.format]
exclude = ["_F.py"]

[tool.ruff.lint]
# Enable recommended set of rules
select = [
    "E",    # pycodestyle errors
    "W",    # pycodestyle warnings
    "F",    # Pyflakes
    "I001", # isort
    #"B",    # flake8-bugbear
    #"C4",   # flake8-comprehensions
    #"PL",   # Pylint
    #"RUF",  # Ruff-specific rules
    #"UP",   # pyupgrade
    #"N",    # pep8-naming
    #"SIM",  # flake8-simplify
    #"TCH",  # flake8-type-checking
    #"ARG",  # flake8-unused-arguments
    #"PTH",  # flake8-use-pathlib
]

# Allow autofix for all enabled rules (when `--fix`) is provided.
fixable = [
    "A",   # flake8-builtins
    "B",   # flake8-bugbear
    "C",   # flake8-comprehensions
    "D",   # pydocstyle
    "E",   # pycodestyle errors
    "F",   # Pyflakes
    "G",   # flake8-logging-format
    "I",   # isort
    "N",   # pep8-naming
    "Q",   # flake8-quotes
    "S",   # flake8-bandit
    "T",   # flake8-print
    "W",   # pycodestyle warnings
    "ANN", # flake8-annotations
    "ARG", # flake8-unused-arguments
    "BLE", # flake8-blind-except
    "COM", # flake8-commas
    "DJ",  # flake8-django
    "DTZ", # flake8-datetimez
    "EM",  # flake8-errmsg
    "ERA", # eradicate
    "EXE", # flake8-executable
    "FBT", # flake8-boolean-trap
    "ICN", # flake8-import-conventions
    "INP", # flake8-no-pep420
    "ISC", # flake8-implicit-str-concat
    "NPY", # NumPy-specific rules
    "PD",  # pandas-vet
    "PGH", # pygrep-hooks
    "PIE", # flake8-pie
    "PL",  # Pylint
    "PT",  # flake8-pytest-style
    "PTH", # flake8-use-pathlib
    "PYI", # flake8-pyi
    "RET", # flake8-return
    "RSE", # flake8-raise
    "RUF", # Ruff-specific rules
    "SIM", # flake8-simplify
    "SLF", # flake8-self
    "TCH", # flake8-type-checking
    "TID", # flake8-tidy-imports
    "TRY", # tryceratops
    "UP",  # pyupgrade
    "YTT", # flake8-2020
]
unfixable = []

# Exclude a variety of commonly ignored directories.
exclude = [
    ".bzr",
    ".direnv",
    ".eggs",
    ".git",
    ".git-rewrite",
    ".hg",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".pytype",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "venv",
]
per-file-ignores = {}

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

[tool.ty.rules]
invalid-method-override = "ignore"

[tool.pyright]
typeCheckingMode = "standard"
reportIncompatibleMethodOverride = false

[tool.black]
line-length = 88
target-version = ['py313']
extend-exclude = "atopile/parser/"

[tool.uv.sources]
atopile-mkdocs-plugin = { path = "tools/atopile_mkdocs_plugin", editable = true }

[tool.uv]
cache-keys = [{ git = { commit = false, tags = true } }]
