Component
Python SDK, infrahubctl
Infrahub SDK version
1.20.1
Current Behavior
The documented way to call client.get / client.filters / client.all / client.allocate_next_ip_prefix is to pass a generated protocol class (e.g. CoreIPPrefixPool, NetworkPod) as the kind= argument. Under mypy this emits a type-abstract error on every such call.
The generated protocols ultimately subclass CoreNode, which is a typing.Protocol, and kind is typed type[SchemaType]. mypy forbids passing an abstract/Protocol type where type[X] (an instantiable type) is expected.
Root cause:
infrahub_sdk/protocols_base.py — class CoreNode(CoreNodeBase, Protocol): ...
infrahub_sdk/client.py — SchemaType = TypeVar("SchemaType", bound=CoreNode), and def get(self, kind: type[SchemaType], ...)
Because CoreNode is a Protocol, every generated subclass is non-instantiable from mypy's perspective, so passing it where type[SchemaType] is expected is exactly what type-abstract flags. This affects any downstream project that follows the documented typed-client pattern and runs mypy — it appears on essentially every client call.
Reproduces on both mypy 2.1.0 and mypy 1.17.1 (so it is not a mypy-version regression).
Expected Behavior
Calling the client with a generated protocol as kind= — the documented, intended API — should type-check cleanly without per-call suppressions.
Steps to Reproduce
from infrahub_sdk import InfrahubClient
from infrahub_sdk.protocols import CoreIPPrefixPool
async def main(client: InfrahubClient) -> None:
# Documented usage: pass a generated protocol as `kind=`.
await client.get(kind=CoreIPPrefixPool, name__value="pool")
Run mypy (infrahub-sdk 1.20.1):
error: Only concrete class can be given where "type[CoreIPPrefixPool]" is expected [type-abstract]
await client.get(kind=CoreIPPrefixPool, name__value="pool")
^~~~~~~~~~~~~~~~
Additional Information
Impact
- Affects all SDK consumers using the typed client + mypy.
- Forces either ~one
# type: ignore[type-abstract] per client call, or a project-wide disable_error_code = ["type-abstract"], which also masks genuine abstract-instantiation mistakes elsewhere.
Suggested fixes (for discussion)
- Document the recommended
disable_error_code = ["type-abstract"] (or per-call ignore) as the supported pattern, with rationale, in the typed-client docs.
- Typing-level: provide client method overloads whose
kind parameter accepts protocol types without tripping type-abstract, if a clean expression is achievable.
- Generation-level: reconsider whether generated node classes must be
Protocol (structural) vs concrete nominal classes — concrete classes would not trigger type-abstract. (Broader implications; raised for completeness.)
Option 1 is the cheapest and unblocks consumers immediately.
Downstream workaround (interim)
Per-call suppression (surgical):
pool = await client.get(kind=CoreIPPrefixPool, name__value="pool") # type: ignore[type-abstract]
Or project-wide (one line, but also silences genuine abstract-instantiation mistakes):
# pyproject.toml
[tool.mypy]
disable_error_code = ["type-abstract"]
Two related typing issues were found in the same downstream upgrade (filed separately): RelatedNode.peer returning InfrahubNode (loses peer type on traversal), and relationship-attribute assignment being rejected.
Component
Python SDK, infrahubctl
Infrahub SDK version
1.20.1
Current Behavior
The documented way to call
client.get/client.filters/client.all/client.allocate_next_ip_prefixis to pass a generated protocol class (e.g.CoreIPPrefixPool,NetworkPod) as thekind=argument. Under mypy this emits atype-abstracterror on every such call.The generated protocols ultimately subclass
CoreNode, which is atyping.Protocol, andkindis typedtype[SchemaType]. mypy forbids passing an abstract/Protocol type wheretype[X](an instantiable type) is expected.Root cause:
infrahub_sdk/protocols_base.py—class CoreNode(CoreNodeBase, Protocol): ...infrahub_sdk/client.py—SchemaType = TypeVar("SchemaType", bound=CoreNode), anddef get(self, kind: type[SchemaType], ...)Because
CoreNodeis aProtocol, every generated subclass is non-instantiable from mypy's perspective, so passing it wheretype[SchemaType]is expected is exactly whattype-abstractflags. This affects any downstream project that follows the documented typed-client pattern and runs mypy — it appears on essentially every client call.Reproduces on both mypy 2.1.0 and mypy 1.17.1 (so it is not a mypy-version regression).
Expected Behavior
Calling the client with a generated protocol as
kind=— the documented, intended API — should type-check cleanly without per-call suppressions.Steps to Reproduce
Run mypy (infrahub-sdk 1.20.1):
Additional Information
Impact
# type: ignore[type-abstract]per client call, or a project-widedisable_error_code = ["type-abstract"], which also masks genuine abstract-instantiation mistakes elsewhere.Suggested fixes (for discussion)
disable_error_code = ["type-abstract"](or per-call ignore) as the supported pattern, with rationale, in the typed-client docs.kindparameter accepts protocol types without trippingtype-abstract, if a clean expression is achievable.Protocol(structural) vs concrete nominal classes — concrete classes would not triggertype-abstract. (Broader implications; raised for completeness.)Option 1 is the cheapest and unblocks consumers immediately.
Downstream workaround (interim)
Per-call suppression (surgical):
Or project-wide (one line, but also silences genuine abstract-instantiation mistakes):
Two related typing issues were found in the same downstream upgrade (filed separately):
RelatedNode.peerreturningInfrahubNode(loses peer type on traversal), and relationship-attribute assignment being rejected.