[build-system]
requires = ["setuptools>=77.0"]
build-backend = "setuptools.build_meta"

[project]
name = "cookidoo-api"
dynamic = ["version", "readme"]
description = "Unofficial package to access Cookidoo."
authors = [
  { name = "Cyrill Raccaud", email = "cyrill.raccaud+pypi@gmail.com" },
]
dependencies = [
  "aiohttp",
  "aiofiles",
  "isodate",
]
requires-python = ">=3.12"

classifiers = [
  "Programming Language :: Python :: 3",
  "Operating System :: OS Independent",
  "Intended Audience :: Information Technology",
  "Topic :: System :: Monitoring",
  "Topic :: System :: Logging",
  "Topic :: System :: Systems Administration",
]
license = "MIT"
keywords = ["cookidoo", "todo", "home-assistant", "iot"]

[tool.setuptools]

[tool.setuptools.packages.find]
include = ["cookidoo_api*"]

[tool.setuptools.package-data]
cookidoo_api = ["py.typed", '*.json']

[tool.setuptools.dynamic]
version = {attr = "cookidoo_api.__version__"}
readme = {file = ["README.md"], content-type = "text/markdown"}

[project.urls]
Documentation = "https://miaucl.github.io/cookidoo-api/"
Source = "https://github.com/miaucl/cookidoo-api"
Issues = "https://github.com/miaucl/cookidoo-api/issues"
"Release notes" = "https://github.com/miaucl/cookidoo-api/releases"


[tool.ruff]

target-version = "py314"

[tool.ruff.lint]
select = [
    "B002", # Python does not support the unary prefix increment
    "B007", # Loop control variable {name} not used within loop body
    "B014", # Exception handler with duplicate exception
    "B023", # Function definition does not bind loop variable {name}
    "B026", # Star-arg unpacking after a keyword argument is strongly discouraged
    "C",  # complexity
    "COM818", # Trailing comma on bare tuple prohibited
    "D",  # docstrings
    "DTZ003",  # Use datetime.now(tz=) instead of datetime.utcnow()
    "DTZ004",  # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts)
    "E",  # pycodestyle
    "F",  # pyflakes/autoflake
    "G", # flake8-logging-format
    "I",  # isort
    "ICN001", # import concentions; {name} should be imported as {asname}
    "N804", # First argument of a class method should be named cls
    "N805", # First argument of a method should be named self
    "N815", # Variable {name} in class scope should not be mixedCase
    "S307", # No builtin eval() allowed
    "PGH004",  # Use specific rule codes when using noqa
    "PLC0414", # Useless import alias. Import alias does not rename original package.
    "PLC", # pylint
    "PLE", # pylint
    "PLR", # pylint
    "PLW", # pylint
    "Q000", # Double quotes found but single quotes preferred
    "RUF006", # Store a reference to the return value of asyncio.create_task
    "S102", # Use of exec detected
    "S103",  # bad-file-permissions
    "S108",  # hardcoded-temp-file
    "S306",  # suspicious-mktemp-usage
    "S307",  # suspicious-eval-usage
    "S313",  # suspicious-xmlc-element-tree-usage
    "S314",  # suspicious-xml-element-tree-usage
    "S315",  # suspicious-xml-expat-reader-usage
    "S316",  # suspicious-xml-expat-builder-usage
    "S317",  # suspicious-xml-sax-usage
    "S318",  # suspicious-xml-mini-dom-usage
    "S319",  # suspicious-xml-pull-dom-usage
    "S601",  # paramiko-call
    "S602",  # subprocess-popen-with-shell-equals-true
    "S604",  # call-with-shell-equals-true
    "S608",  # hardcoded-sql-expression
    "S609",  # unix-command-wildcard-injection
    "SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass
    "SIM117", # Merge with-statements that use the same scope
    "SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys()
    "SIM201", # Use {left} != {right} instead of not {left} == {right}
    "SIM208", # Use {expr} instead of not (not {expr})
    "SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a}
    "SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'.
    "SIM401", # Use get from dict with default instead of an if block
    "T100", # Trace found: {name} used
    "T20",  # flake8-print
    "TID251", # Banned imports
    "TRY004", # Prefer TypeError exception for invalid type
    "B904", # Use raise from to specify exception cause
    "TRY", # tryceratops
    "UP",  # pyupgrade
    "W",  # pycodestyle
]

