# Ruff configuration for the sws12500 Home Assistant custom component. # # This mirrors the rule set Home Assistant core uses, because that is the # convention this integration's code is written against. Core-only settings # (entry-point scripts, the per-component relative-import exemptions, and the # PLATFORM_SCHEMA import aliases for domains this integration does not touch) # have been dropped; everything else is carried over verbatim so that the # tooling in this repository describes the code that is already here. required-version = ">=0.8.0" line-length = 120 [lint] select = [ "A001", # Variable {name} is shadowing a Python builtin "ASYNC210", # Async functions should not call blocking HTTP methods "ASYNC220", # Async functions should not create subprocesses with blocking methods "ASYNC221", # Async functions should not run processes with blocking methods "ASYNC222", # Async functions should not wait on processes with blocking methods "ASYNC230", # Async functions should not open files with blocking methods like open "ASYNC251", # Async functions should not call time.sleep "B002", # Python does not support the unary prefix increment "B005", # Using .strip() with multi-character strings is misleading "B007", # Loop control variable {name} not used within loop body "B014", # Exception handler with duplicate exception "B015", # Pointless comparison. Did you mean to assign a value? Otherwise, prepend assert or remove it. "B017", # pytest.raises(BaseException) should be considered evil "B018", # Found useless attribute access. Either assign it to a variable or remove it. "B023", # Function definition does not bind loop variable {name} "B026", # Star-arg unpacking after a keyword argument is strongly discouraged "B032", # Possible unintentional type annotation (using :). Did you mean to assign (using =)? "B904", # Use raise from to specify exception cause "B905", # zip() without an explicit strict= parameter "BLE", "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 "F541", # f-string without any placeholders "FLY", # flynt "FURB", # refurb "G", # flake8-logging-format "I", # isort "INP", # flake8-no-pep420 "ISC", # flake8-implicit-str-concat "ICN001", # import concentions; {name} should be imported as {asname} "LOG", # flake8-logging "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 "PERF", # Perflint "PGH", # pygrep-hooks "PIE", # flake8-pie "PL", # pylint "PT", # flake8-pytest-style "PTH", # flake8-pathlib "PYI", # flake8-pyi "RET", # flake8-return "RSE", # flake8-raise "RUF005", # Consider iterable unpacking instead of concatenation "RUF006", # Store a reference to the return value of asyncio.create_task "RUF010", # Use explicit conversion flag "RUF013", # PEP 484 prohibits implicit Optional "RUF017", # Avoid quadratic list summation "RUF018", # Avoid assignment expressions in assert statements "RUF019", # Unnecessary key check before dictionary access # "RUF100", # Unused `noqa` directive; temporarily every now and then to clean them up "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 "SIM", # flake8-simplify "SLF", # flake8-self "SLOT", # flake8-slots "T100", # Trace found: {name} used "T20", # flake8-print "TC", # flake8-type-checking "TID", # Tidy imports "TRY", # tryceratops "UP", # pyupgrade "UP031", # Use format specifiers instead of percent format "UP032", # Use f-string instead of `format` call "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 "PLC1901", # {existing} can be simplified to {replacement} as an empty string is falsey; too many false positives "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 "PT011", # pytest.raises({exception}) is too broad, set the `match` parameter or use a more specific exception "PT018", # Assertion should be broken down into multiple parts "RUF001", # String contains ambiguous unicode character. "RUF002", # Docstring contains ambiguous unicode character. "RUF003", # Comment contains ambiguous unicode character. "RUF015", # Prefer next(...) over single element slice "SIM102", # Use a single if statement instead of nested if statements "SIM103", # Return the condition {condition} directly "SIM108", # Use ternary operator {contents} instead of if-else-block "SIM115", # Use context handler for opening files # Moving imports into type-checking blocks can mess with pytest.patch() "TC001", # Move application import {} into a type-checking block "TC002", # Move third-party import {} into a type-checking block "TC003", # Move standard library import {} into a type-checking block "TRY003", # Avoid specifying long messages outside the exception class "TRY400", # Use `logging.exception` instead of `logging.error` # UP038 (`X | Y` in `isinstance`) is in core's ignore list, but the rule was # removed from ruff; listing it only produces a "has no effect" warning. # May conflict with the formatter, https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules "W191", "E111", "E114", "E117", "D206", "D300", "Q", "COM812", "COM819", "ISC001", # Disabled because ruff does not understand type of __all__ generated by a function "PLE0605", ] [lint.flake8-import-conventions.extend-aliases] voluptuous = "vol" "homeassistant.components.binary_sensor.PLATFORM_SCHEMA" = "BINARY_SENSOR_PLATFORM_SCHEMA" "homeassistant.components.sensor.PLATFORM_SCHEMA" = "SENSOR_PLATFORM_SCHEMA" "homeassistant.components.weather.PLATFORM_SCHEMA" = "WEATHER_PLATFORM_SCHEMA" "homeassistant.core.DOMAIN" = "HOMEASSISTANT_DOMAIN" "homeassistant.helpers.area_registry" = "ar" "homeassistant.helpers.category_registry" = "cr" "homeassistant.helpers.config_validation" = "cv" "homeassistant.helpers.device_registry" = "dr" "homeassistant.helpers.entity_registry" = "er" "homeassistant.helpers.floor_registry" = "fr" "homeassistant.helpers.issue_registry" = "ir" "homeassistant.helpers.label_registry" = "lr" "homeassistant.util.dt" = "dt_util" [lint.flake8-pytest-style] fixture-parentheses = false mark-parentheses = false [lint.flake8-tidy-imports.banned-api] "async_timeout".msg = "use asyncio.timeout instead" "pytz".msg = "use zoneinfo instead" "tests".msg = "You should not import tests" [lint.isort] force-sort-within-sections = true known-first-party = ["homeassistant"] combine-as-imports = true split-on-trailing-comma = false [lint.per-file-ignores] # Temporary, as in core: this integration has not migrated to pathlib. "custom_components/**" = ["PTH"] # The integration code satisfies the full rule set above. The test suite does # not, and is relaxed here in two distinct ways. # # Deliberate, permanent exemptions - these are how this suite is written: # D100, D103 - test modules and test functions carry their meaning in names # INP001 - tests/ is intentionally not a package (no __init__.py) # PLC0415 - function-local imports keep import-time patching honest # PTH - as above # SLF001 - tests assert directly on private helpers # # Deferred nits - genuine violations that exist in the tests today. They are # suppressed rather than fixed to keep the introduction of this configuration a # config-only change with no source edits. Remove each entry as it is cleaned # up; none of them affect the integration code: # B007, C420, D401, D413, I001, PLW0108, PT006, PYI034, SIM300, UP035, UP037 "tests/**" = [ "B007", "C420", "D100", "D103", "D401", "D413", "I001", "INP001", "PLC0415", "PLW0108", "PT006", "PTH", "PYI034", "SIM300", "SLF001", "UP035", "UP037", ] [lint.mccabe] max-complexity = 25 [lint.pydocstyle] property-decorators = ["propcache.cached_property"]