feat!: role bases declare their methods instead of stubbing them

A role base used to fill its methods with `raise NotImplementedError`. That is
not neutral under multiple inheritance: the placeholder wins the MRO against a
sibling base's working implementation and silently replaces it. Adding one stub
to a base was therefore a breaking change for every driver mixing that base with
another, and it broke three of them — OpenWrt grew seven forwarding methods,
QNAP one, and OpenMediaVault avoided inheriting StorageDriver at all.

Role bases now declare their surface under `if TYPE_CHECKING` and implement
nothing. There is no longer anything to shadow, so a device can finally say what
it is:

    class QnapQtsDriver(StorageDriver, HypervisorDriver, LinuxDriver):

The order of those bases is the ranking, read back by roles_of(),
role_keys_of() and primary_role_of() in the new roles module. Nothing restates
it: no precedence table, no attribute to override.

Two consequences, both wanted. `hasattr` is a truthful capability probe again,
because a method exists exactly when a driver provided it. And a method that was
never implemented now raises AttributeError rather than NotImplementedError, so
callers should ask before calling.

Shared behaviour moves out of the roles and into function classes, each holding
it once: PackageManagementMixin (was five byte-identical copies),
HealthMetricsMixin (five), ServiceControlMixin, UpdateMixin, NatVpnMixin,
MacAclMixin, FirewallRuleMixin, InterfaceFilterMixin.

BREAKING CHANGE: methods whose contract genuinely differed were renamed apart —
StorageDriver.get_services -> get_storage_services, the storage and hypervisor
snapshot writers -> create/delete/rollback_{volume,vm}_snapshot,
HypervisorDriver.get_storage -> get_vm_storage_pools, get_snapshots ->
get_vm_snapshots, SwitchDriver.get_dot1x_config -> get_dot1x_ports. Two
duplicate names collapsed onto the one already in use: get_pending_updates ->
get_available_updates and remove_package -> uninstall_package.

Also fixes __doc__ being None on all seven role bases: TYPE_LABEL was assigned
above the triple-quoted string, which made it a bare expression rather than a
docstring.
This commit is contained in:
2026-08-21 12:49:45 +07:00
parent ec0612b300
commit d8dbc7a442
26 changed files with 3119 additions and 3084 deletions
+3 -4
View File
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
[project]
name = "napalm-device-types"
version = "0.5.0"
version = "1.0.0"
description = "Abstract device-type base classes for NAPALM drivers"
readme = "README.md"
requires-python = ">=3.9"
requires-python = ">=3.10"
license = { text = "Apache-2.0" }
authors = [
{ name = "Christian Manivong", email = "christian@manivong.de" },
@@ -31,7 +31,6 @@ classifiers = [
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
@@ -61,6 +60,6 @@ where = ["."]
include = ["napalm_device_types*"]
[tool.mypy]
python_version = "3.9"
python_version = "3.10"
strict = true
ignore_missing_imports = true