[project]
name = "borgbackup"
dynamic = ["version", "readme"]
authors = [{name="The Borg Collective (see AUTHORS file)"}]
maintainers = [
    {name="Thomas Waldmann", email="tw@waldmann-edv.de"},
]
description = "Deduplicated, encrypted, authenticated, and compressed backups"
requires-python = ">=3.10"
keywords = ["backup", "borgbackup"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Environment :: Console",
    "Intended Audience :: System Administrators",
    "Operating System :: POSIX :: BSD :: FreeBSD",
    "Operating System :: POSIX :: BSD :: OpenBSD",
    "Operating System :: POSIX :: BSD :: NetBSD",
    "Operating System :: MacOS :: MacOS X",
    "Operating System :: POSIX :: Linux",
    "Programming Language :: Python",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Programming Language :: Python :: 3.14",
    "Topic :: Security :: Cryptography",
    "Topic :: System :: Archiving :: Backup",
]
license = "BSD-3-Clause"
license-files = ["LICENSE", "AUTHORS"]
dependencies = [
  "borghash ~= 0.1.0",
  "borgstore ~= 0.3.0",
  "msgpack >=1.0.3, <=1.1.2",
  "packaging",
  "platformdirs >=3.0.0, <5.0.0; sys_platform == 'darwin'",  # for macOS: breaking changes in 3.0.0.
  "platformdirs >=2.6.0, <5.0.0; sys_platform != 'darwin'",  # for others: 2.6+ works consistently.
  "argon2-cffi",
  "shtab>=1.7.0",
]

[project.optional-dependencies]
llfuse = ["llfuse >= 1.3.8"]
pyfuse3 = ["pyfuse3 >= 3.1.1"]
nofuse = []
s3 = ["borgstore[s3] ~= 0.3.0"]
sftp = ["borgstore[sftp] ~= 0.3.0"]

[project.urls]
"Homepage" = "https://borgbackup.org/"
"Bug Tracker" = "https://github.com/borgbackup/borg/issues"
"Documentation" = "https://borgbackup.readthedocs.io/"
"Repository" = "https://github.com/borgbackup/borg"
"Changelog" = "https://github.com/borgbackup/borg/blob/master/docs/changes.rst"

[project.scripts]
borg = "borg.archiver:main"
borgfs = "borg.archiver:main"

[tool.setuptools]
# See also the MANIFEST.in file.
# We want to install all the files in the package directories...
include-package-data = true

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.exclude-package-data]
# ...except the source files which have been compiled (C extensions):
"*" = ["*.c", "*.h", "*.pyx"]

[build-system]
requires = ["setuptools>=78.1.1", "wheel", "pkgconfig", "Cython>=3.0.3", "setuptools_scm[toml]>=6.2"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]
# Make sure we have the same versioning scheme with all setuptools_scm versions, to avoid different autogenerated files.
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1015052
# https://github.com/borgbackup/borg/issues/6875
write_to = "src/borg/_version.py"
write_to_template = "__version__ = version = {version!r}\n"

[tool.black]
line-length = 120
skip-magic-trailing-comma = true

[tool.ruff]
line-length = 120
target-version = "py310"

# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
select = ["E", "F"]

# for reference ...
#   E402 module level import not at top
#   E501 line too long
#   F401 import unused
#   F405 undefined or defined from star imports
#   F811 redef of unused var

# borg code style guidelines:
# Ignoring E203 due to https://github.com/PyCQA/pycodestyle/issues/373.
ignore = ["E203", "F405", "E402"]

# Allow autofix for all enabled rules (when `--fix` is provided).
fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
unfixable = []

# Exclude a variety of commonly ignored directories.
exclude = [
    ".cache",
    ".eggs",
    ".git",
    ".git-rewrite",
    ".idea",
    ".mypy_cache",
    ".ruff_cache",
    ".tox",
    "build",
    "dist",
]

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

# Code style violation exceptions:
# please note that the values are adjusted so that they do not cause failures
# with existing code. if you want to change them, you should first fix all
# ruff failures that appear with your change.
[tool.ruff.per-file-ignores]
"scripts/make.py" = ["E501"]
"src/borg/archive.py" = ["E501"]
"src/borg/archiver/help_cmd.py" = ["E501"]
"src/borg/cache.py" = ["E501"]
"src/borg/helpers/__init__.py" = ["F401"]
"src/borg/platform/__init__.py" = ["F401"]
"src/borg/testsuite/archiver/disk_full_test.py" = ["F811"]
"src/borg/testsuite/archiver/return_codes_test.py" = ["F811"]
"src/borg/testsuite/benchmark_test.py" = ["F811"]
"src/borg/testsuite/platform_test.py" = ["F811"]

[tool.pytest.ini_options]
markers = []

[tool.mypy]
python_version = "3.10"
strict_optional = false
local_partial_types = true
show_error_codes = true
files = "src/borg/**/*.py"

