[project]
name = "crystal_toolkit"
dynamic = ["version"]
description = """Crystal Toolkit is a web app framework from the Materials Project allowing Python
developers to easily make an interactive web app to display materials science information. As a
showcase of the framework’s capabilities, the Crystal Toolkit app allows you to import, view,
analyze and transform crystal structures and molecules."""
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.10"
authors = [{ name = "Matt Horton", email = "mkhorton@lbl.gov" }]

dependencies = [
  "dash-mp-components>=0.4.38",
  "dash>=2.11.0",
  "flask-caching",
  "frozendict",
  "mp-api",
  "pydantic-settings",
  "pymatgen>=2024.10.22",
  "scikit-image",
  "scikit-learn",
  "shapely",
  "webcolors",
]

[project.optional-dependencies]
server = ["dash-extensions", "gunicorn[gevent]", "habanero", "hiphive", "redis"]
robocrys = ["robocrys"]
temdiff = ["py4DSTEM>=0.13.11"]
fermi = ["ifermi", "pyfftw"]
phonon = ["phonopy"]
vtk = ["dash-vtk"]
localenv = ["dscribe"]
figures = ["kaleido"]
dev = [
  "black",
  "dash[testing]",
  "dephell",
  "jinja2",
  "pre-commit",
  "recommonmark",
  "sphinx_rtd_theme",
]
test = [
  "playwright",
  "pytest",
  "pytest-playwright",
  "pytest-cov",
  "selenium<4",
  "urllib3<2",
]
jupyterlab_deprecated = ["crystaltoolkit-extension"]

[project.urls]
repo = "https://github.com/materialsproject/crystaltoolkit"
docs = "https://docs.crystaltoolkit.org"

[tool.setuptools.packages.find]
exclude = ["docs_rst"]

[tool.setuptools_scm]
write_to = "crystal_toolkit/_version.py"
write_to_template = '__version__ = "{version}"'
local_scheme = "no-local-version"

[build-system]
requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2,<8"]
build-backend = "setuptools.build_meta"

[tool.ruff]
target-version = "py38"

[tool.ruff.lint]
select = [
  "B",    # flake8-bugbear
  "C4",   # flake8-comprehensions
  "D",    # pydocstyle
  "E",    # pycodestyle error
  "EXE",  # flake8-executable
  "F",    # pyflakes
  "FA",   # flake8-future-annotations
  "FLY",  # flynt
  "I",    # isort
  "ICN",  # flake8-import-conventions
  "ISC",  # flake8-implicit-str-concat
  "PD",   # pandas-vet
  "PERF", # perflint
  "PIE",  # flake8-pie
  "PL",   # pylint
  "PT",   # flake8-pytest-style
  "PYI",  # flakes8-pyi
  "Q",    # flake8-quotes
  "RET",  # flake8-return
  "RSE",  # flake8-raise
  "RUF",  # Ruff-specific rules
  "SIM",  # flake8-simplify
  "SLOT", # flake8-slots
  "TCH",  # flake8-type-checking
  "TID",  # tidy imports
  "TID",  # flake8-tidy-imports
  "UP",   # pyupgrade
  "W",    # pycodestyle warning
  "YTT",  # flake8-2020
]
ignore = [
  "B028",    # No explicit stacklevel keyword argument found
  "C408",    # Unnecessary dict call - rewrite as a literal
  "D100",    # Missing docstring in public module
  "D104",    # Missing docstring in public package
  "D105",    # Missing docstring in magic method
  "D107",    # Missing docstring in __init__
  "D205",    # 1 blank line required between summary line and description
  "E501",    # Line too long
  "E731",    # Do not assign a lambda expression, use a def
  "ISC001",
  "PERF203", # try-except-in-loop
  "PLR",     # pylint refactor
  "SIM105",  # Use contextlib.suppress(ValueError) instead of try-except-pass
]
pydocstyle.convention = "google"
isort.split-on-trailing-comma = false

[tool.ruff.lint.per-file-ignores]
"**/tests/*" = ["D"]
"__init__.py" = ["F401"]
# future annotations incompatible with pydantic BaseModel schemas below py 3.10
# TODO remove once python 3.10 is the minimum supported version
"crystal_toolkit/settings.py" = ["UP007"]
# TODO fix all undefined variables in bandstructure.py
"crystal_toolkit/components/bandstructure.py" = ["F821"]

[tool.pytest.ini_options]
addopts = "-p no:warnings"

[tool.codespell]
ignore-words-list = "nd,te,ois"

[tool.mypy]
no_implicit_optional = false

[tool.versioningit.vcs]
default-tag = "0.0.1"
method = "git"

[tool.coverage.run]
branch = true
include = ["crystal_toolkit/*"]
omit = ["crystal_toolkit/apps/*"]

[tool.coverage.paths]
source = ["crystal_toolkit/"]

[tool.coverage.report]
show_missing = true
skip_covered = true
exclude_also = [
  "def __repr__",
  "if self.debug:",
  "if settings.DEBUG",
  "raise AssertionError",
  "raise NotImplementedError",
  "if 0:",
  "if __name__ == .__main__.:",
  "if TYPE_CHECKING:",
  "class .*\\bProtocol\\):",
  "@(abc\\.)?abstractmethod",
]