ignore = [
    "D202",  # No blank lines allowed after function docstring
    "D203",  # 1 blank line required before class docstring
    "D213",  # Multi-line docstring summary should start at the second line
    "D406",  # Section name should end with a newline
    "D407",  # Section name underlining
    "E501",  # line too long
    "E731",  # do not assign a lambda expression, use a def

    # Ignore ignored, as the rule is now back in preview/nursery, which cannot
    # be ignored anymore without warnings.
    # https://github.com/astral-sh/ruff/issues/7491
    # "PLC1901", # Lots of false positives

    # False positives https://github.com/astral-sh/ruff/issues/5386
    "PLC0208", # Use a sequence type instead of a `set` when iterating over values
    "PLR0911", # Too many return statements ({returns} > {max_returns})
    "PLR0912", # Too many branches ({branches} > {max_branches})
    "PLR0913", # Too many arguments to function call ({c_args} > {max_args})
    "PLR0915", # Too many statements ({statements} > {max_statements})
    "PLR2004",  # Magic value used in comparison, consider replacing {value} with a constant variable
    "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target
    "TRY003", # Avoid specifying long messages outside the exception class

    # May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
    "W191",
    "E111",
    "E114",
    "E117",
    "D206",
    "D300",
    "Q000",
    "Q001",
    "Q002",
    "Q003",
    "COM812",
    "COM819",
    "ISC001",
    "ISC002",

    # Disabled because ruff does not understand type of __all__ generated by a function
    "PLE0605",
]

[tool.ruff.lint.flake8-pytest-style]
fixture-parentheses = false

[tool.ruff.lint.flake8-tidy-imports.banned-api]
"async_timeout".msg = "use asyncio.timeout instead"
"pytz".msg = "use zoneinfo instead"

[tool.ruff.lint.isort]
force-sort-within-sections = true
known-first-party = [
    "cookidoo_api",
]
combine-as-imports = true
split-on-trailing-comma = false

[tool.ruff.lint.per-file-ignores]

# Allow for main entry & scripts to write to stdout
"cookidoo_api/__main__.py" = ["T201"]
"cookidoo_api/scripts/*" = ["T201"]
"scripts/*" = ["T20"]
"tests/*" = ["T20"]
"smoke_test/*" = ["T20"]

[tool.ruff.lint.mccabe]
max-complexity = 25

[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = [
    "tests",
]
log_cli = true
# log_cli_level = "DEBUG"
log_cli_format = "%(asctime)s%(msecs)03d [%(levelname)8s] %(name)s:%(lineno)s %(message)s"
log_cli_date_format = "%Y%m%dT%H%M%S."

[tool.coverage.run]
branch = true
source = ["tests"]

[tool.coverage.report]
format="text"
show_missing = true

# Regexes for lines to exclude from consideration
exclude_also = [
  # Don't complain about missing debug-only code:
  "def __repr__",
  "if self\\.debug",

  # Don't complain if tests don't hit defensive assertion code:
  "raise AssertionError",
  "raise NotImplementedError",

  # Don't complain if non-runnable code isn't run:
  "if 0:",
  "if __name__ == .__main__.:",

  # Don't complain about abstract methods, they aren't run:
  "@(abc\\.)?abstractmethod",
]

[tool.coverage.html]
directory = "coverage_html_report"

[tool.mypy]
python_version = "3.12"
plugins = "pydantic.mypy"
show_error_codes = true
follow_imports = "normal"
local_partial_types = true
strict_equality = true
no_implicit_optional = true
warn_incomplete_stub = true
warn_redundant_casts = true
warn_unused_configs = true
warn_unused_ignores = true
enable_error_code = ["ignore-without-code", "redundant-self", "truthy-iterable"]
disable_error_code = ["annotation-unchecked", "import-not-found", "import-untyped"]
extra_checks = false
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