[[tool.mypy.overrides]]
module = [
    "msgpack.*",
    "llfuse",
    "pyfuse3",
    "trio",
    "borg.crypto.low_level",
    "borg.platform.*",
]
ignore_missing_imports = true

[tool.tox]
requires = ["tox>=4.19", "pkgconfig", "cython", "wheel", "setuptools_scm"]
# Important: when adding/removing Python versions here,
#            also update the section "Test environments with different FUSE implementations" accordingly.
env_list = ["py{310,311,312,313,314}-{none,fuse2,fuse3}", "docs", "ruff", "mypy", "bandit"]

[tool.tox.env_run_base]
package = "editable-legacy"  # without this it does not find setup_docs when running under fakeroot
deps = ["-rrequirements.d/development.txt"]
commands = [["python", "-m", "pytest", "-v", "-n", "{env:XDISTN:auto}", "-rs", "--cov=borg", "--cov-config=pyproject.toml", "--benchmark-skip", "--pyargs", "{posargs:borg.testsuite}"]]
pass_env = ["*"]  # fakeroot -u needs some env vars

[tool.tox.env_pkg_base]
pass_env = ["*"]  # needed by tox4, so env vars are visible for building borg

# Test environments with different FUSE implementations
[tool.tox.env.py310-none]

[tool.tox.env.py310-fuse2]
set_env = {BORG_FUSE_IMPL = "llfuse"}
extras = ["llfuse", "sftp", "s3"]

[tool.tox.env.py310-fuse3]
set_env = {BORG_FUSE_IMPL = "pyfuse3"}
extras = ["pyfuse3", "sftp", "s3"]

[tool.tox.env.py311-none]

[tool.tox.env.py311-fuse2]
set_env = {BORG_FUSE_IMPL = "llfuse"}
extras = ["llfuse", "sftp", "s3"]

[tool.tox.env.py311-fuse3]
set_env = {BORG_FUSE_IMPL = "pyfuse3"}
extras = ["pyfuse3", "sftp", "s3"]

[tool.tox.env.py312-none]

[tool.tox.env.py312-fuse2]
set_env = {BORG_FUSE_IMPL = "llfuse"}
extras = ["llfuse", "sftp", "s3"]

[tool.tox.env.py312-fuse3]
set_env = {BORG_FUSE_IMPL = "pyfuse3"}
extras = ["pyfuse3", "sftp", "s3"]

[tool.tox.env.py313-none]

[tool.tox.env.py313-fuse2]
set_env = {BORG_FUSE_IMPL = "llfuse"}
extras = ["llfuse", "sftp", "s3"]

[tool.tox.env.py313-fuse3]
set_env = {BORG_FUSE_IMPL = "pyfuse3"}
extras = ["pyfuse3", "sftp", "s3"]

[tool.tox.env.py314-none]

[tool.tox.env.py314-fuse2]
set_env = {BORG_FUSE_IMPL = "llfuse"}
extras = ["llfuse", "sftp", "s3"]

[tool.tox.env.py314-fuse3]
set_env = {BORG_FUSE_IMPL = "pyfuse3"}
extras = ["pyfuse3", "sftp", "s3"]

[tool.tox.env.ruff]
skip_install = true
deps = ["ruff"]
commands = [["ruff", "check", "."]]

[tool.tox.env.mypy]
deps = ["pytest", "mypy", "pkgconfig"]
commands = [["mypy", "--ignore-missing-imports"]]

[tool.tox.env.docs]
change_dir = "docs"
deps = ["sphinx", "sphinxcontrib-jquery", "guzzle_sphinx_theme"]
commands = [["sphinx-build", "-n", "-v", "-W", "--keep-going", "-b", "html", "-d", "{envtmpdir}/doctrees", ".", "{envtmpdir}/html"]]

[tool.bandit]
exclude_dirs = [".cache", ".eggs", ".git", ".git-rewrite", ".idea", ".mypy_cache", ".ruff_cache", ".tox", "build", "dist", "src/borg/testsuite"]
skips = [
    "B101",  # skip assert warnings, we do not allow running borg with assertions disabled.
    "B404",  # do not warn about just import subprocess
]

[tool.tox.env.bandit]
skip_install = true
deps = ["bandit[toml]"]
commands = [["bandit", "-r", "src/borg", "-c", "pyproject.toml"]]

[tool.coverage.run]
branch = true
disable_warnings = ["module-not-measured", "no-ctracer"]
source = ["src/borg"]
omit = [
    "*/borg/__init__.py",
    "*/borg/__main__.py",
    "*/borg/_version.py",
    "*/borg/fuse.py",
    "*/borg/support/*",
    "*/borg/testsuite/*",
    "*/borg/hash_sizes.py",
]

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "pragma: freebsd only",
    "pragma: unknown platform only",
    "def __repr__",
    "raise AssertionError",
    "raise NotImplementedError",
    "if 0:",
    "if __name__ == .__main__.:",
]
ignore_errors = true
