From c893a98feaebe5b661296d8b341c37ef8a00f8df Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Thu, 20 Aug 2026 15:55:54 +0200 Subject: [PATCH 1/3] docs: explain what `query-report` reports and where the query name comes from (#1253) The command's help said only that it runs a query through InfrahubGraphQLQueryReport, which names an internal type and tells a user nothing about what the answer means. Describe the consequence first: artifact and generator definitions use the verdict to decide how much to regenerate when data changes. State the rule that produces it, note that NAME comes from the queries section of .infrahub.yml, and record on --branch that the answer is branch-dependent because uniqueness constraints live in the schema. --- docs/docs/infrahubctl/infrahubctl-graphql.mdx | 16 +++++++++---- infrahub_sdk/ctl/graphql.py | 23 ++++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/docs/docs/infrahubctl/infrahubctl-graphql.mdx b/docs/docs/infrahubctl/infrahubctl-graphql.mdx index e97d5216b..143fc027d 100644 --- a/docs/docs/infrahubctl/infrahubctl-graphql.mdx +++ b/docs/docs/infrahubctl/infrahubctl-graphql.mdx @@ -16,13 +16,21 @@ $ infrahubctl graphql [OPTIONS] COMMAND [ARGS]... **Commands**: -* `query-report`: Run a GraphQL query through... +* `query-report`: Report how Infrahub will interpret a... * `export-schema`: Export the GraphQL schema to a file. * `generate-return-types`: Create Pydantic Models for GraphQL query... ## `infrahubctl graphql query-report` -Run a GraphQL query through InfrahubGraphQLQueryReport and report its analysis. +Report how Infrahub will interpret a GraphQL query. + +Reports whether the query targets unique nodes, meaning every query it contains returns a +single object. Artifact and generator definitions use this to decide how much to regenerate +when data changes: when it is true, only the artifacts or generator instances of the objects +that changed are regenerated, and when it is false, all of them are. + +A query returns a single object when it filters on ids or hfid, or on every part of one +uniqueness constraint of the model, and the values it filters on are always provided. **Usage**: @@ -32,12 +40,12 @@ $ infrahubctl graphql query-report [OPTIONS] NAME **Arguments**: -* `NAME`: Name of the GraphQL query to analyze. [required] +* `NAME`: Name of the GraphQL query to analyze, as declared under queries in .infrahub.yml. [required] **Options**: * `--online`: Fetch the query from the Infrahub server (CoreGraphQLQuery by name) instead of reading it from the local .infrahub.yml file. -* `--branch TEXT`: Branch on which to run the report. +* `--branch TEXT`: Branch on which to run the report. Uniqueness constraints come from the schema, so the result can differ between branches. * `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml] * `--help`: Show this message and exit. diff --git a/infrahub_sdk/ctl/graphql.py b/infrahub_sdk/ctl/graphql.py index 37d551374..c46a893ea 100644 --- a/infrahub_sdk/ctl/graphql.py +++ b/infrahub_sdk/ctl/graphql.py @@ -112,7 +112,9 @@ def callback() -> None: @app.command(name="query-report") @catch_exception(console=console) async def query_report( - name: str = typer.Argument(..., help="Name of the GraphQL query to analyze."), + name: str = typer.Argument( + ..., help="Name of the GraphQL query to analyze, as declared under queries in .infrahub.yml." + ), online: bool = typer.Option( False, "--online", @@ -121,10 +123,25 @@ async def query_report( "instead of reading it from the local .infrahub.yml file." ), ), - branch: str | None = typer.Option(None, help="Branch on which to run the report."), + branch: str | None = typer.Option( + None, + help=( + "Branch on which to run the report. Uniqueness constraints come from the schema, " + "so the result can differ between branches." + ), + ), _: str = CONFIG_PARAM, ) -> None: - """Run a GraphQL query through InfrahubGraphQLQueryReport and report its analysis.""" + """Report how Infrahub will interpret a GraphQL query. + + Reports whether the query targets unique nodes, meaning every query it contains returns a + single object. Artifact and generator definitions use this to decide how much to regenerate + when data changes: when it is true, only the artifacts or generator instances of the objects + that changed are regenerated, and when it is false, all of them are. + + A query returns a single object when it filters on ids or hfid, or on every part of one + uniqueness constraint of the model, and the values it filters on are always provided. + """ client = initialize_client(branch=branch) if online: From 3518107f59dc234794601b7d42105f35e91888f9 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Fri, 21 Aug 2026 09:49:09 +0200 Subject: [PATCH 2/3] chore: narrow over-broad mypy and ty error suppressions The generated read models were suppressing all of mypy's `misc` code to silence one decorated-property error. mypy split that diagnostic into its own `prop-decorator` code in 1.11, the version pinned here, so scope the override to it. Verified by removing each suppression and re-running the checker, which also surfaced three dead entries (mypy `arg-type` on utils, ty `invalid-argument-type` on test fixtures, ty `no-matching-overload`) and two stale violation counts. --- pyproject.toml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b8941b9a7..f6fc39879 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -163,7 +163,7 @@ invalid-assignment = "ignore" # 1 violation in importer/json.py include = ["infrahub_sdk/node/node.py"] [tool.ty.overrides.rules] -invalid-argument-type = "ignore" # 9 violations - lines 776, 855, 859, 862 +invalid-argument-type = "ignore" # 8 violations [[tool.ty.overrides]] @@ -183,7 +183,6 @@ unused-ignore-comment = "ignore" # Clashes with mypy's type ignore comments include = ["tests/fixtures/**"] [tool.ty.overrides.rules] -invalid-argument-type = "ignore" # Test fixtures - dynamic mock data possibly-missing-attribute = "ignore" # Test fixtures use dynamic attributes # Test-specific overrides - tests have more lenient type checking @@ -231,9 +230,8 @@ include = [ ] [tool.ty.overrides.rules] -invalid-argument-type = "ignore" # 29 violations +invalid-argument-type = "ignore" # 25 violations invalid-assignment = "ignore" -no-matching-overload = "ignore" possibly-missing-attribute = "ignore" [[tool.ty.overrides]] @@ -276,7 +274,7 @@ disable_error_code = ["call-overload"] [[tool.mypy.overrides]] module = "infrahub_sdk.utils" -disable_error_code = ["arg-type", "attr-defined", "return-value", "union-attr"] +disable_error_code = ["attr-defined", "return-value", "union-attr"] [[tool.mypy.overrides]] # ``main.py`` intentionally narrows the ``attributes``/``relationships``/``choices`` fields inherited @@ -290,7 +288,7 @@ disable_error_code = ["assignment"] # The generated read models expose ``kind`` via ``@computed_field`` stacked on ``@property``. mypy # does not support decorators on top of ``@property`` and flags it, but pydantic requires this order. module = "infrahub_sdk.schema.generated.read" -disable_error_code = ["misc"] +disable_error_code = ["prop-decorator"] [tool.ruff] line-length = 120 From 92528ab207698c13fb18d1ad220c0e7bf01b2641 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2026 09:36:07 +0200 Subject: [PATCH 3/3] chore(deps): bump h2 from 4.3.0 to 4.4.1 (#1234) Bumps [h2](https://github.com/python-hyper/h2) from 4.3.0 to 4.4.1. - [Changelog](https://github.com/python-hyper/h2/blob/master/CHANGELOG.rst) - [Commits](https://github.com/python-hyper/h2/compare/v4.3.0...v4.4.1) --- updated-dependencies: - dependency-name: h2 dependency-version: 4.4.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- uv.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/uv.lock b/uv.lock index 201b6daa8..d5ba79e90 100644 --- a/uv.lock +++ b/uv.lock @@ -582,24 +582,24 @@ wheels = [ [[package]] name = "h2" -version = "4.3.0" +version = "4.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "hpack" }, { name = "hyperframe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/85/7c366e69d84c17bb778fe41419e1fbcce3033d5b7ce29bbffff0a98b859f/h2-4.4.1.tar.gz", hash = "sha256:4e866ffb1a869ae14dd9b5e6beb5c24a13da0495ad72b65925ded182521c1516", size = 2157281, upload-time = "2026-08-03T11:45:09.509Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/e85faf23bd72a92d1921e37d674ca56eb298a3c8be31fdecef0ff2b3aaac/h2-4.4.1-py3-none-any.whl", hash = "sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6", size = 62636, upload-time = "2026-08-03T11:44:59.164Z" }, ] [[package]] name = "hpack" -version = "4.1.0" +version = "4.2.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/5b/fcabf6028144a8723726318b07a32c2f3314acdff6265743cf08a344b18e/hpack-4.2.0.tar.gz", hash = "sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0", size = 51300, upload-time = "2026-06-23T18:34:46.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, + { url = "https://files.pythonhosted.org/packages/71/b4/4a9fcfb2aef6ba44d9073ecd301443aa00b3dac95de5619f2a7de7ec8a91/hpack-4.2.0-py3-none-any.whl", hash = "sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986", size = 34246, upload-time = "2026-06-23T18:34:45.472Z" }, ] [[package]]