#
# We use the pyproject.toml package specification.
# See https://packaging.python.org/en/latest/guides/section-build-and-publish/
# See https://github.com/collective/icalendar/issues/686
# See https://packaging.python.org/en/latest/specifications/pyproject-toml/
#

[build-system]
requires = ["hatchling>=1.27.0", "hatch-vcs"]
build-backend = "hatchling.build"

[project]
name = "icalendar"
license = "BSD-2-Clause"
license-files = ["LICENSE.rst"]
keywords = [
    "calendar",
    "calendaring",
    "ical",
    "icalendar",
    "event",
    "todo",
    "journal",
    "recurring",
    "rfc5545",
]
# If an email address for the authors ever arises, then add it here.
# See https://github.com/collective/icalendar/pull/707#discussion_r1775275335
authors = [
  { name="Plone Foundation" },
]
maintainers = [
  { name="Nicco Kunzmann", email="niccokunzmann@rambler.ru" },
  { name="Sashank Bhamidi" },
  { name="Christian Geier" },
  { name="Jaca", email="vitouejj@gmail.com" },
]
# These attributes are dynamically generated by hatch-vcs
dynamic = [
    "version"
]
description = "RFC 5545 compatible parser and generator of iCalendar files"
readme = { file = "README.rst", content-type = "text/x-rst" }

##
## When adjusting the Python Version, adjust also:
## - .github/workflows/tests.yml
## - .github/workflows/documentation.yml
## - the classifiers below
## - the documentation:
##   - docs/contribute/development.rst
##   - docs/how-to/upgrade.rst
## - dependencies - some are for compatibility and can be removed
## - tool.ruff.target-version
## - src/icalendar/compatibility.py
## - Makefile
##
requires-python = ">=3.10"

# see https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers = [
    "Development Status :: 5 - Production/Stable",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: BSD License",
    "Operating System :: OS Independent",
    "Programming Language :: Python",
    "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",
    "Programming Language :: Python :: Implementation :: CPython",
    "Programming Language :: Python :: Implementation :: PyPy",
]

dependencies = [
    "python-dateutil",
    # tzdata: IANA timezone database fallback for zoneinfo (required on Windows,
    # provides current timezone data on all platforms)
    "tzdata>=2025.3",
    # typing.TypeIs was added in Python 3.13
    # TODO: remove this dependency here and throughout code
    # when we eventually drop support for Python 3.12.
    "typing-extensions~=4.10; python_version < '3.13'",
]

# Static URLs for GitHub dependency graph compatibility
# See https://github.com/collective/icalendar/issues/1035
[project.urls]
Homepage = "https://icalendar.readthedocs.io/en/stable/"
Repository = "https://github.com/collective/icalendar"
Issues = "https://github.com/collective/icalendar/issues"
Documentation = "https://icalendar.readthedocs.io/en/stable/"
Changelog = "https://icalendar.readthedocs.io/en/stable/changelog.html"
Funding = "https://opencollective.com/python-icalendar"

# Local development dependencies
[dependency-groups]
dev = [
  {include-group = "test"},
  {include-group = "docs"},
  {include-group = "formatting"},
  "towncrier>=25.8.0",
  "tox",
  "tox-uv",
]

test = [
    "coverage",
    "hypothesis",
    "mypy>=1.0",
    "pytest",
    "pytz>=2025.2",
    "types-python-dateutil",
    "types-pytz",
]

formatting = [
    "ruff==0.15.2", # also update .pre-commit-config.yaml
    "pre-commit",
]

docs = [
    "pydata-sphinx-theme>=0.14.4",
    "sphinx>=7",
    "sphinx-autobuild>=2021.3.14",
    "sphinx-autodoc-typehints",
    "sphinx-copybutton>=0.5.2",
    "sphinx-design>=0.5.0",
    "sphinx-icalendar>=0.1.1",
    "sphinx-issues>=5.0.1",
    "sphinx-llms-txt>=0.7.1",
    "sphinx-notfound-page>=1.1.0",
    "sphinx-reredirects>=0.1.6",
    "vale>=3.12.0",
]

