[build-system]
requires = ["setuptools~=82.0.0", "wheel~=0.46.1"]
build-backend = "setuptools.build_meta"

[project]
name = "Supervisor"
dynamic = ["version", "dependencies"]
license = { text = "Apache-2.0" }
description = "Open-source private cloud os for Home-Assistant based on HassOS"
readme = "README.md"
authors = [
  { name = "The Home Assistant Authors", email = "hello@home-assistant.io" },
]
keywords = ["docker", "home-assistant", "api"]
requires-python = ">=3.13.0"

[project.urls]
"Homepage" = "https://www.home-assistant.io/"
"Source Code" = "https://github.com/home-assistant/supervisor"
"Bug Reports" = "https://github.com/home-assistant/supervisor/issues"
"Docs: Dev" = "https://developers.home-assistant.io/"
"Discord" = "https://www.home-assistant.io/join-chat/"
"Forum" = "https://community.home-assistant.io/"

[tool.setuptools]
platforms = ["any"]
zip-safe = false
include-package-data = true

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

[tool.pylint.MAIN]
py-version = "3.13"
# Use a conservative default here; 2 should speed up most setups and not hurt
# any too bad. Override on command line as appropriate.
jobs = 2
persistent = false
extension-pkg-allow-list = ["ciso8601"]

[tool.pylint.BASIC]
class-const-naming-style = "any"
good-names = ["id", "i", "j", "k", "ex", "Run", "_", "fp", "T", "os"]

[tool.pylint."MESSAGES CONTROL"]
# Reasons disabled:
# format - handled by ruff
# abstract-method - with intro of async there are always methods missing
# cyclic-import - doesn't test if both import on load
# duplicate-code - unavoidable
# locally-disabled - it spams too much
# too-many-* - are not enforced for the sake of readability
# too-few-* - same as too-many-*
# unused-argument - generic callbacks and setup methods create a lot of warnings
disable = [
  "format",
  "abstract-method",
  "cyclic-import",
  "duplicate-code",
  "locally-disabled",
  "no-else-return",
  "not-context-manager",
  "too-few-public-methods",
  "too-many-arguments",
  "too-many-branches",
  "too-many-instance-attributes",
  "too-many-lines",
  "too-many-locals",
  "too-many-public-methods",
  "too-many-return-statements",
  "too-many-statements",
  "unused-argument",
  "consider-using-with",

  # Handled by ruff
  # Ref: <https://github.com/astral-sh/ruff/issues/970>
  "await-outside-async",    # PLE1142
  "bad-str-strip-call",     # PLE1310
  "bad-string-format-type", # PLE1307
  "bidirectional-unicode",  # PLE2502
  "continue-in-finally",    # PLE0116
  "duplicate-bases",        # PLE0241
  "format-needs-mapping",   # F502
  "function-redefined",     # F811
  # Needed because ruff does not understand type of __all__ generated by a function
  # "invalid-all-format", # PLE0605
  "invalid-all-object",                 # PLE0604
  "invalid-character-backspace",        # PLE2510
  "invalid-character-esc",              # PLE2513
  "invalid-character-nul",              # PLE2514
  "invalid-character-sub",              # PLE2512
  "invalid-character-zero-width-space", # PLE2515
  "logging-too-few-args",               # PLE1206
  "logging-too-many-args",              # PLE1205
  "missing-format-string-key",          # F524
  "mixed-format-string",                # F506
  "no-method-argument",                 # N805
  "no-self-argument",                   # N805
  "nonexistent-operator",               # B002
  "nonlocal-without-binding",           # PLE0117
  "not-in-loop",                        # F701, F702
  "notimplemented-raised",              # F901
  "return-in-init",                     # PLE0101
  "return-outside-function",            # F706
  "syntax-error",                       # E999
  "too-few-format-args",                # F524
  "too-many-format-args",               # F522
  "too-many-star-expressions",          # F622
  "truncated-format-string",            # F501
  "undefined-all-variable",             # F822
  "undefined-variable",                 # F821
  "used-prior-global-declaration",      # PLE0118
  "yield-inside-async-function",        # PLE1700
  "yield-outside-function",             # F704
  "anomalous-backslash-in-string",      # W605
  "assert-on-string-literal",           # PLW0129
  "assert-on-tuple",                    # F631
  "bad-format-string",                  # W1302, F
  "bad-format-string-key",              # W1300, F
  "bare-except",                        # E722
  "binary-op-exception",                # PLW0711
  "cell-var-from-loop",                 # B023
  # "dangerous-default-value", # B006, ruff catches new occurrences, needs more work
  "duplicate-except",                     # B014
  "duplicate-key",                        # F601
  "duplicate-string-formatting-argument", # F
  "duplicate-value",                      # F
  "eval-used",                            # PGH001
  "exec-used",                            # S102
  # "expression-not-assigned", # B018, ruff catches new occurrences, needs more work
  "f-string-without-interpolation",      # F541
  "forgotten-debug-statement",           # T100
  "format-string-without-interpolation", # F
  # "global-statement", # PLW0603, ruff catches new occurrences, needs more work
  "global-variable-not-assigned",  # PLW0602
  "implicit-str-concat",           # ISC001
  "import-self",                   # PLW0406
  "inconsistent-quotes",           # Q000
  "invalid-envvar-default",        # PLW1508
  "keyword-arg-before-vararg",     # B026
  "logging-format-interpolation",  # G
  "logging-fstring-interpolation", # G
  "logging-not-lazy",              # G
  "misplaced-future",              # F404
  "named-expr-without-context",    # PLW0131
  "nested-min-max",                # PLW3301
  # "pointless-statement", # B018, ruff catches new occurrences, needs more work
  "raise-missing-from", # TRY200
  # "redefined-builtin", # A001, ruff is way more stricter, needs work
  "try-except-raise",               # TRY203
  "unused-argument",                # ARG001, we don't use it
  "unused-format-string-argument",  #F507
  "unused-format-string-key",       # F504
  "unused-import",                  # F401
  "unused-variable",                # F841
  "useless-else-on-loop",           # PLW0120
  "wildcard-import",                # F403
  "bad-classmethod-argument",       # N804
  "consider-iterating-dictionary",  # SIM118
  "empty-docstring",                # D419
  "invalid-name",                   # N815
  "line-too-long",                  # E501, disabled globally
  "missing-class-docstring",        # D101
  "missing-final-newline",          # W292
  "missing-function-docstring",     # D103
  "missing-module-docstring",       # D100
  "multiple-imports",               #E401
  "singleton-comparison",           # E711, E712
  "subprocess-run-check",           # PLW1510
  "superfluous-parens",             # UP034
  "ungrouped-imports",              # I001
  "unidiomatic-typecheck",          # E721
  "unnecessary-direct-lambda-call", # PLC3002
  "unnecessary-lambda-assignment",  # PLC3001
  "unneeded-not",                   # SIM208
  "useless-import-alias",           # PLC0414
  "wrong-import-order",             # I001
  "wrong-import-position",          # E402
  "comparison-of-constants",        # PLR0133
  "comparison-with-itself",         # PLR0124
  # "consider-alternative-union-syntax", # UP007, typing extension
  "consider-merging-isinstance", # PLR1701
  # "consider-using-alias",              # UP006, typing extension
  "consider-using-dict-comprehension", # C402
  "consider-using-generator",          # C417
  "consider-using-get",                # SIM401
  "consider-using-set-comprehension",  # C401
  "consider-using-sys-exit",           # PLR1722
  "consider-using-ternary",            # SIM108
  "literal-comparison",                # F632
  "property-with-parameters",          # PLR0206
  "super-with-arguments",              # UP008
  "too-many-branches",                 # PLR0912
  "too-many-return-statements",        # PLR0911
  "too-many-statements",               # PLR0915
  "trailing-comma-tuple",              # COM818
  "unnecessary-comprehension",         # C416
  "use-a-generator",                   # C417
  "use-dict-literal",                  # C406
  "use-list-literal",                  # C405
  "useless-object-inheritance",        # UP004
  "useless-return",                    # PLR1711
  # "no-self-use", # PLR6301  # Optional plugin, not enabled
]

