ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Home Assistant Core 自定义 Pylint 插件全解析:70+ 条 74xx 规则如何把集成质量规范写成代码

Home Assistant Core 自定义 Pylint 插件全解析:70+ 条 74xx 规则如何把集成质量规范写成代码 Home Assistant Core 自定义 Pylint 插件全解析70 条 74xx 规则如何把集成质量规范写成代码【免费下载链接】core:house_with_garden: Open source home automation that puts local control and privacy first.项目地址: https://gitcode.com/GitHub_Trending/co/core本文基于 Home Assistant Core 仓库中的 pylint/plugins/README.md 及其插件源码展开系统讲解这套专为 Home Assistant 集成integration开发的 Pylint 插件它为何不能只用 Ruff 替代、如何在pyproject.toml中加载、{C,W,E,R}74{00-99}规则编号体系以及 70 余条规则各自的检查逻辑与源码级实现原理。读完后你将能够完整掌握该插件的每条home-assistant-*规则的触发条件、豁免场景与禁用方式并理解其跨文件分析、类型推断、AST 变量追踪三类 Ruff 无法覆盖的检查能力。一、插件定位Ruff 做不了的检查交给 PylintHome Assistant 的 CI/CD 流水线中同时使用 Ruff 与 Pylint 两套工具。Ruff 负责快速、单文件的 lintimport 排序、格式化、常见 Python 问题而本插件扩展 Pylint覆盖那些无法用 Ruff 规则表达的模式原因有三类引自 README跨文件分析读取manifest.json检查integration_type、读取quality_scale.yaml验证集成对质量等级规则的声明类型推断例如通过 import 解析出_pytest.fixtures.FixtureFunctionMarker这类装饰器名称复杂 AST 模式追踪变量赋值到 API 调用的完整链路例如检测到data[CONF_HOST]赋值的变量后来流入async_set_unique_id()。该插件被明确定位为Home Assistant Core 内部 CI/CD 使用不适用于 lint 外部自定义集成仓库。二、加载机制init-hookload-plugins插件的加载依赖 pyproject.toml 根目录下[tool.pylint.MAIN]中的两段配置[tool.pylint.MAIN] py-version 3.14 jobs 2 init-hook \ from pathlib import Path; \ import sys; \ from pylint.config import find_default_config_files; \ sys.path.append( \ str(Path(next(find_default_config_files())).parent.joinpath(pylint/plugins)) \ ) \ load-plugins [ pylint.extensions.code_style, pylint.extensions.typing, pylint_home_assistant, pylint_per_file_ignores, ] persistent false fail-on [I]init-hook在 Pylint 解析配置时执行把pylint/plugins目录追加到sys.path使pylint_home_assistant模块可被导入load-plugins按列表加载插件pylint_home_assistant即本插件包从源码结构看插件入口是 pylint_home_assistant/__init__.py 中的register(linter)函数它用pkgutil.walk_packages递归遍历checkers/子包凡定义了register函数的模块都会被加载注册同时对比linter._checkers中已有的 checker 类型跳过重复注册用于 Pylint 并行模式-j 2下 worker 进程的重注册场景。这种自动发现机制意味着新增 checker 只需在checkers/下建模块并定义register无需修改入口文件。插件包自身的元数据见 pylint/plugins/pyproject.toml包名pylint-home-assistant依赖astroid4.0、pylint4.0要求requires-python 3.14.0——即该插件与 Home Assistant Core 当前的 Python 3.14 运行时前提绑定。插件包内部结构目录即职责pylint/plugins/pylint_home_assistant/ ├── checkers/ # 所有规则实现 │ ├── actions/ # 服务注册、吞异常 │ ├── config_flow/ # 配置流程相关检查 │ ├── quality_scale/ # 质量等级门控检查 │ ├── tests/ # 测试代码规范 │ ├── type_hints/ # 参数/返回值类型检查 │ ├── logger.py, imports.py, inheritance.py, runtime_data.py ... ├── helpers/ # 共享工具manifest/quality_scale/AST 解析 │ ├── integration.py # 定位集成目录、has_config_flow │ ├── quality_scale.py # quality_scale.yaml 读取与缓存 │ └── module_info.py # 模块路径解析config_flow、sensor 等 ├── const.py # Platform/Module/QualityScaleRule 枚举 └── __init__.py # 插件入口自动发现 register其中 const.py 把homeassistant.const.Platform等平台名内联成Platform枚举使插件包不依赖homeassistant包本身QualityScaleTier枚举定义了 Bronze/Silver/Gold/Platinum 四个等级QualityScaleRule枚举则列出了全部质量等级规则名供门控类 checker 使用。三、规则编号体系{C,W,E,R}74{00-99}每条检查遵循 Pylint 官方自定义 checker 编号约定74是 Home Assistant 的基础 ID首位字母表示级别——C Convention约定、W Warning警告、E Error错误、R Refactor重构。完整规则总表如下继承自 README 规则表禁用规则时请使用名称而非错误码CodeRuleDescriptionC7401home-assistant-logger-periodLogger 消息不得以句号结尾C7402home-assistant-logger-capitalLogger 消息须以大写字母开头debug 级别除外C7403home-assistant-relative-import集成内部应使用相对导入C7404home-assistant-absolute-import跨集成引用应使用绝对导入C7405home-assistant-component-root-import禁止导入其他集成的内部模块C7406home-assistant-helper-namespace-import使用 helper 命名空间导入模式C7407home-assistant-import-constant-alias带别名的 DOMAIN 导入需使用描述性别名C7408home-assistant-import-constant-unnecessary-alias同集成内导入 DOMAIN 的冗余别名C7409home-assistant-enforce-sorted-platformsPLATFORMS 列表必须按字母序排序C7410home-assistant-enforce-greek-micro-char使用希腊字母 μU03BC而非 ANSI micro 符号U00B5C7411home-assistant-enforce-class-module实体类应放在正确的平台模块中C7412home-assistant-entity-description-redundant-defaultEntityDescription 字段设为默认值是冗余的C7413home-assistant-duplicate-const常量与homeassistant.const中同值常量重复C7414home-assistant-enforce-utcnow使用homeassistant.util.dt.utcnow而非datetime.now(UTC)C7415home-assistant-domain-argument测试中 domain 参数应为 domain 常量或变量C7425home-assistant-enforce-now使用homeassistant.util.dt.now而非datetime.now(tz)C7427home-assistant-enforce-naive-now使用homeassistant.util.dt.naive_now而非datetime.now()E7401home-assistant-invalid-inheritance无效的实体类继承链E7402home-assistant-argument-type函数参数应使用指定的类型提示E7403home-assistant-return-type函数应使用指定的返回类型提示E7404home-assistant-missing-super-call方法必须通过super()调用父类实现E7405home-assistant-action-swallowed-exceptionAction handler 不得吞掉异常E7406home-assistant-exception-translation-key-missing翻译键在strings.jsonexceptions 段中不存在E7408home-assistant-exception-translation-key-domain-mismatchtranslation_key/translation_domain只设置了其一E7409home-assistant-mdi-icon-not-foundMDI 图标字符串在 Material Design Icons 集合中不存在E7410home-assistant-mdi-icon-json-not-foundicons.json中的 MDI 图标在集合中不存在E7418home-assistant-exception-placeholder-mismatch代码中的翻译占位符与strings.json不匹配R7401home-assistant-consider-usefixtures-decorator未使用的 fixture 应使用pytest.mark.usefixturesR7402home-assistant-unused-test-fixture-argument未使用的测试参数应使用pytest.mark.usefixturesR7403home-assistant-tests-redundant-usefixturespytestmark已生效时pytest.mark.usefixtures冗余R7404home-assistant-tests-registry-fixtures测试中应使用 registry fixture 而非直接调用registry.async_get(hass)W7401home-assistant-deprecated-import导入使用了已废弃路径W7402home-assistant-async-callback-decorator协程函数不应加callback装饰器W7403home-assistant-pytest-fixture-decoratorPytest fixture 的 scope 或 autouse 配置非法W7404home-assistant-async-load-fixtures测试 fixture 文件应异步加载W7405home-assistant-use-runtime-data使用entry.runtime_data替代hass.data[DOMAIN]W7406home-assistant-unique-id-ip-basedunique ID 不应基于 IP/主机名W7407home-assistant-config-flow-polling-field配置流程不应包含轮询间隔字段W7408home-assistant-config-flow-name-field配置流程不应包含名称字段W7409home-assistant-test-non-deterministic测试含产生非确定性执行的if/matchW7410home-assistant-missing-reauthentication-flow配置流程应实现async_step_reauthW7411home-assistant-missing-parallel-updates平台模块应定义PARALLEL_UPDATESW7412home-assistant-missing-diagnostics集成 diagnostics 模块应实现诊断函数W7413home-assistant-missing-config-entry-unloading集成应实现async_unload_entryW7414home-assistant-service-registered-in-setup-entry服务应注册在async_setup而非async_setup_entryW7415home-assistant-sequential-executor-jobs连续async_add_executor_job调用应合并W7416home-assistant-missing-has-entity-name实体类应设置_attr_has_entity_name TrueW7417home-assistant-exception-not-translatedHomeAssistantError应使用translation_key/translation_domainW7418home-assistant-tests-direct-async-setup-entry测试不应直接调用集成的async_setup_entryW7419home-assistant-exception-message-with-translation设置translation_key时不要传位置参数消息W7420home-assistant-tests-direct-platform-async-setup-entry测试不应直接调用平台的async_setup_entryW7421home-assistant-tests-direct-async-migrate-entry测试不应直接调用async_migrate_entryW7422home-assistant-tests-direct-async-setup测试不应直接调用async_setupW7423home-assistant-missing-entity-unique-id实体类未静态保证 unique id 非 NoneW7424home-assistant-entity-unique-id-static实体类在类级别将_attr_unique_id设为静态字符串W7425home-assistant-entity-unique-id-redundant-domainunique ID 冗余地引用了DOMAIN常量或集成 domain 片段W7426home-assistant-tests-direct-async-unload-entry测试不应直接调用async_unload_entryW7427home-assistant-entity-unique-id-redundant-platformunique ID 冗余地包含平台名如sensor、light片段W7428home-assistant-config-flow-field-not-translated配置流程表单字段在strings.json中缺少翻译W7429home-assistant-unnecessary-format-macCONNECTION_NETWORK_MAC下format_mac()是多余的W7430home-assistant-serial-port-selector-usb-dependency使用SerialPortSelector的配置流程必须在dependencies中声明usbW7431home-assistant-options-flow-field-not-translatedOptions flow 表单字段缺少翻译W7432home-assistant-subentry-flow-field-not-translatedSubentry flow 表单字段缺少翻译W7433home-assistant-missing-test-before-configure配置流程应在创建 entry 前测试连接W7434home-assistant-config-flow-menu-missing-stepasync_show_menu的 option 没有对应async_step_*方法W7435home-assistant-json-fixture应使用 JSON fixture 辅助函数而非手动解析已加载 fixtureW7436home-assistant-light-missing-color-modeLight 设置了 supported color modes 但未报告color_modeW7437home-assistant-light-missing-supported-color-modesLight 报告了color_mode但未设置 supported color modes四、典型规则的源码级实现解析下面选取几类有代表性的 checker展示规则如何落到 AST 访问器上。4.1 Logger 风格C7401 / C7402checkers/logger.py 中的HassLoggerFormatChecker只实现了一个visit_call访问器逻辑非常紧凑仅处理LOGGER或_LOGGER的属性调用node.func必须是Attribute且表达式名为两者之一取第一个位置参数且必须是字符串常量消息以.结尾则报home-assistant-logger-periodC7401方法名不在(debug,)白名单内、且首字母不是大写则报home-assistant-logger-capitalC7402——debug 级别豁免首字母大写要求。这条规则解释了仓库中所有集成日志首字母大写、无尾句号的统一风格来源也是纯文本约束、无需类型推断的轻量级规则范本。4.2 变量追踪W7406 基于 IP 的 unique IDcheckers/config_flow/unique_id_no_ip.py 是 README 中复杂 AST 模式的典型实现作用域限定parse_module(node.root().name)必须解析为config_flow模块函数必须是async_set_unique_id支持位置参数与unique_id关键字参数两种形式直接检查_IP_HOST_NAMES集合CONF_HOST、CONF_IP_ADDRESS、CONF_URL、host、hostname、ip、ip_address并递归处理data[CONF_HOST]下标、data.get(host)调用以及 f-string 内嵌引用递归时会跳过函数调用参数——因为get_unique_id(host)这样的调用会转换值结果不再是 IP变量回溯当参数只是一个变量名如uid时_resolve_variable_ip_ref向上找到 enclosing function扫描该函数内所有对该变量的赋值对赋值右值再做上述 IP 引用检查。这就实现了 README 所述检测到uid data[CONF_HOST]之后流入async_set_unique_id(uid)的能力——单文件 linter 靠简单模式匹配难以完成这种跨语句的数据流追踪。4.3 现代化数据存取W7405entry.runtime_datacheckers/runtime_data.py 检查hass.data[DOMAIN]的三种访问形态下标、hass.data.setdefault(DOMAIN, ...)、hass.data.get(DOMAIN)。源码中有两处值得注意的豁免逻辑删除不报父节点为Delete或pop调用时跳过async_unload_entry清理数据是合法操作模块/函数豁免application_credentials、config_flow、const、diagnostics模块以及async_migrate_entry、async_remove_entry、async_unload_entry函数内跳过只约束有 config flow 的集成通过helpers/integration.py的has_config_flow读取manifest.json判断纯 YAML 集成不被要求迁移——这正是跨文件分析能力checker 必须离开当前 AST去读同目录的manifest.json。4.4 质量等级门控quality_scale.yaml驱动的按需检查W7410reauthentication-flowSilver、W7411parallel-updatesSilver、W7412diagnosticsGold、W7413config-entry-unloadingSilver、W7416has-entity-nameBronze、W7423entity-unique-idBronze、W7433test-before-configureBronze等规则都是质量等级门控检查只有当集成的quality_scale.yaml中把对应规则标记为done时才会触发。实现位于 helpers/quality_scale.py_load_quality_scale读取集成目录下的quality_scale.yaml并按目录路径缓存_quality_scale_cache测试可用clear_quality_scale_cache清空_get_rule_status兼容两种写法——rules.rule: done与rules.rule: {status: done}quality_scale_rule_is_done与quality_scale_rule_is_done_or_exempt分别提供严格done与done/exempt两种判定。这种设计的意义在于规则只在集成主动承诺达标后才开始执法避免了插件对存量代码的海量误伤。规则名与等级对应关系可直接在 const.py 的QualityScaleRule/QualityScaleTier中查证。4.5 unique ID 静态保证与格式规则entity_unique_id相关规则分两组门控组quality_scale.yaml声明entity-unique-id: done后生效W7423实体类未静态保证 unique id 非 None。认可的写法有三类体中_attr_unique_id 非 None 值方法体中所有成功路径都执行self._attr_unique_id ...顶层或if/else双分支if cond: return的早退守卫会破坏保证而被拒绝if cond: raise ...则被接受或unique_id属性/方法重写。子类显式_attr_unique_id None会覆盖祖先的非 None 值并被标记同一模块内被其他类继承的 Mixin/抽象基类豁免。W7424类体把_attr_unique_id设为字面量字符串且manifest.json未声明single_config_entry: true。因为 unique id 的唯一定域是(domain, platform)且跨该集成所有 config entry 共享多 entry 集成第二个 entry 就会碰撞。非门控组checkers/entity_unique_id_format.pyW7425unique ID 冗余引用集成 domain。注册表键本身已含集成名manifest.json的domain字段因此f{DOMAIN}_{entry.entry_id}或集成名为myhub时写fmyhub-{device_id}都是冗余。片段判定要求边界为非字母数字字符_ - . :、空格或字符串边界所以myhubitat_...、myhub2不会误报。扫描位置有三类体赋值、方法体内self._attr_unique_id ...、unique_id属性/方法中的return带别名的DOMAIN导入不扫描。W7427类似地unique ID 包含实体平台名sensor、light等源自实体所在模块作为分隔片段也被标记因为注册表键的domain字段就是平台名。作用域比 W7425 窄只检查集成子模块路径本身就是平台名的文件sensor.py单文件或sensor/包entity.py、根__init__.py因平台上下文不明确而不查。4.6 其他值得注意的检查器异常翻译checkers/exception_translations.pyW7417 标记用硬编码消息抛HomeAssistantError质量等级门控、W7419 标记设置了translation_key又传位置参数消息、E7406/E7408/E7418 则跨文件核对strings.json的exceptions段——键是否存在、translation_key与translation_domain是否成对、translation_placeholders{...}与消息中的{placeholder}槽位是否一一对应。表单翻译checkers/flow_translations.pyW7428/W7431/W7432 要求async_show_form(data_schema...)的每个键在strings.json中有对应路径的翻译config.step.step_id.data.field、options.step.step_id.data.field、config_subentries.type.step.step_id.data.fieldsection 字段再加.sections.key。subentry 类型通过查找ConfigFlow类的async_get_supported_subentry_types并映射 handler 类名到类型键来解析。MDI 图标checkers/mdi_icons.pyE7409/E7410 校验代码与icons.json中的mdi:图标确实存在于 Material Design Icons 集合图标集合数据由插件的generated/子包提供避免运行时网络查询。配置流程字段W7407 禁止CONF_SCAN_INTERVAL/update_interval/refresh_interval等轮询字段轮询频率由集成作者依据 API 限速与设备能力决定W7408 禁止名称字段CONF_NAME、name、CONF_DEVICE_NAME等integration_type: helper的 helper 集成与ConfigSubentryFlow子类豁免——名称应来自设备发现或集成代码本身。菜单选项W7434 校验self.async_show_menu(menu_options...)的每个 option 都存在对应async_step_option方法含祖先类缺失会导致用户点击时抛出UnknownStep只检查可静态解析的字面量 list/tuple/set 或字面量 dict动态构造形式跳过以避免误报。Light 颜色模式W7436/W7437 捕获设置了supported_color_modes却没有color_mode或反之的半截状态——运行时会直接抛HomeAssistantError。判定按 MRO 解析有效声明子类_attr_... None会否定继承值两者都缺失的类故意不报因为最典型的场景是抽象基类报了反而是误伤。设备注册表 MACW7429 标记connections关键字参数内CONNECTION_NETWORK_MAC元组里多余的format_mac()调用因为设备注册表在存储前会用_normalize_connections()归一化而直接拿元组与device.connections做in判断/集合求交的场景不报——那种比较绕过了归一化确实需要format_mac()。USB 依赖W7430 要求使用SerialPortSelector的配置流程把usb写进dependencies硬依赖而非after_dependencies因为该选择器依赖usb/list_serial_portswebsocket 命令只有usb集成被设置后才注册。测试规范W7418/W7420/W7421/W7422/W7426 禁止测试直接调用集成的async_setup_entry/平台async_setup_entry/async_migrate_entry/async_setup/async_unload_entry应改为await hass.config_entries.async_setup(entry.entry_id)等让真实的 setup/migration/unload 管线平台加载、服务、监听器、runtime_data清理被真正执行W7409 标记测试函数体内的if/matchmatch无任何豁免if的豁免包括return/raise/pytest.skip/xfail/fail守卫、引用函数参数的条件、不含assert的分支推荐pytest.mark.parametrizeW7435 要求使用load_json_value_fixture/async_load_json_object_fixture等专用辅助而非json.loads(load_fixture(...))tests/common.py 中的 helper 定义方自身豁免R7404 要求测试请求tests/conftest.py中定义的area_registry、device_registry、entity_registry等 fixture而不是er.async_get(hass)。执行器作业W7415 把连续出现不被if/try/with/for打断的多个async_add_executor_job调用合并成单个执行器作业避免阻塞调用之间不必要的回到事件循环的上下文切换。时间获取三件套C7414utcnow、C7425now(tz)、C7427无参now()/now(None)把datetime.now的各种裸调用分别路由到homeassistant.util.dt的三个辅助函数其中utcnow底层就是functools.partial(datetime.datetime.now, UTC)收益在于避免每次调用做UTC全局查找并统一代码库风格。服务注册W7414 要求服务注册在async_setup而非async_setup_entry即使注册藏在被async_setup_entry调用的辅助函数里也会被捕获目的是让自动化校验在没有任何 config entry 时仍可用。Light 之外的实体结构规则E7401 阻止sensor.py继承BinarySensorEntity这类跨平台继承C7411 要求平台实体类放在对应平台模块而非__init__.pyE7404 强制某些实体方法super()调父类C7412 标记把EntityDescription字段显式赋为None/True/False默认值的冗余赋值只检查这三类字面量默认C7413 标记与homeassistant.const同值重复定义的常量。默认禁用R7402未使用的测试 fixture 参数应改用pytest.mark.usefixtures在存量违规清理完成前默认禁用。五、如何禁用规则README 明确要求禁用时一律使用规则名如home-assistant-logger-period而不是错误码如C7401以保持可读性。三种作用域写法单行——行尾 disable 注释hass.data[DOMAIN] data # pylint: disablehome-assistant-use-runtime-data仅下一行——行内注释会使行过长时用上一行的disable-next# pylint: disable-nexthome-assistant-use-runtime-data hass.data[DOMAIN] data整个模块——放在文件顶部、模块 docstring 之后My integration setup. # pylint: disablehome-assistant-use-runtime-data对于静态分析确实无法跟随的动态模式如 W7423 说明文档所建议把 disable 注释直接放在类声明行上是最小粒度的逃生舱。六、运行与验证前提结合 pyproject.toml 的 Pylint 配置运行该插件的检查需要满足仓库根目录的[tool.pylint.MAIN]配置生效init-hook定位依赖当前配置文件位于仓库根这一前提find_default_config_files()找到的配置文件的上一级目录 pylint/plugins才会被加入sys.pathPython 3.14 环境py-version 3.14、插件requires-python 3.14.0与astroid4.0、pylint4.0fail-on [I]表示以 Info 级别消息也作为失败原因jobs 2提供默认并行度README 注释说明可按需通过命令行覆盖插件自身的 checker 实现位于 pylint/plugins/pylint_home_assistant/checkers/共享工具位于 pylint/plugins/pylint_home_assistant/helpers/规则总数与编号以上文总表为准。七、小结这套插件把 Home Assistant 的集成质量规范——日志风格、导入约定、runtime_data迁移、unique ID 稳定性、质量等级规则承诺、异常与表单翻译完整性、测试写法——全部编码成了 70 条可执行的静态检查。它的工程价值不在于规则条数而在于三类能力读manifest.json/quality_scale.yaml/strings.json的跨文件验证、通过 import 解析的类型推断、以及变量赋值到 API 调用的 AST 数据流追踪。对维护者而言新增一条规范的成本就是在checkers/下加一个带register的模块对集成开发者而言规则总表与 disable 语法就是日常开发手册。【免费下载链接】core:house_with_garden: Open source home automation that puts local control and privacy first.项目地址: https://gitcode.com/GitHub_Trending/co/core创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表