fix(ctl): handle extensions paths in display_schema_load_errors (#1007)#1008
fix(ctl): handle extensions paths in display_schema_load_errors (#1007)#1008iddocohen wants to merge 7 commits into
Conversation
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## stable #1008 +/- ##
==========================================
- Coverage 81.56% 81.52% -0.04%
==========================================
Files 134 134
Lines 11442 11467 +25
Branches 1730 1735 +5
==========================================
+ Hits 9333 9349 +16
- Misses 1564 1570 +6
- Partials 545 548 +3
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 17 files with indirect coverage changes 🚀 New features to boost your workflow:
|
|
Not a review but just highlighting that we also want a towncrier newsfragment under /changelog so that we include what was fixed in the release notes. Also in general when we're fixing bugs it's fine to target the |
Deploying infrahub-sdk-python with
|
| Latest commit: |
3a0584a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://4c10cdcc.infrahub-sdk-python.pages.dev |
| Branch Preview URL: | https://ic-fix-display-schema-load-e.infrahub-sdk-python.pages.dev |
|
I think you'll need to rebase this on |
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
90c534d to
0ab1e4d
Compare
ajtmccarty
left a comment
There was a problem hiding this comment.
thanks for the PR
most of these comments are little things. the testing structure one is larger though
There was a problem hiding this comment.
this would be easier to test if this code was written using dependency injection and then you could skip the patch() calls. sometimes patching is necessary for testing, but we generally try to avoid it when you can achieve a more robust test by moving the test up a layer or two in the test pyramid. in this case testing just display_schema_load_errors is fast, but brittle b/c it assumes a certain response structure from the schema load endpoint. I think that these would be better as integration tests similar to those in python_sdk/tests/integration/test_schema.py, then the test really ensures that the response from the server and the client handling work together correctly. sadly, integration tests are slower, but I think it is worth it in this case b/c we have clearly left schema extensions behind for a while and don't want to again
There was a problem hiding this comment.
Completed a full refactor, particularly around where to display the error, which was needed some time to think about how.
| schemas_data: list[SchemaFile], | ||
| schema_index: int, | ||
| node_index: int, | ||
| container: str = "nodes", |
There was a problem hiding this comment.
optional, b/c we didn't have the before and it's not really your job to clean up the typing, but it would be nice if this were either an Enum or a Literal to make it clear exactly what values are expected here
|
|
||
| if len(tail) == 1: | ||
| loc_type = tail[0] | ||
| error_message = f"{loc_type} ({input_str}) | {error['msg']} ({error['type']})" |
There was a problem hiding this comment.
are we sure that the msg and type keys exist here? if not this will raise a KeyError. it would be safer, and more reasable, to do err_msg = error.get("msg", "" \ "No error message") and then use err_msg and err_type in the f-string.
| if data.get(attribute) is not None: | ||
| return data.get("name", None) | ||
| return None | ||
| return error_data[attribute].get("name", None) |
There was a problem hiding this comment.
not your fault, but error_data.get(attribute,{}).get(...) would be safer
There was a problem hiding this comment.
I believe error_data is a list and not a dict hence .get() will not work but understood the spirt of the concern and will do an out of bound check.
| path_suffix = f" (extensions/{container})" if is_extension else "" | ||
| input_str = error.get("input") | ||
|
|
||
| if len(tail) == 1: |
There was a problem hiding this comment.
also not your fault, but adding a few one-line comments in here would make it easier to understand this pretty opaque error string parsing
…ests Replace patch()-heavy unit tests for display_schema_load_errors with integration tests that exercise the real server response → renderer path, so we catch any drift between the schema-load endpoint's error shape and CLI handling (particularly for extensions paths). Add an optional Console parameter to display_schema_load_errors so tests can capture rendered output via dependency injection instead of patching the module-level console. Keep valid_error_path covered as a fast parametrized unit test with no patchin
Replace on with a Literal of nodes, generics, relationships so the accepted values are explicit at the type level. Matches the set already enforced at runtime by valid_error_path
…notate _render_schema_error parsing branches
ajtmccarty
left a comment
There was a problem hiding this comment.
thanks for the updates. looking good. just a couple more little comments
| return loc_path[3] in {"nodes", "generics"} and isinstance(loc_path[4], int) | ||
|
|
||
|
|
||
| SchemaContainer = Literal["nodes", "generics", "relationships"] |
There was a problem hiding this comment.
generally, this kind of definition goes near the top of the file. but if it is only used in one place, you should be able to do get_node(..., container: Literal["nodes", "generics", "relationships"] = "nodes", ...)
| schemas_data = [SchemaFile(location=Path("broken.yml"), content=broken_schema)] | ||
| buffer = StringIO() | ||
| output = Console(file=buffer, width=1000, force_terminal=False) | ||
| display_schema_load_errors(response=response.errors, schemas_data=schemas_data, output=output) | ||
| rendered = buffer.getvalue() |
There was a problem hiding this comment.
I did not realize it would be such a pain to get the output. is it possible to wrap client.schema.load in something like this from the rich documentation?
from rich.console import Console
console = Console()
with console.capture() as capture:
console.print("[bold red]Hello[/] World")
str_output = capture.get()
There was a problem hiding this comment.
I tried to do that but it didn't work. I will ask Claude for help here. Stay tuned.
…le.capture() and hoist SchemaContainer alias
Why
infrahubctl schema loadcrashes with an unhandledValueErrorwhen theserver rejects a schema that uses the
extensions:block. The traceback replaces thehuman-readable rejection message, so users have no idea what the server
actually rejected.
Goal: Render extensions-block rejections in the same one-line format as
top-level rejections.
Non-goals: No new error categories, no changes to
infrahubctl schema checkor to the server's rejection format, no broader refactor of the CLIschema commands.
Closes #1007
What changed
Behavioral changes
extensions:block now render asNode: <kind> (extensions/<container>) | <field> (<input>) | <msg> (<type>)instead of crashing with
ValueError: invalid literal for int() with base 10.loc_pathshapes (non-int schema index, unrecognisedcontainer) are skipped silently instead of crashing the renderer.
preserved by the existing tests.
Implementation notes
display_schema_load_errorsnow branches onloc_path[3] == "extensions",computes
container / node_index / tailonce, and dispatches onlen(tail)instead of
len(loc_path)so the offset shift doesn't have to be repeated._render_schema_errorto keep the mainloop within the project's branch-count limits.
valid_error_pathnow whitelistsloc_path[3] in {nodes, generics, extensions}and requiresloc_path[2](and the node-index slot) to beint.get_nodegainedcontainerandis_extensionkwargs (defaults match theold behaviour) and uses
payload.get(...)so a schema file with onlyextensions:no longer raisesKeyError.What stayed the same
display_schema_load_errorsunchanged.get_nodesignature is backward-compatible (new params have defaults).How to review
infrahub_sdk/ctl/schema.py— start with_render_schema_error, thenvalid_error_path, thenget_node.tests/unit/sdk/test_schema.py— three new tests cover top-level extensionserrors, nested-attribute extensions errors, and the malformed-path filter.
and still pass — that's the regression guard for the format-stability claim
above.
The
valid_error_pathwhitelist is the area I'd most want a second opinionon: if the server later adds a new top-level container, errors against it
will now be silently skipped instead of bubbling up. The trade-off is
intentional for a CLI rendering helper, but worth flagging.
How to test
Impact & rollout
errors is unchanged.
get_nodegained two optional kwargs with defaults.schema load.Checklist
uv run towncrier create +fix-schema-load-extensions.fixed.md)