[tool.pylint.REPORTS]
score = false

[tool.pylint.TYPECHECK]
ignored-modules = ["distutils"]

[tool.pylint.FORMAT]
expected-line-ending-format = "LF"

[tool.pylint.EXCEPTIONS]
overgeneral-exceptions = ["builtins.BaseException", "builtins.Exception"]

[tool.pylint.DESIGN]
max-positional-arguments = 10

[tool.pytest.ini_options]
testpaths = ["tests"]
norecursedirs = [".git"]
log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s"
log_date_format = "%Y-%m-%d %H:%M:%S"
asyncio_default_fixture_loop_scope = "function"
asyncio_mode = "auto"
filterwarnings = [
  "error",
  "ignore:pkg_resources is deprecated as an API:DeprecationWarning:dirhash",
  "ignore::pytest.PytestUnraisableExceptionWarning",
]
markers = [
  "no_mock_init_websession: disable the autouse mock of init_websession for this test",
]

[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
  "B904",    # Use raise from to specify exception cause
  "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
  "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
  "TRY203",  # Remove exception handler; error is immediately re-raised
  "UP",      # pyupgrade
  "W",       # pycodestyle
]

lint.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
  "UP006",   # keep type annotation style as is
  "UP007",   # keep type annotation style as is

  # 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-import-conventions.extend-aliases]
voluptuous = "vol"

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

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

[tool.ruff.lint.isort]
force-sort-within-sections = true
section-order = [
  "future",
  "standard-library",
  "third-party",
  "first-party",
  "local-folder",
]
forced-separate = ["tests"]
known-first-party = ["supervisor", "tests"]
combine-as-imports = true
split-on-trailing-comma = false

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

# DBus Service Mocks must use typing and names understood by dbus-fast
"tests/dbus_service_mocks/*.py" = ["F722", "F821", "N815"]

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