Skip to content

Conversation

@solababs
Copy link
Contributor

@solababs solababs commented Jan 19, 2026

Fixes #697

Replace protocols with concrete classes to address type-abstract mypy errors

Summary by CodeRabbit

  • Chores

    • Updated Ruff linting tool from v0.11.9 to v0.14.10
  • Refactor

    • Strengthened and expanded type annotations across the SDK for clearer type checking and IDE support
    • Removed type-suppression directives to surface compile-time type issues
    • Converted several protocol-like bases to concrete bases with default behaviors and adjusted schema validation messaging

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

Walkthrough

Ruff version in .pre-commit-config.yaml was bumped from v0.11.9 to v0.14.10. Several type-ignore comments were removed (infrahub_sdk/ctl/branch.py, infrahub_sdk/testing/repository.py). CoreNodeBase, CoreNode, and CoreNodeSync were changed from runtime-checkable Protocols to concrete base classes with methods that raise NotImplementedError. RelatedNodeBase expanded _peer typing and adjusted duck-typed assignment logic. infrahub_sdk/schema/init.py now imports CoreNodeBase and uses a CoreNodeBase subclass check for schema-kind detection, with an updated error message.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'IHS-183: Fix typing errors for protocols' clearly summarizes the main change: converting protocol-based classes to concrete base classes and updating type annotations throughout the SDK to resolve typing issues.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
infrahub_sdk/node/related_node.py (1)

37-47: isinstance check will fail silently for InfrahubNode/InfrahubNodeSync instances.

Line 37 type annotation allows InfrahubNode | InfrahubNodeSync | CoreNodeBase, but line 46's isinstance(data, CoreNodeBase) check will never match InfrahubNode or InfrahubNodeSync because neither inherits from CoreNodeBase. This means the peer assignment is skipped when data is an InfrahubNode or InfrahubNodeSync instance, contradicting the type annotation and creating unexpected behavior. Consider using isinstance(data, (InfrahubNode, InfrahubNodeSync, CoreNodeBase)) or having InfrahubNodeBase inherit from CoreNodeBase to align runtime type checking with the declared types.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 19, 2026

Deploying infrahub-sdk-python with  Cloudflare Pages  Cloudflare Pages

Latest commit: 152d48c
Status: ✅  Deploy successful!
Preview URL: https://724391cd.infrahub-sdk-python.pages.dev
Branch Preview URL: https://sb-20260119-fix-typing-for-p.infrahub-sdk-python.pages.dev

View logs

@codecov
Copy link

codecov bot commented Jan 19, 2026

Codecov Report

❌ Patch coverage is 87.50000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
infrahub_sdk/schema/__init__.py 33.33% 1 Missing and 1 partial ⚠️
@@             Coverage Diff             @@
##           develop     #749      +/-   ##
===========================================
+ Coverage    80.30%   80.35%   +0.05%     
===========================================
  Files          115      115              
  Lines         9874     9872       -2     
  Branches      1518     1512       -6     
===========================================
+ Hits          7929     7933       +4     
  Misses        1417     1417              
+ Partials       528      522       -6     
Flag Coverage Δ
integration-tests 41.30% <18.75%> (+<0.01%) ⬆️
python-3.10 50.90% <18.75%> (-0.01%) ⬇️
python-3.11 50.92% <18.75%> (-0.01%) ⬇️
python-3.12 50.92% <18.75%> (+0.03%) ⬆️
python-3.13 50.90% <18.75%> (+0.01%) ⬆️
python-3.14 52.56% <18.75%> (+0.03%) ⬆️
python-filler-3.12 24.07% <68.75%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
infrahub_sdk/ctl/branch.py 81.13% <ø> (ø)
infrahub_sdk/node/related_node.py 92.44% <100.00%> (ø)
infrahub_sdk/protocols_base.py 78.22% <100.00%> (+4.21%) ⬆️
infrahub_sdk/testing/repository.py 77.77% <ø> (ø)
infrahub_sdk/schema/__init__.py 69.42% <33.33%> (+0.08%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@solababs solababs requested a review from a team January 19, 2026 13:09
self._properties = self._properties_flag + self._properties_object

self._peer = None
self._peer: InfrahubNode | InfrahubNodeSync | InfrahubNodeBase | CoreNodeBase | None = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this be InfrahubNodeBase | CoreNodeBase | None? looks like the other two InfrahubNode classes inherit from InfrahubNodeBase

return schema

if hasattr(schema, "_is_runtime_protocol") and getattr(schema, "_is_runtime_protocol", None):
if isinstance(schema, type) and issubclass(schema, (CoreNode, CoreNodeSync)):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this be issubclass(schema, (CoreNodeBase))?


@runtime_checkable
class CoreNodeBase(Protocol):
class CoreNodeBase:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be worth updating the PR description to explain why these are no longer Protocols

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace protocols with concrete classes to address type-abstract mypy errors

Comment on lines +46 to +49
# Check for InfrahubNodeBase instances using duck-typing (_schema attribute)
# to avoid circular imports, or CoreNodeBase instances
if isinstance(data, CoreNodeBase) or hasattr(data, "_schema"):
self._peer = cast("InfrahubNodeBase | CoreNodeBase", data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it is the best solution right now, but it smells
is the circular import error resolvable? I suspect it is probably pretty tangled, but maybe not

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it is not


def get_kind(self) -> str: ...
def get_kind(self) -> str:
return ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These methods should not be called so as such I think we can just have a raise NotImplementedError instead of having these placeholder returns.

@ogenstad
Copy link
Contributor

One thing that nags me about this is that we still have all of the nodes within a file called protocols.py which could potentially be a bit misleading since they are not protocols any more so it might be a bit confusing. However the only reason we have this is for typing and I don't expect any external protocols to have been used in this context and even if they did we could see that it didn't work as intended for typing.

I don't think that it's worth renaming or moving around the files to correct this confusion. Just wanted to see if you guys had any thoughts around it? (@solababs, @ajtmccarty)

@ajtmccarty
Copy link
Contributor

maybe they could move to models_base.py and models.py, but I'm not sure if that would break backwards compatibility

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.

4 participants