content: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # SETUP CONFIGURATION.                                                        #
  [build-system]
  requires = ["hatchling"]
  build-backend = "hatchling.build"
  
  [project]
  name = "anomalib"
  dynamic = ["version"]
  readme = "README.md"
  description = "anomalib - Anomaly Detection Library"
  requires-python = ">=3.10"
  license = { file = "LICENSE" }
  authors = [{ name = "Intel OpenVINO" }]
  
  classifiers = [
      "Development Status :: 5 - Production/Stable",
      "Intended Audience :: Developers",
      "License :: OSI Approved :: Apache Software License",
      "Programming Language :: Python",
      "Programming Language :: Python :: 3",
      "Programming Language :: Python :: 3.10",
      "Programming Language :: Python :: 3.11",
      "Programming Language :: Python :: 3.12",
      "Topic :: Software Development :: Libraries",
      "Topic :: Scientific/Engineering :: Artificial Intelligence",
  ]
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # REQUIREMENTS                                                                #
  dependencies = [
      # CLI requirements
      "omegaconf>=2.1.1",
      "rich>=13.5.2",
      "jsonargparse[signatures]>=4.27.7",
      "docstring_parser",     # CLI help-formatter
      "rich_argparse",        # CLI help-formatter
      # Torch and Lightning dependencies
      "lightning>=2.2",
      "torchmetrics>=1.8.2",
      "lightning-utilities",
      # Deep learning models and utilities
      "timm",
      "freia>=0.2",
      "einops>=0.3.2",
      # Image processing and computer vision
      "pillow>=10.1.0",
      "opencv-python>=4.5.3.56",
      "scikit-image",
      "tifffile",
      "imagecodecs",
      "kornia>=0.6.6",
      # Data science and analysis
      "pandas>=1.1.0",
      "matplotlib>=3.4.3",
      "scikit-learn",
  ]
  
  [project.optional-dependencies]
  # Model-specific optional dependencies
  openvino = ["openvino>=2024.0", "nncf>=2.10.0", "onnx>=1.16.0"]
  clip = [
      # NOTE: open-clip-torch throws the following error on v2.26.1
      #   torch.onnx.errors.UnsupportedOperatorError: Exporting the operator
      #   'aten::_native_multi_head_attention' to ONNX opset version 14 is not supported
      "open-clip-torch>=2.23.0,<2.26.1",
  ]
  vlm = ["ollama>=0.4.0", "openai", "python-dotenv", "transformers"]
  video = ["av>=10.0.0"]
  loggers = [
      "comet-ml>=3.31.7",
      "gradio>=4",
      "tensorboard",
      "wandb",
      "mlflow >=1.0.0",
  ]
  notebooks = ["gitpython", "ipykernel", "ipywidgets", "notebook"]
  docs = [
      "myst-parser[linkify]",
      "nbsphinx",
      "pandoc",
      "sphinx",
      "sphinx_autodoc_typehints",
      "sphinx_book_theme",
      "sphinx-copybutton",
      "sphinx_design",
  ]
  test = [
      "pre-commit",
      "pytest",
      "pytest-cov",
      "pytest-xdist",
      "pytest-mock",
      "pytest-sugar",
      "pytest-timeout",
      "pytest-json-report",
      "coverage[toml]",
      "tox",
      "commitizen",
  ]
  # PyTorch dependency groups
  cpu = [
      "torch>=2.4.0",
      "torchvision>=0.19.0",
  ]
  cu118 = [
      "torch>=2.4.0",
      "torchvision>=0.19.0",
  ]
  cu121 = [
      "torch>=2.4.0",
      "torchvision>=0.19.0",
  ]
  cu124 = [
      "torch>=2.4.0",
      "torchvision>=0.19.0",
  ]
  rocm = [
      "torch>=2.4.0",
      "torchvision>=0.19.0",
      "pytorch-triton-rocm ; sys_platform == 'linux'",
  ]
  xpu = [
      "torch>=2.4.0",
      "torchvision>=0.19.0",
      "pytorch-triton-xpu ; sys_platform == 'linux' or sys_platform == 'win32'",
  ]
  full = ["anomalib[clip,openvino,loggers,notebooks,vlm,video]"]
  dev = ["anomalib[full,docs,test]"]
  
  [project.scripts]
  anomalib = "anomalib.cli.cli:main"
  
  [tool.hatch.version]
  path = "src/anomalib/__init__.py"
  
  [tool.hatch.build.targets.sdist]
  only-include = [
      "CHANGELOG.md",
      "CITATION.cff",
      "LICENSE",
      "MANIFEST.in",
      "pyproject.toml",
      "README.md",
      "src/",
      "third-party-programs.txt",
  ]
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # UV CONFIGURATION                                                            #
  [tool.uv]
  # UV workspace configuration
  package = true
  managed = true
  conflicts = [
      [
          { extra = "cpu" },
          { extra = "cu118" },
          { extra = "cu121" },
          { extra = "cu124" },
          { extra = "rocm" },
          { extra = "xpu" },
      ],
  ]
  
  # PyTorch index configuration
  [[tool.uv.index]]
  name = "pytorch-cpu"
  url = "https://download.pytorch.org/whl/cpu"
  explicit = true
  
  [[tool.uv.index]]
  name = "pytorch-cu118"
  url = "https://download.pytorch.org/whl/cu118"
  explicit = true
  
  [[tool.uv.index]]
  name = "pytorch-cu121"
  url = "https://download.pytorch.org/whl/cu121"
  explicit = true
  
  [[tool.uv.index]]
  name = "pytorch-cu124"
  url = "https://download.pytorch.org/whl/cu124"
  explicit = true
  
  [[tool.uv.index]]
  name = "pytorch-rocm61"
  url = "https://download.pytorch.org/whl/rocm6.1"
  explicit = true
  
  [[tool.uv.index]]
  name = "pytorch-xpu"
  url = "https://download.pytorch.org/whl/xpu"
  explicit = true
  
  # PyTorch sources configuration
  [tool.uv.sources]
  torch = [
      { index = "pytorch-cpu", extra = "cpu" },
      { index = "pytorch-cu118", extra = "cu118" },
      { index = "pytorch-cu121", extra = "cu121" },
      { index = "pytorch-cu124", extra = "cu124" },
      { index = "pytorch-rocm61", extra = "rocm" },
      { index = "pytorch-xpu", extra = "xpu" },
  ]
  torchvision = [
      { index = "pytorch-cpu", extra = "cpu" },
      { index = "pytorch-cu118", extra = "cu118" },
      { index = "pytorch-cu121", extra = "cu121" },
      { index = "pytorch-cu124", extra = "cu124" },
      { index = "pytorch-rocm61", extra = "rocm" },
      { index = "pytorch-xpu", extra = "xpu" },
  ]
  pytorch-triton-rocm = [
      { index = "pytorch-rocm61", extra = "rocm" },
  ]
  pytorch-triton-xpu = [
      { index = "pytorch-xpu", extra = "xpu" },
  ]
  
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # RUFF CONFIGURATION                                                          #
  [tool.ruff]
  # Enable preview features
  preview = true
  
  # Enable rules
  select = [
      "F",    # Pyflakes (`F`)
      "E",    # pycodestyle error (`E`)
      "W",    # pycodestyle warning (`W`)
      "C90",  # mccabe (`C90`)
      "I",    # isort (`I`)
      "N",    # pep8-naming (`N`)
      "D",    # pydocstyle (`D`)
      "UP",   # pyupgrade (`UP`)
      "YTT",  # flake8-2020 (`YTT`)
      "ANN",  # flake8-annotations (`ANN`)
      "S",    # flake8-bandit (`S`)
      "BLE",  # flake8-blind-except (`BLE`)
      "FBT",  # flake8-boolean-trap (`FBT`)
      "B",    # flake8-bugbear (`B`)
      "A",    # flake8-builtins (`A`)
      "COM",  # flake8-commas (`COM`)
      "CPY",  # flake8-copyright (`CPY`)
      "C4",   # flake8-comprehensions (`C4`)
      "DTZ",  # flake8-datatimez (`DTZ`)
      "T10",  # flake8-debugger (`T10`)
      "EM",   # flake8-errmsg (`EM`)
      "FA",   # flake8-future-annotations (`FA`)
      "ISC",  # flake8-implicit-str-concat (`ISC`)
      "ICN",  # flake8-import-conventions (`ICN`)
      "PIE",  # flake8-pie (`PIE`)
      "PT",   # flake8-pytest-style (`PT`)
      "RSE",  # flake8-raise (`RSE`)
      "RET",  # flake8-return (`RET`)
      "SLF",  # flake8-self (`SLF`)
      "SIM",  # flake8-simplify (`SIM`)
      "TID",  # flake8-tidy-imports (`TID`)
      "TC",   # flake8-type-checking (`TC`)
      "INT",  # flake8-gettext (`INT`)
      "ARG",  # flake8-unsused-arguments (`ARG`)
      "PTH",  # flake8-use-pathlib (`PTH`)
      "TD",   # flake8-todos (`TD`)
      "FIX",  # flake8-fixme (`FIX`)
      "ERA",  # eradicate (`ERA`)
      "PD",   # pandas-vet (`PD`)
      "PGH",  # pygrep-hooks (`PGH`)
      "PL",   # pylint (`PL`)
      "TRY",  # tryceratos (`TRY`)
      "FLY",  # flynt (`FLY`)
      "NPY",  # NumPy-specific rules (`NPY`)
      "PERF", # Perflint (`PERF`)
      "RUF",  # Ruff-specific rules (`RUF`)
      # "FURB", # refurb (`FURB`) - ERROR: Unknown rule selector: `FURB`
      # "LOG",  # flake8-logging (`LOG`) - ERROR: Unknown rule selector: `LOG`
  ]
  
  ignore = [
      # pydocstyle
      "D107", # Missing docstring in __init__
  
      # pylint
      "PLR0913", # Too many arguments to function call
      "PLR2004", # consider replacing with a constant variable
      "PLR0912", # Too many branches
      "PLR0915", # Too many statements
  
      # NOTE: Disable the following rules for now.
      "A004", # import is shadowing a Python built-in
      "A005", # Module is shadowing a Python built-in
      "B909", # Mutation to loop iterable during iteration
      "PLC2701", # Private name import
      "PLC0415", # import should be at the top of the file
      "PLR0917", # Too many positional arguments
      "E226", # Missing whitespace around arithmetic operator
      "E266", # Too many leading `#` before block comment
  
      "F822", # Undefined name `` in `__all__`
  
      "PGH004", # Use specific rule codes when using 'ruff: noqa'
      "PT001", # Use @pytest.fixture over @pytest.fixture()
      "PLR6104", # Use `*=` to perform an augmented assignment directly
      "PLR0914", # Too many local variables
      "PLC0206", # Extracting value from dictionary without calling `.items()`
      "PLC1901", # can be simplified
  
      "RUF021", # Parenthesize the `and` subexpression
      "RUF022", # Apply an isort-style sorting to '__all__'
      "S404", # `subprocess` module is possibly insecure
      # End of disable rules
  
      # flake8-annotations
      "ANN002", # Missing type annotation for *args
      "ANN003", # Missing type annotation for **kwargs
  
      # flake8-bandit (`S`)
      "S101", # Use of assert detected.
  
      # flake8-boolean-trap (`FBT`)
      "FBT001", # Boolean positional arg in function definition
      "FBT002", # Boolean default value in function definition
  
      # flake8-datatimez (`DTZ`)
      "DTZ005", # The use of `datetime.datetime.now()` without `tz` argument is not allowed
  
      # flake8-fixme (`FIX`)
      "FIX002", # Line contains TODO, consider resolving the issue
  ]
  
  # Allow autofix for all enabled rules (when `--fix`) is provided.
  fixable = ["ALL"]
  unfixable = []
  
  # Exclude a variety of commonly ignored directories.
  exclude = [
      ".bzr",
      ".direnv",
      ".eggs",
      ".git",
      ".hg",
      ".mypy_cache",
      ".nox",
      ".pants.d",
      ".pytype",
      ".ruff_cache",
      ".svn",
      ".tox",
      ".venv",
      "__pypackages__",
      "_build",
      "buck-out",
      "build",
      "dist",
      "node_modules",
      "venv",
  ]
  
  # Same as Black.
  line-length = 120
  
  # Allow unused variables when underscore-prefixed.
  dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
  
  # Assume Python 3.10.
  target-version = "py310"
  
  # Allow imports relative to the "src" and "tests" directories.
  src = ["src", "tests"]
  
  [tool.ruff.mccabe]
  # Unlike Flake8, default to a complexity level of 10.
  max-complexity = 15
  
  
  [tool.ruff.pydocstyle]
  convention = "google"
  
  [tool.ruff.flake8-copyright]
  notice-rgx = """
  # Copyright \\(C\\) (\\d{4}(-\\d{4})?) Intel Corporation
  # SPDX-License-Identifier: Apache-2\\.0
  """
  
  [tool.ruff.per-file-ignores]
  "examples/notebooks/**/*" = ["CPY001"]
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # MYPY CONFIGURATION.                                                         #
  [tool.mypy]
  ignore_missing_imports = true
  show_error_codes = true
  
  
  [[tool.mypy.overrides]]
  module = ["torch.*", "wandb.*"]
  follow_imports = "skip"
  follow_imports_for_stubs = true
  
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # BANDIT CONFIGURATION                                                        #
  [tool.bandit]
  skips = ["B101"]
  exclude_dirs = ["tests"]
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # PYTEST CONFIGURATION                                                        #
  [tool.pytest.ini_options]
  addopts = ["--strict-markers", "--strict-config", "--showlocals", "-ra"]
  testpaths = "tests"
  pythonpath = "src"
  markers = [
      "gpu: marks tests that require GPU",
      "cpu: marks tests that can run on CPU only (default)",
  ]
  
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # COVERAGE CONFIGURATION                                                      #
  [tool.coverage.report]
  exclude_lines = [
      "pragma: no cover",
      "def __repr__",
      "raise NotImplementedError",
      "if TYPE_CHECKING:",
      "@abstractmethod",
      "pass",
      "raise ImportError",
      "raise ValueError",
      "except ImportError:",
  ]
  
  [tool.coverage.paths]
  source = ["src", ".tox/*/site-packages"]
  
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # NBQA CONFIGURATION                                                          #
  [tool.nbqa.addopts]
  ruff = ["--ignore=E402"]
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # LINK CHECKER CONFIGURATION                                                  #
  [tool.md_dead_link_check]
  exclude_links = [
      "https://github.com/open-edge-platform/anomalib/settings/actions/runners/new",
      "https://open-edge-platform.github.io/anomalib/*", # Remove it after publish documentation
  ]
  exclude_files = ["docs/*"]
  force_get_requests_for_links = ["https://secure.ethicspoint.com/*"]
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # COMMITIZEN CONFIGURATION                                                    #
  [tool.commitizen]
  name = "cz_conventional_commits"
  tag_format = "v$version"
  version_scheme = "semver"
  version_provider = "pep621"
  update_changelog_on_bump = true
  changelog_file = "CHANGELOG.md"
  changelog_format = "## $version ($date)"
  major_version_zero = true
  types = [
      "feat",
      "fix",
      "docs",
      "style",
      "refactor",
      "perf",
      "test",
      "build",
      "ci",
      "chore",
  ]
  scopes = [
      "data",
      "model",
      "metric",
      "utils",
      "cli",
      "docs",
      "ci",
      "engine",
      "visualization",
      "benchmarking",
      "logger",
      "openvino",
      "notebooks"
  ]
  
  # Conventional commit rules
  [tool.commitizen.rules]
  scope-enum = [2, "always", [
      "data",
      "model",
      "metric",
      "utils",
      "cli",
      "docs",
      "ci",
      "engine",
      "visualization",
      "benchmarking",
      "logger",
      "openvino",
      "notebooks"
  ]]
  scope-empty = [0]
  subject-case = [2, "never", ["sentence-case", "start-case", "pascal-case", "upper-case"]]
  subject-empty = [2, "never"]
  subject-full-stop = [2, "never", "."]
  type-case = [2, "always", "lower-case"]
  type-empty = [2, "never"]
  