Skip to content

v4: close out all remaining v4 issues#278

Merged
jtdub merged 10 commits into
nextfrom
v4-remaining
Jul 18, 2026
Merged

v4: close out all remaining v4 issues#278
jtdub merged 10 commits into
nextfrom
v4-remaining

Conversation

@jtdub

@jtdub jtdub commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

One large PR completing the remaining v4 work. Each issue was re-evaluated on its merits; where the issue's proposed approach wasn't the best fit, the deviation is explained below and in CHANGELOG.md.

Closes #226, closes #229, closes #218, closes #220, closes #215, closes #217, closes #186, closes #180, closes #181, closes #232, closes #227, closes #230, closes #222, closes #223, closes #224.

Implemented

Documented decisions (see CHANGELOG "v4 design decisions")

Test plan

  • poetry run ./scripts/build.py lint-and-test — ruff, mypy strict, pyright strict, pylint, yamllint, flynt all pass; 660 tests, 98% coverage (≥95% required).
  • New unit tests: registry (custom platforms, overrides, restore), classmethod constructors, unified negation strategies, root duplicates, transform callbacks, plugins, format detection, non-mutation guarantee, per-platform view capability + behavior tests.

🤖 Generated with Claude Code

jtdub and others added 5 commits July 18, 2026 15:48
…218)

- New hier_config.registry: register_driver()/unregister_driver()/
  get_registered_platforms(). Custom platforms register by string name
  (case-insensitive, usable anywhere a Platform is accepted); built-in
  drivers can be overridden and restored. Built-ins register at import.
  Re-evaluated vs the issue's runtime Platform-enum extension: mutating
  Enum classes at runtime is fragile; a registry keyed by Platform | str
  provides the same capability safely.
- Registered drivers carry their view via view_class, completing #229.
- HConfig.from_text()/from_lines()/from_dump() classmethods replace
  get_hconfig()/get_hconfig_fast_load()/get_hconfig_from_dump()/
  get_hconfig_fast_generic_load() (#218). get_hconfig_driver() and
  get_hconfig_view() remain standalone utilities.
- HConfigDriverBase and HConfigDriverRules are exported as public API.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
)

NegationDefaultWithRule, NegationDefaultWhenRule, and NegationSubRule
collapse into one NegationRule model with a NegationStrategy enum
(REPLACE, DEFAULT, REGEX_SUB) and a single `negation` list on
HConfigDriverRules. Precedence is now explicit list order - first
matching rule wins - instead of being encoded in method ordering.

driver.negate_with() remains as the imperative extension hook (used by
HP ProCurve) and reads REPLACE-strategy rules from the unified list.
load_driver_rules() still accepts the v2 dict keys
(negation_negate_with, negation_default_when, negation_sub) and maps
them onto unified rules for Nautobot compatibility.

Also silences pylint cyclic-import, which now only fires on the lazy
(call-time) imports inside HConfig's classmethod constructors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… format detection (#215, #217, #186, #180, #181, #232)

- #215: a ParentAllowsDuplicateChildRule with empty match_rules now applies
  to the root, unifying root-level duplicate handling with the existing
  child mechanism (children can never match an empty rule because lineage
  matching requires equal lengths).
- #217: the diff/remediation/future/with_tags algorithms move from
  HConfigBase into hier_config/tree_algorithms.py as standalone functions
  (compute_difference, compute_remediation, compute_future,
  compute_with_tags); HConfigBase keeps thin delegating methods.
- #186: _load_from_string_lines() is now a thin wrapper over a stateful
  _ConfigTextLoader class with focused methods for banner processing,
  line normalization, and hierarchy construction; drops the C901 noqa.
- #180: HConfigDriverRules.remediation_transform_callbacks run on the
  remediation config in WorkflowRemediation.remediation_config.
- #181: RemediationPlugin ABC (hier_config.plugins); user plugins are
  passed via WorkflowRemediation(plugins=...) and applied after driver
  callbacks.
- #232: XML and JSON inputs are detected in HConfig.from_text() and
  rejected with a clear InvalidConfigError pointing at the supported
  set-style paths (JunOS/VyOS/Nokia SRL preprocessors).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
)