[project.scripts]
icalendar = "icalendar.cli:main"

[tool.hatch.build]
exclude = [
  "/.*",
  "/*.*",
  "/src/icalendar/fuzzing",
  "/dist",
  "/build",
  "/htmlcov",
]

[tool.hatch.build.targets.wheel]
packages = ["src/icalendar"]

[tool.hatch.metadata.hooks.vcs.urls]
# Dynamic URLs for built packages (hatch-vcs replaces static [project.urls])
Homepage = "https://icalendar.readthedocs.io/en/stable/"
Repository = "https://github.com/collective/icalendar"
source_archive = "https://github.com/collective/icalendar/archive/{commit_hash}.zip"
Issues = "https://github.com/collective/icalendar/issues"
Documentation = "https://icalendar.readthedocs.io/en/stable/"
Changelog = "https://icalendar.readthedocs.io/en/stable/changelog.html"
Funding = "https://opencollective.com/python-icalendar"

[tool.hatch.version]
# This configuration allows us to use the version from the tags and dynamically generate
# version files. This speeds up the release process.
source = "vcs"

[tool.hatch.version.raw-options]
# see https://github.com/ofek/hatch-vcs/issues/43#issuecomment-1553065222
local_scheme = "no-local-version"

[tool.hatch.build.hooks.vcs]
version-file = "src/icalendar/_version.py"

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

[tool.ruff]
target-version = "py310"
unsafe-fixes = true

[tool.ruff.lint]
select = ["ALL"]
ignore = [
    "ANN",     # flake8-annotations
    "B020",    # Loop control variable {name} overrides iterable it iterates
    "C401",    # Unnecessary generator (rewrite as a set comprehension)
    "C901",    # {name} is too complex ({complexity} > {max_complexity})
    "COM812",  # Trailing comma missing
    "D1",      # Missing docstring
    "D2",      # docstrings stuffs
    "D4",      # docstrings stuffs
    "EM10",    # Exception string usage
    "ERA001",  # Found commented-out code
    "FBT002",  # Boolean default positional argument in function definition
    "FIX",     # TODO comments
    "ISC001",  # Implicitly concatenated string literals on one line (to avoid with formatter)
    "N818",    # Exception name {name} should be named with an Error suffix
    "PLR091",  # Too many things (complexity, arguments, branches, etc...)
    "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable
    "RUF012",  # Mutable class attributes should be annotated with typing.ClassVar
    "RUF015",  # Prefer next({iterable}) over single element slice
    "S101",    # Use of assert detected
    "TD",      # TODO comments
    "TRY003",  # Avoid specifying long messages outside the exception class
    "S104",    # Possible binding to all interfaces
    "E722",    # Do not use bare `except`
    "RUF005",  # Consider iterable unpacking instead of concatenation
    "DTZ005",  # `datetime.datetime.now()` called without a `tz` argument
    "PERF401", # Use a list comprehension to create a transformed list
    "ARG002",  # Unused method argument: ...
    "ARG001",  # Unused function argument: ...
]
extend-safe-fixes = [
    "PT006", # Wrong type passed to first argument of @pytest.mark.parametrize; expected {expected_string}
]

[tool.ruff.lint.per-file-ignores]
"src/icalendar/tests/**" = [
    "B011",   # Do not assert False (python -O removes these calls), raise AssertionError()
    "B018",   # useless expression, used to test property access errors
    "DTZ001", # datetime.datetime() called without a tzinfo argument
    "E501",   # line too long
    "E721",   # comparing types with ==
    "E741",   # ambiguous variable name
    "FBT001", # Boolean positional argument in function definition
    "FBT003", # Boolean positional value in function call
    "INP001", # implicit namespace package
    "N802",   # Function name {name} should be lowercase
    "N803",   # Argument name {name} for {param_type} should be lowercase
    "N806",   # Variable should be lowercase
    "N999",   # module name
    "PLC0415", # import outside top-level - common in tests for isolation
    "PT011",  # pytest.raises({exception}) is too broad
    "PT012",  # pytest.raises() block should contain a single simple statement
    "PT015",  # Assertion always fails, replace with pytest.fail()
    "PT027",  # assertRaises
    "RUF001", # String contains ambiguous character
    "SIM300", # Yoda condition
    "SLF001", # Private member accessed
    "T201",   # print found
    "T203",   # `pprint` found
]
"src/icalendar/cal/**" = [
    "A002",    # argument shadowing builtin (sorted)
    "DTZ001",  # datetime() called without tzinfo
    "E501",    # line too long (docstrings with RFC references)
    "N802",    # function name should be lowercase (TRIGGER_RELATED etc.)
    "PERF203", # try-except in loop (parsing loop)
    "PLW1641", # eq without hash (Component intentionally unhashable)
    "PLW2901", # loop variable overwritten
    "SLF001",  # private member access
]
"src/icalendar/prop/**" = [
    "DTZ001",  # datetime() called without tzinfo
    "DTZ007",  # naive datetime from strptime
    "E501",    # line too long (docstrings with code examples)
    "N801",    # Class name should use CamelCase
    "PERF203", # try-except in loop
    "PLW1641", # eq without hash (mutable structured types)
    "PLW2901", # loop variable overwritten
]
"src/icalendar/**" = [
    "FBT001",  # boolean positional argument
    "FBT003",  # boolean positional value in function call
    "PLC0415", # import outside top-level (circular import avoidance)
]
"src/icalendar/fuzzing/*" = [
    "T201",   # print found
    "N802",   # Function name {name} should be lowercase
]
"docs/conf.py" = [
    "INP001", # needs __init__.py
]
"src/icalendar/timezone/equivalent_timezone_ids_result.py" = [
    "DTZ001", # `datetime.datetime()` called without a `tzinfo` argument
    "E501",   # line too long
]
"src/icalendar/timezone/equivalent_timezone_ids.py" = [
    "T201",   # print
    "T203",   # pprint
]
".github/workflows/test_generate_matrix.py" = [
    "T201",   # print
    "INP001", # needs __init__.py
]

[tool.pytest.ini_options]
# see https://docs.pytest.org/en/6.2.x/customize.html
minversion = "6.0"
# see https://docs.pytest.org/en/6.2.x/reference.html?highlight=testpaths#confval-testpaths
testpaths = [
    "src/icalendar/tests",
]
# see https://docs.pytest.org/en/6.2.x/reference.html?highlight=testpaths#confval-norecursedirs
norecursedirs = [
    "src/icalendar/tests/hypothesis",
    "build",
]
filterwarnings = [
    "ignore::DeprecationWarning",
]

[tool.towncrier]
directory = "news/"
filename = "CHANGES.rst"
issue_format = "`Issue #{issue} <https://github.com/collective/icalendar/issues/{issue}>`_"
start_string = ".. towncrier release notes start\n"
title_format = "{version} ({project_date})"
# First underline is used for version/date header.
# Second underline is used for the type names (like 'Bug fixes:').
underlines = ["-", "~", "`"]

[[tool.towncrier.type]]
directory = "breaking"
name = "Breaking changes"
showcontent = true

[[tool.towncrier.type]]
directory = "removal"
name = "Removals and deprecations"
showcontent = true

[[tool.towncrier.type]]
directory = "feature"
name = "New features"
showcontent = true

[[tool.towncrier.type]]
directory = "bugfix"
name = "Bug fixes"
showcontent = true

[[tool.towncrier.type]]
directory = "documentation"
name = "Documentation"
showcontent = true

[[tool.towncrier.type]]
directory = "deps"
name = "Dependency changes"
showcontent = true

[[tool.towncrier.type]]
directory = "internal"
name = "Internal changes"
showcontent = true

[[tool.towncrier.type]]
directory = "chore"
name = "Other tasks"
showcontent = false


[tool.uv]
python-preference = "managed"

[tool.uv.sources]
icalendar = { workspace = true, editable = true }

[tool.uv.workspace]
members = [
    ".",
]