Migrate README, docs/, and CLAUDE.md to the HConfig.from_* constructors,
unified NegationRule, and the driver registry; add a Registering a Custom
Driver section; consolidate the Unreleased changelog with the full v4
change set and the documented design decisions for #222, #223, and #224.
Adds a regression test pinning that remediation() does not mutate its
input configs (#224).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…S/XR views (#227, #230)

ConfigViewInterfaceBase now holds only the core surface (name,
description, enabled, ipv4_interfaces, is_loopback, is_svi, number,
port_number, vrf, plus concrete helpers). Optional capabilities move to
ABC mixins - InterfaceBundleViewMixin, InterfaceVlanViewMixin,
InterfaceNACViewMixin, InterfacePhysicalViewMixin - so users check
capability with isinstance() instead of catching NotImplementedError.
HConfigViewBase.bundle_interface_views and module_numbers are
capability-aware. All mixins are exported as public API.

The EOS, NXOS, and XR views are completed to core + Bundle + Vlan
(addresses in CIDR/mask/XR forms, VRF membership, dot1q subinterfaces,
switchport access/trunk, Port-Channel/Bundle-Ether bundles, plus root
view properties: interface_names_mentioned, ipv4_default_gw, location,
stack_members, vlans). No NotImplementedError stubs remain in those
views; NAC/Physical are simply not inherited where unsupported. IOS
gains bundle_member_interfaces and a case-insensitive is_bundle fix;
ProCurve gains a real bundle_id.

Raise-only tests are replaced with behavior tests parsing real config
snippets and per-platform capability assertions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jtdub
jtdub requested a review from aedwardstx as a code owner July 18, 2026 21:16
jtdub and others added 5 commits July 18, 2026 16:25
- port_number now derives from the letter-stripped `number` property on
  all five platform views, fixing ValueError on slash-less names such as
  Port-channel10, Bundle-Ether10, and Trk1; regression tests added for
  IOS, EOS, NX-OS, and XR.
- NegationRule validates per-strategy fields at construction: REPLACE
  requires `use`, REGEX_SUB requires `search` (empty `replace` stays
  valid for deletion-style substitutions). Previously a REPLACE rule
  with empty `use` silently fell through to swap_negation.
- Align the NegationRule docstring with actual evaluation order (REPLACE
  rules are consulted first via driver.negate_with(), then remaining
  rules in list order).
- Document that the driver registry is unsynchronized (register at
  startup) and that structured-format detection only guards from_text().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gins on callables

Findings from four parallel cleanup reviews (reuse, simplification,
efficiency, altitude) applied:

- Hoist ~390 duplicated lines out of the five platform view files:
  concrete defaults for name/number/port_number/description/enabled/
  is_loopback/is_svi on ConfigViewInterfaceBase; bundle_id and
  bundle_member_interfaces on InterfaceBundleViewMixin keyed by a
  _bundle_membership_prefix hook; native_vlan/tagged_vlans/tagged_all on
  InterfaceVlanViewMixin keyed by an _encapsulation_prefix hook;
  interface_names_mentioned/location/stack_members/vlans defaults on
  HConfigViewBase; shared parse_ipv4_interface() in platforms.functions.
  Platforms keep overrides only where behavior genuinely differs
  (HP ProCurve throughout; XR native_vlan).
- Remove the pass-through _future/_with_tags/_remediation/_difference
  shims from HConfigBase; HConfig calls the tree_algorithms functions
  directly.
- from_lines()/from_dump() build their empty tree via HConfig(driver)
  instead of routing "" through the full text pipeline; the structured-
  format guard now also covers the raw-str form of from_lines() and
  inspects a bounded prefix instead of copying the whole input.
- WorkflowRemediation(plugins=...) accepts plain callables;
  RemediationPlugin gained __call__ as sugar.
- Replace the side-effecting `elif predicate: pass` in _ConfigTextLoader
  with a positive branch.
- Adapt to ruff 0.15.22 (new preview rules from the renovate lock bump):
  noun-phrase property docstrings, per-file PLC0415 ignore for the lazy
  classmethod imports, unused TypeVar removal.

Deliberate skips: single-pass negation evaluation (would change driver-
facing precedence), caching the root duplicate-rule check (rules are
mutated post-init by the documented extension pattern), dropping plugin
name/description metadata (specified by #181), and the full
constructors/root cycle removal (blocked by pydantic runtime type
resolution in driver_base).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
get_child(startswith="") matches any first child, so a platform that
inherits InterfaceBundleViewMixin without declaring
_bundle_membership_prefix would get garbage from the default bundle_id
and bundle_member_interfaces instead of failing inert. Both defaults
now short-circuit when the prefix is unset; pinned by a regression
test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant