From 61331027e16756199b69e67ab23bea6336549d70 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Thu, 29 Jan 2026 13:30:31 +0100 Subject: [PATCH 01/10] =?UTF-8?q?=F0=9F=91=8C=20Improve=20reduce=5Fneed=20?= =?UTF-8?q?for=20schema=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently reduce_need depends on the schema to determine whether core fields should be added to the to-be-validated need. To run unevaluatedProperties validations. This is a performance issue, as the need reduction has to run for all need-schema combinations. --- sphinx_needs/schema/core.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index c3d679cad..112794fbe 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -460,6 +460,7 @@ def reduce_need( :param json_schema: The user provided and merged JSON merge. """ reduced_need: dict[str, Any] = {} + del schema_properties for field, value in need.iter_extra_items(): if value is None: @@ -480,9 +481,7 @@ def reduce_need( # value is not provided continue schema_field = field_properties[field] - if field in schema_properties and not ( - "default" in schema_field and value == schema_field["default"] - ): + if not ("default" in schema_field and value == schema_field["default"]): # keep core field, it has no default or differs from the default and # is part of the user provided schema reduced_need[field] = value From 16bbcebd683d0164414f8edbd5a346d721036fe4 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Fri, 30 Jan 2026 12:10:32 +0100 Subject: [PATCH 02/10] Revert changes in core --- sphinx_needs/schema/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index 112794fbe..c3d679cad 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -460,7 +460,6 @@ def reduce_need( :param json_schema: The user provided and merged JSON merge. """ reduced_need: dict[str, Any] = {} - del schema_properties for field, value in need.iter_extra_items(): if value is None: @@ -481,7 +480,9 @@ def reduce_need( # value is not provided continue schema_field = field_properties[field] - if not ("default" in schema_field and value == schema_field["default"]): + if field in schema_properties and not ( + "default" in schema_field and value == schema_field["default"] + ): # keep core field, it has no default or differs from the default and # is part of the user provided schema reduced_need[field] = value From 774723ca9ab1090d1f3056f1ac3bfcc7a1fbce9d Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Fri, 30 Jan 2026 13:04:02 +0100 Subject: [PATCH 03/10] Add edge case tests for reduce_need and unevaluatedProperties --- sphinx_needs/schema/core.py | 48 ++++++-- tests/schema/__snapshots__/test_schema.ambr | 58 ++++++++- tests/schema/fixtures/unevaluated.yml | 130 +++++++++++++++++++- 3 files changed, 219 insertions(+), 17 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index c3d679cad..b558ed42d 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -442,19 +442,47 @@ def reduce_need( """ Reduce a need to its relevant fields for validation in a specific schema context. - The reduction is required to separated actively set fields from defaults. - Also internal fields shall be removed, if they are not actively used in the schema. - This is required to make unevaluatedProperties work as expected which disallows - additional fields. - - Needs can be reduced in multiple contexts as the need can be primary target of validation - or it can be a link target which might mean only a single field shall be checked for a - specific value. + The reduction is done to separate actively set fields from unset fields. + The validation of unevaluatedProperties requires a need to only have fields that are actively + set, to complain about all additional fields that do not appear in the given schema. Fields are kept - - if they are extra fields and differ from their default value + + - if they are extra fields and not None and differ from their default value - if they are links and the list is not empty - - if they are part of the user provided schema + - if they are core fields, part of the user provided schema and differ from their default value + + Some more details, starting with extra fields: + The default value for unset SN5 extra options is an empty string. + If an option is given in RST without a value, it is set to ''. The same applies if the + option is not part of the RST at all. So it's not possible to + distinguish between an option that is unset or an option that is set but empty. + For unevaluatedProperties this means that '' values are *not* part of the + reduced need. + + SN6 introduced schemas. For backwards compatibility reasons, the behavior is kept + the same for extra options without a schema. But if a schema is defined for an extra + option (it's enough to set schema.type), the field is set to nullable, and the default value + for unset options is None. + Nullable extra fields that are given in RST with an empty value are set to ''. + So it becomes possible to distinguish between unset (None) and set-but-empty ('') options. + In terms of unevaluatedProperties this means that None values are not part of the + reduced need, while '' values are part of it. + + For link fields: + An empty list means that no links of that type are defined. + The default value for link fields is always an empty list and a given, but empty + list field in RST also leads to an empty list in the need. + So it's not possible to distinguish between unset and set-but-empty link fields. + + For core fields: + Core fields do never lead to unevaluatedProperties warnings, as they are always + part of the need, and commonly users do not actively set them. An example is + type_name. So core fields are only kept if they are part of the user provided schema. + The should also meet user expectations for unevaluatedProperties, + as they define the metamodel of the needs using extra fields and extra links, + and want to disallow the appearance of other extra fields and not be bothered + with SN internal fields. :param need: The need to reduce. :param json_schema: The user provided and merged JSON merge. diff --git a/tests/schema/__snapshots__/test_schema.ambr b/tests/schema/__snapshots__/test_schema.ambr index bf35f61c5..98f49bc2d 100644 --- a/tests/schema/__snapshots__/test_schema.ambr +++ b/tests/schema/__snapshots__/test_schema.ambr @@ -4758,7 +4758,7 @@ }), }) # --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_allof_error] +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_allof] ''' ERROR: Need 'IMPL_1' has schema violations: Severity: violation @@ -4768,7 +4768,7 @@ ''' # --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_allof_error].1 +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_allof].1 dict({ 'validated_needs_count': 1, 'validation_warnings': dict({ @@ -4790,7 +4790,7 @@ }), }) # --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error] +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_schema_given_in_rst] ''' ERROR: Need 'IMPL_1' has schema violations: Severity: violation @@ -4800,7 +4800,7 @@ ''' # --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error].1 +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_schema_given_in_rst].1 dict({ 'validated_needs_count': 1, 'validation_warnings': dict({ @@ -4822,3 +4822,53 @@ }), }) # --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_no_schema_given_in_rst] + '' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_no_schema_given_in_rst].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_no_schema_missing_in_rst] + '' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_no_schema_missing_in_rst].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_missing_in_rst] + '' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_missing_in_rst].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_link_empty_value] + '' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_link_empty_value].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_link_missing] + '' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_link_missing].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + }), + }) +# --- diff --git a/tests/schema/fixtures/unevaluated.yml b/tests/schema/fixtures/unevaluated.yml index 50ecc965e..544ab4a2e 100644 --- a/tests/schema/fixtures/unevaluated.yml +++ b/tests/schema/fixtures/unevaluated.yml @@ -1,4 +1,6 @@ -unevaluated_error: +unevaluated_extra_no_schema_missing_in_rst: + # should not complain, the comment field is unset + # as there is no schema, the default is empty string, so unevaluatedProperties does not see it conf: | extensions = ["sphinx_needs"] needs_from_toml = "ubproject.toml" @@ -10,7 +12,6 @@ unevaluated_error: .. impl:: title :id: IMPL_1 :asil: QM - :comment: This is a comment schemas: schemas: - validate: @@ -18,7 +19,79 @@ unevaluated_error: properties: {"asil": {}} unevaluatedProperties: false -unevaluated_allof_error: +unevaluated_extra_no_schema_given_in_rst: + # should not complain, the comment field is set, but as there is no schema; + # the default is also an empty string, so the field is removed from the need and + # unevaluatedProperties does not see it + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.comment] + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :comment: + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + +unevaluated_extra_with_schema_missing_in_rst: + # should not complain, the comment field is missing in the need; + # as it has a schema, the default is None, so the field is removed from validation + # and unevaluatedProperties does not see it + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.comment] + schema.type = "string" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + +unevaluated_error_extra_with_schema_given_in_rst: + # should complain, the comment field is set, and it has a schema, so SN detects it is actively + # given as '', and it will appear in the unevaluatedProperties evaluation as forbidden + # additional field + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.comment] + schema.type = "string" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :comment: + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + +unevaluated_error_allof: + # check whether unevaluatedProperties implementation applies to combined schemas (allOf) + # should complain about "approved" given as additional field, but not comment or asil conf: | extensions = ["sphinx_needs"] needs_from_toml = "ubproject.toml" @@ -42,3 +115,54 @@ unevaluated_allof_error: unevaluatedProperties: false allOf: - properties: {"comment": {}} + +unevaluated_link_empty_value: + # should not complain, the link field is set, but empty, so it is removed from validation + # and unevaluatedProperties does not see it + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [[needs.extra_links]] + option = "relates" + outgoing = "relates" + incoming = "related by" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :relates: + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + +unevaluated_link_missing: + # should not complain, the link field is missing in the need; + # unset link fields are given as empty list [] in the model, so cannot distinguish from default + # which is also []; as it is not set, it is removed from validation and unevaluatedProperties + # does not see it + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [[needs.extra_links]] + option = "relates" + outgoing = "relates" + incoming = "related by" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false From 2f95c3565babfcf2edc19283f9ca3e6b99494dc7 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Fri, 30 Jan 2026 15:33:01 +0100 Subject: [PATCH 04/10] Refine reduce_need logic, tests for unevaluated properties --- sphinx_needs/schema/core.py | 123 ++--- tests/schema/__snapshots__/test_schema.ambr | 490 +++++++++++++++++++- tests/schema/fixtures/unevaluated.yml | 210 ++++++++- 3 files changed, 762 insertions(+), 61 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index b558ed42d..f8e1bb06d 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -439,80 +439,91 @@ def reduce_need( field_properties: Mapping[str, NeedFieldProperties], schema_properties: set[str], ) -> dict[str, Any]: - """ - Reduce a need to its relevant fields for validation in a specific schema context. - - The reduction is done to separate actively set fields from unset fields. - The validation of unevaluatedProperties requires a need to only have fields that are actively - set, to complain about all additional fields that do not appear in the given schema. - - Fields are kept - - - if they are extra fields and not None and differ from their default value - - if they are links and the list is not empty - - if they are core fields, part of the user provided schema and differ from their default value - - Some more details, starting with extra fields: - The default value for unset SN5 extra options is an empty string. - If an option is given in RST without a value, it is set to ''. The same applies if the - option is not part of the RST at all. So it's not possible to - distinguish between an option that is unset or an option that is set but empty. - For unevaluatedProperties this means that '' values are *not* part of the - reduced need. - - SN6 introduced schemas. For backwards compatibility reasons, the behavior is kept - the same for extra options without a schema. But if a schema is defined for an extra - option (it's enough to set schema.type), the field is set to nullable, and the default value - for unset options is None. - Nullable extra fields that are given in RST with an empty value are set to ''. - So it becomes possible to distinguish between unset (None) and set-but-empty ('') options. - In terms of unevaluatedProperties this means that None values are not part of the - reduced need, while '' values are part of it. - - For link fields: - An empty list means that no links of that type are defined. - The default value for link fields is always an empty list and a given, but empty - list field in RST also leads to an empty list in the need. - So it's not possible to distinguish between unset and set-but-empty link fields. - - For core fields: - Core fields do never lead to unevaluatedProperties warnings, as they are always - part of the need, and commonly users do not actively set them. An example is - type_name. So core fields are only kept if they are part of the user provided schema. - The should also meet user expectations for unevaluatedProperties, - as they define the metamodel of the needs using extra fields and extra links, - and want to disallow the appearance of other extra fields and not be bothered - with SN internal fields. - - :param need: The need to reduce. - :param json_schema: The user provided and merged JSON merge. + """Reduce a need to the subset relevant for schema validation. + + The returned mapping is used as the validation instance (especially for + ``unevaluatedProperties``) to focus on user-controlled fields. + + Rules: + + - ``None`` values are always dropped (schema validation cannot handle null types here). + - Any field/link covered by ``schema_properties`` is kept (so it gets validated). + - Extra fields not covered by the schema are kept only if a default is defined and either: + the default is ``None`` (value is non-``None``), or the default is ``""`` and the value is + not ``""``. + - Link fields not covered by the schema are kept only if the list is non-empty. + - Core fields are dropped unless covered by ``schema_properties``, except the core fields + ``status`` and ``tags`` which follow the same keep rule as extra fields. + + Defaults other than ``None``/``""`` are treated as "inactive" during reduction and therefore + only participate in validation if the field is covered by ``schema_properties``. + + SN5 vs SN6 empty-vs-missing semantics for extra options: + + - Without a field schema (SN5-style), unset and present-but-empty (``:opt:``) both become + ``""``, so "unset" and "set-but-empty" cannot be distinguished. + - With a field schema (SN6), unset nullable options default to ``None``, while ``:opt:`` + results in ``""``. This allows distinguishing "unset" from "set-but-empty". + + In this reduction, that distinction matters: for fields not covered by + ``schema_properties``, ``""`` is dropped if the default is also ``""`` (SN5-style), while + ``""`` is kept if the default is ``None`` (SN6-style), so set-but-empty can be treated as + actively set. + + For link fields, missing vs empty cannot be distinguished (both result in ``[]``). + + :param need: Need to reduce. + :param field_properties: Field defaults/types used to detect default values. + :param schema_properties: Set of field names covered by the current schema. + :return: Reduced mapping used as schema validation instance. """ reduced_need: dict[str, Any] = {} for field, value in need.iter_extra_items(): if value is None: - # value is not provided + # field is unset, and schema validation cannot handle null types continue + + if field in schema_properties: + # keep all fields covered by the schema as validation constraints exist + reduced_need[field] = value + continue + schema_field = field_properties[field] - if not ("default" in schema_field and value == schema_field["default"]): - # keep explicitly set extra options + if "default" in schema_field and ( + (schema_field["default"] is None) + or (schema_field["default"] == "" and value != "") + ): + # there is a default and if it is None, and the value is not, keep it + # or if the default is "" and the value is not "", keep it + # this covers the SN5 (no-schema) and SN6 (with-schema) cases reduced_need[field] = value for field, value in need.iter_links_items(): + if field in schema_properties: + reduced_need[field] = value + continue + if value: - # keep non-empty link fields reduced_need[field] = value for field, value in need.iter_core_items(): if value is None: - # value is not provided continue + + if field in schema_properties: + reduced_need[field] = value + continue + schema_field = field_properties[field] - if field in schema_properties and not ( - "default" in schema_field and value == schema_field["default"] + if ( + field in {"status", "tags"} + and "default" in schema_field + and ( + (schema_field["default"] is None) + or (schema_field["default"] == "" and value != "") + ) ): - # keep core field, it has no default or differs from the default and - # is part of the user provided schema reduced_need[field] = value return reduced_need diff --git a/tests/schema/__snapshots__/test_schema.ambr b/tests/schema/__snapshots__/test_schema.ambr index 98f49bc2d..a02657d0b 100644 --- a/tests/schema/__snapshots__/test_schema.ambr +++ b/tests/schema/__snapshots__/test_schema.ambr @@ -2842,17 +2842,47 @@ }) # --- # name: test_schemas[schema/fixtures/extra_links-contains] - '' + ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > contains + Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] + + ''' # --- # name: test_schemas[schema/fixtures/extra_links-contains].1 dict({ 'validated_needs_count': 2, 'validation_warnings': dict({ + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > contains', + 'severity': 'violation', + 'validation_msg': 'None of [] are valid under the given schema', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-contains_error] ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > contains + Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -2882,11 +2912,33 @@ 'type': 'sn_schema_violation', }), ]), + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > contains', + 'severity': 'violation', + 'validation_msg': 'None of [] are valid under the given schema', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-inject_array] ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > minItems + Schema message: [] has less than 2 items [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -2916,6 +2968,22 @@ 'type': 'sn_schema_violation', }), ]), + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > minItems', + 'severity': 'violation', + 'validation_msg': '[] has less than 2 items', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- @@ -2964,17 +3032,75 @@ }) # --- # name: test_schemas[schema/fixtures/extra_links-max_contains] - '' + ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > maxContains + Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] + ERROR: Need 'SPEC_2' has schema violations: + Severity: violation + Field: links + Need path: SPEC_2 + Schema path: extra_links > schema > properties > links > maxContains + Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] + + ''' # --- # name: test_schemas[schema/fixtures/extra_links-max_contains].1 dict({ 'validated_needs_count': 3, 'validation_warnings': dict({ + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > maxContains', + 'severity': 'violation', + 'validation_msg': 'None of [] are valid under the given schema', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), + 'SPEC_2': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_2', + 'schema_path': 'extra_links > schema > properties > links > maxContains', + 'severity': 'violation', + 'validation_msg': 'None of [] are valid under the given schema', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-max_contains_error] ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > maxContains + Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] + ERROR: Need 'SPEC_2' has schema violations: + Severity: violation + Field: links + Need path: SPEC_2 + Schema path: extra_links > schema > properties > links > maxContains + Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -3004,6 +3130,38 @@ 'type': 'sn_schema_violation', }), ]), + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > maxContains', + 'severity': 'violation', + 'validation_msg': 'None of [] are valid under the given schema', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), + 'SPEC_2': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_2', + 'schema_path': 'extra_links > schema > properties > links > maxContains', + 'severity': 'violation', + 'validation_msg': 'None of [] are valid under the given schema', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- @@ -3052,17 +3210,47 @@ }) # --- # name: test_schemas[schema/fixtures/extra_links-min_contains] - '' + ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > minContains + Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] + + ''' # --- # name: test_schemas[schema/fixtures/extra_links-min_contains].1 dict({ 'validated_needs_count': 2, 'validation_warnings': dict({ + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > minContains', + 'severity': 'violation', + 'validation_msg': 'None of [] are valid under the given schema', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-min_contains_error] ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > minContains + Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -3092,21 +3280,67 @@ 'type': 'sn_schema_violation', }), ]), + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > minContains', + 'severity': 'violation', + 'validation_msg': 'None of [] are valid under the given schema', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-min_items] - '' + ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > minItems + Schema message: [] has less than 1 item [sn_schema_violation.extra_link_fail] + + ''' # --- # name: test_schemas[schema/fixtures/extra_links-min_items].1 dict({ 'validated_needs_count': 2, 'validation_warnings': dict({ + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > minItems', + 'severity': 'violation', + 'validation_msg': '[] has less than 1 item', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-min_items_error] ''' + ERROR: Need 'SPEC_1' has schema violations: + Severity: violation + Field: links + Need path: SPEC_1 + Schema path: extra_links > schema > properties > links > minItems + Schema message: [] has less than 2 items [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -3136,6 +3370,22 @@ 'type': 'sn_schema_violation', }), ]), + 'SPEC_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'links', + 'need_path': 'SPEC_1', + 'schema_path': 'extra_links > schema > properties > links > minItems', + 'severity': 'violation', + 'validation_msg': '[] has less than 2 items', + }), + 'log_lvl': 'error', + 'subtype': 'extra_link_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- @@ -4758,6 +5008,150 @@ }), }) # --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_docname_in_schema_and_invalid] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Field: docname + Need path: IMPL_1 + Schema path: [0] > local > properties > docname > enum + Schema message: "index" is not one of "some_other_doc" [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_docname_in_schema_and_invalid].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'docname', + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > properties > docname > enum', + 'severity': 'violation', + 'validation_msg': '"index" is not one of "some_other_doc"', + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_field_in_schema_and_invalid] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Field: status + Need path: IMPL_1 + Schema path: [0] > local > properties > status > enum + Schema message: "OPEN" is not one of "CLOSED" [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_field_in_schema_and_invalid].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'status', + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > properties > status > enum', + 'severity': 'violation', + 'validation_msg': '"OPEN" is not one of "CLOSED"', + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_status_set_not_in_schema] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Need path: IMPL_1 + Schema path: [0] > local > unevaluatedProperties + Schema message: Unevaluated properties are not allowed ('status' was unexpected) [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_status_set_not_in_schema].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > unevaluatedProperties', + 'severity': 'violation', + 'validation_msg': "Unevaluated properties are not allowed ('status' was unexpected)", + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_tags_in_schema_and_invalid] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Field: tags.0 + Need path: IMPL_1 + Schema path: [0] > local > properties > tags > items > enum + Schema message: "tag_a" is not one of "tag_b" [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_tags_in_schema_and_invalid].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'tags.0', + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > properties > tags > items > enum', + 'severity': 'violation', + 'validation_msg': '"tag_a" is not one of "tag_b"', + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_tags_set_not_in_schema] + '' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_tags_set_not_in_schema].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + }), + }) +# --- # name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_allof] ''' ERROR: Need 'IMPL_1' has schema violations: @@ -4822,6 +5216,16 @@ }), }) # --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_missing_schema_const_missing_in_rst] + '' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_missing_schema_const_missing_in_rst].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + }), + }) +# --- # name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_no_schema_given_in_rst] '' # --- @@ -4842,6 +5246,84 @@ }), }) # --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_const_empty_in_rst] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Field: comment + Need path: IMPL_1 + Schema path: [0] > local > properties > comment > const + Schema message: "must_be_checked_when_missing" was expected [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_const_empty_in_rst].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'comment', + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > properties > comment > const', + 'severity': 'violation', + 'validation_msg': '"must_be_checked_when_missing" was expected', + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_const_given_in_rst] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Field: comment + Need path: IMPL_1 + Schema path: [0] > local > properties > comment > const + Schema message: "must_be_checked_when_given_empty" was expected [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_const_given_in_rst].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'field': 'comment', + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > properties > comment > const', + 'severity': 'violation', + 'validation_msg': '"must_be_checked_when_given_empty" was expected', + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_const_missing_in_rst] + '' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_const_missing_in_rst].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + }), + }) +# --- # name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_missing_in_rst] '' # --- diff --git a/tests/schema/fixtures/unevaluated.yml b/tests/schema/fixtures/unevaluated.yml index 544ab4a2e..358fb67b8 100644 --- a/tests/schema/fixtures/unevaluated.yml +++ b/tests/schema/fixtures/unevaluated.yml @@ -65,6 +65,105 @@ unevaluated_extra_with_schema_missing_in_rst: properties: {"asil": {}} unevaluatedProperties: false +unevaluated_extra_with_schema_const_missing_in_rst: + # should not complain, because the comment field has a schema; not providing a value + # leads to a None value which cannot be handled by schemas.json and gets removed before + # validation; the const validation is not triggered on a non-existing field + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.comment] + schema.type = "string" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + schemas: + schemas: + - validate: + local: + properties: + asil: {} + comment: {"const": "must_be_checked_when_missing"} + unevaluatedProperties: false + +unevaluated_extra_with_schema_const_empty_in_rst: + # should complain, because the comment field has a schema; the field is given empty + # which is represented as empty string, which does not match the constraint + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.comment] + schema.type = "string" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :comment: + schemas: + schemas: + - validate: + local: + properties: + asil: {} + comment: {"const": "must_be_checked_when_missing"} + unevaluatedProperties: false + +unevaluated_extra_missing_schema_const_missing_in_rst: + # should not complain, the field has no schema constraint, so the default is an empty string; + # the value does not differ from this, which leads to the removal of the field for validation; + # the behavior is identical to the behavior with field schema + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.comment] + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + schemas: + schemas: + - validate: + local: + properties: + asil: {} + comment: {"const": "must_be_checked_when_missing"} + unevaluatedProperties: false + +unevaluated_extra_with_schema_const_given_in_rst: + # should complain, because the field has a schema constraint; not providing it fails the + # constraint + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.comment] + schema.type = "string" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :comment: + schemas: + schemas: + - validate: + local: + properties: + asil: {} + comment: {"const": "must_be_checked_when_given_empty"} + unevaluatedProperties: false + unevaluated_error_extra_with_schema_given_in_rst: # should complain, the comment field is set, and it has a schema, so SN detects it is actively # given as '', and it will appear in the unevaluatedProperties evaluation as forbidden @@ -91,7 +190,7 @@ unevaluated_error_extra_with_schema_given_in_rst: unevaluated_error_allof: # check whether unevaluatedProperties implementation applies to combined schemas (allOf) - # should complain about "approved" given as additional field, but not comment or asil + # should complain about "approved" given as additional field, but not for comment or asil conf: | extensions = ["sphinx_needs"] needs_from_toml = "ubproject.toml" @@ -166,3 +265,112 @@ unevaluated_link_missing: local: properties: {"asil": {}} unevaluatedProperties: false + +unevaluated_core_status_set_not_in_schema: + # should complain, because the core field status is treated like an extra field, + # so it is kept when explicitly set and triggers unevaluatedProperties. + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :status: OPEN + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + +unevaluated_core_field_in_schema_and_invalid: + # should complain about status, because it is part of the user schema and actively set, + # therefore it must be kept in the reduced need and validated + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :status: OPEN + schemas: + schemas: + - validate: + local: + properties: {"asil": {}, "status": {"enum": ["CLOSED"]}} + unevaluatedProperties: false + +unevaluated_core_tags_set_not_in_schema: + # TODO: check impl again, it does not trigger + # should complain, because the core field tags is treated like an extra field, + # so it is kept when explicitly set and triggers unevaluatedProperties. + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :tags: tag_a + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + +unevaluated_core_tags_in_schema_and_invalid: + # should complain about tags, because it is part of the user schema and actively set, + # therefore it must be kept in the reduced need and validated + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + :tags: tag_a + schemas: + schemas: + - validate: + local: + properties: + asil: {} + tags: + type: array + items: {type: string, enum: ["tag_b"]} + unevaluatedProperties: false + +unevaluated_core_docname_in_schema_and_invalid: + # should complain about docname, because it is part of the user schema, + # therefore it must be kept in the reduced need and validated + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + schemas: + schemas: + - validate: + local: + properties: {"asil": {}, "docname": {"enum": ["some_other_doc"]}} + unevaluatedProperties: false From 32682a9005cbd40c6325e26c97280845b523a7f7 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Fri, 30 Jan 2026 15:35:55 +0100 Subject: [PATCH 05/10] Refactor reduce_need function to remove redundant field checks --- sphinx_needs/schema/core.py | 5 - tests/schema/__snapshots__/test_schema.ambr | 485 -------------------- 2 files changed, 490 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index f8e1bb06d..fe5926f6f 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -484,11 +484,6 @@ def reduce_need( # field is unset, and schema validation cannot handle null types continue - if field in schema_properties: - # keep all fields covered by the schema as validation constraints exist - reduced_need[field] = value - continue - schema_field = field_properties[field] if "default" in schema_field and ( (schema_field["default"] is None) diff --git a/tests/schema/__snapshots__/test_schema.ambr b/tests/schema/__snapshots__/test_schema.ambr index a02657d0b..155cca920 100644 --- a/tests/schema/__snapshots__/test_schema.ambr +++ b/tests/schema/__snapshots__/test_schema.ambr @@ -1,489 +1,4 @@ # serializer version: 1 -# name: test_schema_benchmark[100] - ''' - /index.rst:2: WARNING: toctree contains reference to nonexisting document ' :maxdepth: 2' [toc.not_readable] - WARNING: Need 'FEAt_P01' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P01 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P01" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P01' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P01 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P01" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P01' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P01 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P01' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P01 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P01" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P01' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P01 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P01" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P01' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P01 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P01" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P01' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P01 > links > SPEC_SAFE_UNSAFE_FEAT_P01 > links > FEAT_P01 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P02' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P02 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P02" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P02' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P02 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P02" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P02' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P02 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P02' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P02 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P02" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P02' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P02 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P02" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P02' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P02 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P02" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P02' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P02 > links > SPEC_SAFE_UNSAFE_FEAT_P02 > links > FEAT_P02 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P03' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P03 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P03" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P03' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P03 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P03" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P03' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P03 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P03' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P03 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P03" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P03' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P03 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P03" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P03' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P03 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P03" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P03' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P03 > links > SPEC_SAFE_UNSAFE_FEAT_P03 > links > FEAT_P03 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P04' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P04 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P04" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P04' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P04 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P04" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P04' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P04 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P04' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P04 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P04" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P04' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P04 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P04" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P04' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P04 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P04" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P04' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P04 > links > SPEC_SAFE_UNSAFE_FEAT_P04 > links > FEAT_P04 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P05' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P05 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P05" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P05' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P05 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P05" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P05' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P05 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P05' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P05 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P05" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P05' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P05 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P05" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P05' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P05 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P05" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P05' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P05 > links > SPEC_SAFE_UNSAFE_FEAT_P05 > links > FEAT_P05 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P06' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P06 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P06" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P06' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P06 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P06" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P06' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P06 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P06' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P06 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P06" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P06' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P06 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P06" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P06' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P06 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P06" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P06' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P06 > links > SPEC_SAFE_UNSAFE_FEAT_P06 > links > FEAT_P06 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P07' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P07 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P07" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P07' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P07 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P07" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P07' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P07 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P07' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P07 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P07" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P07' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P07 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P07" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P07' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P07 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P07" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P07' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P07 > links > SPEC_SAFE_UNSAFE_FEAT_P07 > links > FEAT_P07 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P08' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P08 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P08" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P08' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P08 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P08" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P08' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P08 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P08' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P08 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P08" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P08' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P08 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P08" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P08' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P08 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P08" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P08' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P08 > links > SPEC_SAFE_UNSAFE_FEAT_P08 > links > FEAT_P08 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P09' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P09 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P09" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P09' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P09 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P09" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P09' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P09 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P09' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P09 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P09" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P09' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P09 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P09" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P09' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P09 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P09" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P09' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P09 > links > SPEC_SAFE_UNSAFE_FEAT_P09 > links > FEAT_P09 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - WARNING: Need 'FEAt_P10' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P10 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P10" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P10' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P10 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P10" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P10' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P10 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P10' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P10 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P10" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P10' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P10 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P10" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P10' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P10 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P10" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P10' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P10 > links > SPEC_SAFE_UNSAFE_FEAT_P10 > links > FEAT_P10 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - - ''' -# --- -# name: test_schema_benchmark[10] - ''' - /index.rst:2: WARNING: toctree contains reference to nonexisting document ' :maxdepth: 2' [toc.not_readable] - WARNING: Need 'FEAt_P1' has schema warnings: - Severity: warning - Field: id - Need path: FEAt_P1 - Schema path: [0] > local > properties > id > pattern - User message: id must be uppercase - Schema message: "FEAt_P1" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] - ERROR: Need 'SPEC_MISSING_APPROVAL_P1' has schema violations: - Severity: violation - Field: id - Need path: SPEC_MISSING_APPROVAL_P1 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_MISSING_APPROVAL_P1" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - WARNING: Need 'SPEC_MISSING_APPROVAL_P1' has schema infos: - Severity: info - Need path: SPEC_MISSING_APPROVAL_P1 - Schema path: spec-approved[2] > local > required - User message: Approval required due to high efforts - Schema message: "approved" is a required property [sn_schema_info.local_fail] - ERROR: Need 'SPEC_P1' has schema violations: - Severity: violation - Field: id - Need path: SPEC_P1 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_P1" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P1' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_UNSAFE_FEAT_P1 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_UNSAFE_FEAT_P1" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'SPEC_SAFE_P1' has schema violations: - Severity: violation - Field: id - Need path: SPEC_SAFE_P1 - Schema path: spec[1] > local > properties > id > pattern - Schema message: "SPEC_SAFE_P1" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] - ERROR: Need 'FEAT_P1' has schema violations: - Severity: violation - Field: asil - Need path: IMPL_SAFE_P1 > links > SPEC_SAFE_UNSAFE_FEAT_P1 > links > FEAT_P1 - Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum - Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] - - ''' -# --- # name: test_schema_config[both_definitions_and_json_given] "You cannot use both 'needs_schema_definitions' and 'needs_schema_definitions_from_json' at the same time." # --- From 9faba44b98e260622536598d92b924a7432e222c Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Fri, 30 Jan 2026 16:57:09 +0100 Subject: [PATCH 06/10] Refactor reduce_need function to simplify link field handling - Removed unnecessary checks for schema properties when processing link fields. - Updated logic to retain only non-empty link fields in the reduced need. --- sphinx_needs/schema/core.py | 5 +- tests/schema/__snapshots__/test_schema.ambr | 743 +++++++++++++------- 2 files changed, 490 insertions(+), 258 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index fe5926f6f..5ca6768e9 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -495,11 +495,8 @@ def reduce_need( reduced_need[field] = value for field, value in need.iter_links_items(): - if field in schema_properties: - reduced_need[field] = value - continue - if value: + # keep non-empty link fields reduced_need[field] = value for field, value in need.iter_core_items(): diff --git a/tests/schema/__snapshots__/test_schema.ambr b/tests/schema/__snapshots__/test_schema.ambr index 155cca920..16fe03e99 100644 --- a/tests/schema/__snapshots__/test_schema.ambr +++ b/tests/schema/__snapshots__/test_schema.ambr @@ -1,4 +1,489 @@ # serializer version: 1 +# name: test_schema_benchmark[100] + ''' + /index.rst:2: WARNING: toctree contains reference to nonexisting document ' :maxdepth: 2' [toc.not_readable] + WARNING: Need 'FEAt_P01' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P01 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P01" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P01' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P01 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P01" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P01' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P01 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P01' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P01 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P01" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P01' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P01 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P01" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P01' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P01 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P01" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P01' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P01 > links > SPEC_SAFE_UNSAFE_FEAT_P01 > links > FEAT_P01 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P02' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P02 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P02" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P02' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P02 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P02" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P02' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P02 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P02' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P02 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P02" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P02' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P02 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P02" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P02' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P02 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P02" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P02' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P02 > links > SPEC_SAFE_UNSAFE_FEAT_P02 > links > FEAT_P02 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P03' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P03 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P03" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P03' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P03 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P03" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P03' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P03 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P03' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P03 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P03" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P03' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P03 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P03" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P03' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P03 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P03" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P03' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P03 > links > SPEC_SAFE_UNSAFE_FEAT_P03 > links > FEAT_P03 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P04' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P04 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P04" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P04' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P04 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P04" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P04' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P04 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P04' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P04 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P04" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P04' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P04 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P04" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P04' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P04 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P04" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P04' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P04 > links > SPEC_SAFE_UNSAFE_FEAT_P04 > links > FEAT_P04 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P05' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P05 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P05" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P05' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P05 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P05" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P05' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P05 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P05' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P05 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P05" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P05' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P05 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P05" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P05' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P05 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P05" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P05' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P05 > links > SPEC_SAFE_UNSAFE_FEAT_P05 > links > FEAT_P05 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P06' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P06 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P06" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P06' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P06 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P06" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P06' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P06 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P06' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P06 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P06" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P06' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P06 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P06" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P06' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P06 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P06" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P06' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P06 > links > SPEC_SAFE_UNSAFE_FEAT_P06 > links > FEAT_P06 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P07' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P07 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P07" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P07' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P07 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P07" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P07' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P07 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P07' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P07 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P07" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P07' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P07 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P07" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P07' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P07 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P07" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P07' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P07 > links > SPEC_SAFE_UNSAFE_FEAT_P07 > links > FEAT_P07 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P08' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P08 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P08" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P08' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P08 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P08" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P08' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P08 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P08' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P08 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P08" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P08' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P08 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P08" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P08' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P08 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P08" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P08' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P08 > links > SPEC_SAFE_UNSAFE_FEAT_P08 > links > FEAT_P08 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P09' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P09 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P09" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P09' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P09 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P09" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P09' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P09 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P09' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P09 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P09" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P09' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P09 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P09" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P09' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P09 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P09" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P09' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P09 > links > SPEC_SAFE_UNSAFE_FEAT_P09 > links > FEAT_P09 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + WARNING: Need 'FEAt_P10' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P10 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P10" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P10' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P10 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P10" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P10' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P10 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P10' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P10 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P10" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P10' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P10 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P10" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P10' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P10 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P10" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P10' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P10 > links > SPEC_SAFE_UNSAFE_FEAT_P10 > links > FEAT_P10 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + + ''' +# --- +# name: test_schema_benchmark[10] + ''' + /index.rst:2: WARNING: toctree contains reference to nonexisting document ' :maxdepth: 2' [toc.not_readable] + WARNING: Need 'FEAt_P1' has schema warnings: + Severity: warning + Field: id + Need path: FEAt_P1 + Schema path: [0] > local > properties > id > pattern + User message: id must be uppercase + Schema message: "FEAt_P1" does not match "^[A-Z0-9_]+$" [sn_schema_warning.local_fail] + ERROR: Need 'SPEC_MISSING_APPROVAL_P1' has schema violations: + Severity: violation + Field: id + Need path: SPEC_MISSING_APPROVAL_P1 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_MISSING_APPROVAL_P1" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + WARNING: Need 'SPEC_MISSING_APPROVAL_P1' has schema infos: + Severity: info + Need path: SPEC_MISSING_APPROVAL_P1 + Schema path: spec-approved[2] > local > required + User message: Approval required due to high efforts + Schema message: "approved" is a required property [sn_schema_info.local_fail] + ERROR: Need 'SPEC_P1' has schema violations: + Severity: violation + Field: id + Need path: SPEC_P1 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_P1" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_UNSAFE_FEAT_P1' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_UNSAFE_FEAT_P1 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_UNSAFE_FEAT_P1" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'SPEC_SAFE_P1' has schema violations: + Severity: violation + Field: id + Need path: SPEC_SAFE_P1 + Schema path: spec[1] > local > properties > id > pattern + Schema message: "SPEC_SAFE_P1" does not match "^REQ[a-zA-Z0-9_-]*$" [sn_schema_violation.local_fail] + ERROR: Need 'FEAT_P1' has schema violations: + Severity: violation + Field: asil + Need path: IMPL_SAFE_P1 > links > SPEC_SAFE_UNSAFE_FEAT_P1 > links > FEAT_P1 + Schema path: safe-impl-[links]->safe-spec-[links]->safe-feat[4] > validate > network > links > items > validate > network > links > items > local > allOf > 1 > allOf > 0 > properties > asil > enum + Schema message: "QM" is not one of "A", "B" or 2 other candidates [sn_schema_violation.network_local_fail] + + ''' +# --- # name: test_schema_config[both_definitions_and_json_given] "You cannot use both 'needs_schema_definitions' and 'needs_schema_definitions_from_json' at the same time." # --- @@ -2357,47 +2842,17 @@ }) # --- # name: test_schemas[schema/fixtures/extra_links-contains] - ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > contains - Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] - - ''' + '' # --- # name: test_schemas[schema/fixtures/extra_links-contains].1 dict({ 'validated_needs_count': 2, 'validation_warnings': dict({ - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > contains', - 'severity': 'violation', - 'validation_msg': 'None of [] are valid under the given schema', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-contains_error] ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > contains - Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -2427,33 +2882,11 @@ 'type': 'sn_schema_violation', }), ]), - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > contains', - 'severity': 'violation', - 'validation_msg': 'None of [] are valid under the given schema', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-inject_array] ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > minItems - Schema message: [] has less than 2 items [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -2483,22 +2916,6 @@ 'type': 'sn_schema_violation', }), ]), - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > minItems', - 'severity': 'violation', - 'validation_msg': '[] has less than 2 items', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- @@ -2547,75 +2964,17 @@ }) # --- # name: test_schemas[schema/fixtures/extra_links-max_contains] - ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > maxContains - Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] - ERROR: Need 'SPEC_2' has schema violations: - Severity: violation - Field: links - Need path: SPEC_2 - Schema path: extra_links > schema > properties > links > maxContains - Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] - - ''' + '' # --- # name: test_schemas[schema/fixtures/extra_links-max_contains].1 dict({ 'validated_needs_count': 3, 'validation_warnings': dict({ - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > maxContains', - 'severity': 'violation', - 'validation_msg': 'None of [] are valid under the given schema', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), - 'SPEC_2': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_2', - 'schema_path': 'extra_links > schema > properties > links > maxContains', - 'severity': 'violation', - 'validation_msg': 'None of [] are valid under the given schema', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-max_contains_error] ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > maxContains - Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] - ERROR: Need 'SPEC_2' has schema violations: - Severity: violation - Field: links - Need path: SPEC_2 - Schema path: extra_links > schema > properties > links > maxContains - Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -2645,38 +3004,6 @@ 'type': 'sn_schema_violation', }), ]), - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > maxContains', - 'severity': 'violation', - 'validation_msg': 'None of [] are valid under the given schema', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), - 'SPEC_2': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_2', - 'schema_path': 'extra_links > schema > properties > links > maxContains', - 'severity': 'violation', - 'validation_msg': 'None of [] are valid under the given schema', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- @@ -2725,47 +3052,17 @@ }) # --- # name: test_schemas[schema/fixtures/extra_links-min_contains] - ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > minContains - Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] - - ''' + '' # --- # name: test_schemas[schema/fixtures/extra_links-min_contains].1 dict({ 'validated_needs_count': 2, 'validation_warnings': dict({ - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > minContains', - 'severity': 'violation', - 'validation_msg': 'None of [] are valid under the given schema', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-min_contains_error] ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > minContains - Schema message: None of [] are valid under the given schema [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -2795,67 +3092,21 @@ 'type': 'sn_schema_violation', }), ]), - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > minContains', - 'severity': 'violation', - 'validation_msg': 'None of [] are valid under the given schema', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-min_items] - ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > minItems - Schema message: [] has less than 1 item [sn_schema_violation.extra_link_fail] - - ''' + '' # --- # name: test_schemas[schema/fixtures/extra_links-min_items].1 dict({ 'validated_needs_count': 2, 'validation_warnings': dict({ - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > minItems', - 'severity': 'violation', - 'validation_msg': '[] has less than 1 item', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- # name: test_schemas[schema/fixtures/extra_links-min_items_error] ''' - ERROR: Need 'SPEC_1' has schema violations: - Severity: violation - Field: links - Need path: SPEC_1 - Schema path: extra_links > schema > properties > links > minItems - Schema message: [] has less than 2 items [sn_schema_violation.extra_link_fail] ERROR: Need 'IMPL_1' has schema violations: Severity: violation Field: links @@ -2885,22 +3136,6 @@ 'type': 'sn_schema_violation', }), ]), - 'SPEC_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'links', - 'need_path': 'SPEC_1', - 'schema_path': 'extra_links > schema > properties > links > minItems', - 'severity': 'violation', - 'validation_msg': '[] has less than 2 items', - }), - 'log_lvl': 'error', - 'subtype': 'extra_link_fail', - 'type': 'sn_schema_violation', - }), - ]), }), }) # --- From d930d464852f4b6d9495e46599107978deb522a4 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Fri, 30 Jan 2026 17:49:28 +0100 Subject: [PATCH 07/10] Update reduce_need logic and tests for unevaluated properties and schema constraints --- sphinx_needs/schema/core.py | 40 ++++++------- tests/schema/__snapshots__/test_schema.ambr | 66 +++++++++------------ tests/schema/fixtures/unevaluated.yml | 36 ++--------- 3 files changed, 49 insertions(+), 93 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index 5ca6768e9..4699b4884 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -447,28 +447,27 @@ def reduce_need( Rules: - ``None`` values are always dropped (schema validation cannot handle null types here). - - Any field/link covered by ``schema_properties`` is kept (so it gets validated). - - Extra fields not covered by the schema are kept only if a default is defined and either: - the default is ``None`` (value is non-``None``), or the default is ``""`` and the value is - not ``""``. - - Link fields not covered by the schema are kept only if the list is non-empty. - - Core fields are dropped unless covered by ``schema_properties``, except the core fields - ``status`` and ``tags`` which follow the same keep rule as extra fields. + - Extra fields are included only if a default is configured (independent of ``schema_properties``): - Defaults other than ``None``/``""`` are treated as "inactive" during reduction and therefore - only participate in validation if the field is covered by ``schema_properties``. + - If the default is ``None``, any non-``None`` value is kept. + - If the default is ``""``, the value is kept only if it is not ``""``. + - Defaults other than ``None``/``""`` are treated as inactive and are never included by this + reduction step. + + - Link fields are included only if they are non-empty (independent of ``schema_properties``). + - Core fields are included if they are covered by ``schema_properties``. + Additionally, ``status`` is kept if it is non-``None`` and ``tags`` is kept if it is non-empty. SN5 vs SN6 empty-vs-missing semantics for extra options: - Without a field schema (SN5-style), unset and present-but-empty (``:opt:``) both become - ``""``, so "unset" and "set-but-empty" cannot be distinguished. + ``""``, so "unset" and "set-but-empty" cannot be distinguished. - With a field schema (SN6), unset nullable options default to ``None``, while ``:opt:`` - results in ``""``. This allows distinguishing "unset" from "set-but-empty". + results in ``""``. This allows distinguishing "unset" from "set-but-empty". - In this reduction, that distinction matters: for fields not covered by - ``schema_properties``, ``""`` is dropped if the default is also ``""`` (SN5-style), while - ``""`` is kept if the default is ``None`` (SN6-style), so set-but-empty can be treated as - actively set. + In this reduction, that distinction matters for extra fields: ``""`` is dropped if the + default is also ``""`` (SN5-style), while ``""`` is kept if the default is ``None`` + (SN6-style), so set-but-empty can be treated as actively set. For link fields, missing vs empty cannot be distinguished (both result in ``[]``). @@ -508,14 +507,9 @@ def reduce_need( continue schema_field = field_properties[field] - if ( - field in {"status", "tags"} - and "default" in schema_field - and ( - (schema_field["default"] is None) - or (schema_field["default"] == "" and value != "") - ) - ): + if field == "status" and value is not None: # default is None + reduced_need[field] = value + if field == "tags" and len(value) > 0: # default is [] reduced_need[field] = value return reduced_need diff --git a/tests/schema/__snapshots__/test_schema.ambr b/tests/schema/__snapshots__/test_schema.ambr index 16fe03e99..76a6733cc 100644 --- a/tests/schema/__snapshots__/test_schema.ambr +++ b/tests/schema/__snapshots__/test_schema.ambr @@ -4893,12 +4893,34 @@ }) # --- # name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_tags_set_not_in_schema] - '' + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Need path: IMPL_1 + Schema path: [0] > local > unevaluatedProperties + Schema message: Unevaluated properties are not allowed ('tags' was unexpected) [sn_schema_violation.local_fail] + + ''' # --- # name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_tags_set_not_in_schema].1 dict({ 'validated_needs_count': 1, 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > unevaluatedProperties', + 'severity': 'violation', + 'validation_msg': "Unevaluated properties are not allowed ('tags' was unexpected)", + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), }), }) # --- @@ -4934,7 +4956,7 @@ }), }) # --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_schema_given_in_rst] +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_schema_given_in_rst_no_constraint] ''' ERROR: Need 'IMPL_1' has schema violations: Severity: violation @@ -4944,7 +4966,7 @@ ''' # --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_schema_given_in_rst].1 +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_schema_given_in_rst_no_constraint].1 dict({ 'validated_needs_count': 1, 'validation_warnings': dict({ @@ -5003,7 +5025,7 @@ Field: comment Need path: IMPL_1 Schema path: [0] > local > properties > comment > const - Schema message: "must_be_checked_when_missing" was expected [sn_schema_violation.local_fail] + Schema message: "expected" was expected [sn_schema_violation.local_fail] ''' # --- @@ -5020,41 +5042,7 @@ 'need_path': 'IMPL_1', 'schema_path': '[0] > local > properties > comment > const', 'severity': 'violation', - 'validation_msg': '"must_be_checked_when_missing" was expected', - }), - 'log_lvl': 'error', - 'subtype': 'local_fail', - 'type': 'sn_schema_violation', - }), - ]), - }), - }) -# --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_const_given_in_rst] - ''' - ERROR: Need 'IMPL_1' has schema violations: - Severity: violation - Field: comment - Need path: IMPL_1 - Schema path: [0] > local > properties > comment > const - Schema message: "must_be_checked_when_given_empty" was expected [sn_schema_violation.local_fail] - - ''' -# --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_extra_with_schema_const_given_in_rst].1 - dict({ - 'validated_needs_count': 1, - 'validation_warnings': dict({ - 'IMPL_1': list([ - dict({ - 'children': list([ - ]), - 'details': dict({ - 'field': 'comment', - 'need_path': 'IMPL_1', - 'schema_path': '[0] > local > properties > comment > const', - 'severity': 'violation', - 'validation_msg': '"must_be_checked_when_given_empty" was expected', + 'validation_msg': '"expected" was expected', }), 'log_lvl': 'error', 'subtype': 'local_fail', diff --git a/tests/schema/fixtures/unevaluated.yml b/tests/schema/fixtures/unevaluated.yml index 358fb67b8..b57925e43 100644 --- a/tests/schema/fixtures/unevaluated.yml +++ b/tests/schema/fixtures/unevaluated.yml @@ -87,7 +87,7 @@ unevaluated_extra_with_schema_const_missing_in_rst: local: properties: asil: {} - comment: {"const": "must_be_checked_when_missing"} + comment: {"const": "expected"} unevaluatedProperties: false unevaluated_extra_with_schema_const_empty_in_rst: @@ -112,7 +112,7 @@ unevaluated_extra_with_schema_const_empty_in_rst: local: properties: asil: {} - comment: {"const": "must_be_checked_when_missing"} + comment: {"const": "expected"} unevaluatedProperties: false unevaluated_extra_missing_schema_const_missing_in_rst: @@ -136,35 +136,10 @@ unevaluated_extra_missing_schema_const_missing_in_rst: local: properties: asil: {} - comment: {"const": "must_be_checked_when_missing"} + comment: {"const": "expected"} unevaluatedProperties: false -unevaluated_extra_with_schema_const_given_in_rst: - # should complain, because the field has a schema constraint; not providing it fails the - # constraint - conf: | - extensions = ["sphinx_needs"] - needs_from_toml = "ubproject.toml" - needs_schema_definitions_from_json = "schemas.json" - ubproject: | - [needs.fields.asil] - [needs.fields.comment] - schema.type = "string" - rst: | - .. impl:: title - :id: IMPL_1 - :asil: QM - :comment: - schemas: - schemas: - - validate: - local: - properties: - asil: {} - comment: {"const": "must_be_checked_when_given_empty"} - unevaluatedProperties: false - -unevaluated_error_extra_with_schema_given_in_rst: +unevaluated_error_extra_with_schema_given_in_rst_no_constraint: # should complain, the comment field is set, and it has a schema, so SN detects it is actively # given as '', and it will appear in the unevaluatedProperties evaluation as forbidden # additional field @@ -309,7 +284,6 @@ unevaluated_core_field_in_schema_and_invalid: unevaluatedProperties: false unevaluated_core_tags_set_not_in_schema: - # TODO: check impl again, it does not trigger # should complain, because the core field tags is treated like an extra field, # so it is kept when explicitly set and triggers unevaluatedProperties. conf: | @@ -357,7 +331,7 @@ unevaluated_core_tags_in_schema_and_invalid: unevaluated_core_docname_in_schema_and_invalid: # should complain about docname, because it is part of the user schema, - # therefore it must be kept in the reduced need and validated + # therefore it is kept in the reduced need and validated conf: | extensions = ["sphinx_needs"] needs_from_toml = "ubproject.toml" From 663358b91536af61b75bd7f08777cee1425c3e48 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Fri, 30 Jan 2026 18:08:39 +0100 Subject: [PATCH 08/10] Enhance reduce_need logic to improve handling of default values and unevaluated properties; update related tests --- sphinx_needs/schema/core.py | 20 ++-- tests/schema/__snapshots__/test_schema.ambr | 100 +++++++++++++++++++- tests/schema/fixtures/unevaluated.yml | 69 +++++++++++++- 3 files changed, 178 insertions(+), 11 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index 4699b4884..fa048c05a 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -484,14 +484,17 @@ def reduce_need( continue schema_field = field_properties[field] - if "default" in schema_field and ( - (schema_field["default"] is None) - or (schema_field["default"] == "" and value != "") - ): - # there is a default and if it is None, and the value is not, keep it - # or if the default is "" and the value is not "", keep it - # this covers the SN5 (no-schema) and SN6 (with-schema) cases - reduced_need[field] = value + if "default" in schema_field: + if schema_field["default"] is None: + # keep all non-None values - they are actively set + reduced_need[field] = value + elif schema_field["default"] == "": + # SN5 case: only keep non-empty values + if value != "": + reduced_need[field] = value + else: + # keep the value - there is a default that is not None/"" or a real RST value + reduced_need[field] = value for field, value in need.iter_links_items(): if value: @@ -503,6 +506,7 @@ def reduce_need( continue if field in schema_properties: + # actively included by schema - include to validate constraints reduced_need[field] = value continue diff --git a/tests/schema/__snapshots__/test_schema.ambr b/tests/schema/__snapshots__/test_schema.ambr index 76a6733cc..dd2d3283d 100644 --- a/tests/schema/__snapshots__/test_schema.ambr +++ b/tests/schema/__snapshots__/test_schema.ambr @@ -4792,7 +4792,7 @@ }), }) # --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_field_in_schema_and_invalid] +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_status_in_schema_and_invalid] ''' ERROR: Need 'IMPL_1' has schema violations: Severity: violation @@ -4803,7 +4803,7 @@ ''' # --- -# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_field_in_schema_and_invalid].1 +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_core_status_in_schema_and_invalid].1 dict({ 'validated_needs_count': 1, 'validation_warnings': dict({ @@ -4956,6 +4956,102 @@ }), }) # --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_core_status_with_default] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Need path: IMPL_1 + Schema path: [0] > local > unevaluatedProperties + Schema message: Unevaluated properties are not allowed ('status' was unexpected) [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_core_status_with_default].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > unevaluatedProperties', + 'severity': 'violation', + 'validation_msg': "Unevaluated properties are not allowed ('status' was unexpected)", + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_core_tags_with_default] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Need path: IMPL_1 + Schema path: [0] > local > unevaluatedProperties + Schema message: Unevaluated properties are not allowed ('tags' was unexpected) [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_core_tags_with_default].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > unevaluatedProperties', + 'severity': 'violation', + 'validation_msg': "Unevaluated properties are not allowed ('tags' was unexpected)", + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_default] + ''' + ERROR: Need 'IMPL_1' has schema violations: + Severity: violation + Need path: IMPL_1 + Schema path: [0] > local > unevaluatedProperties + Schema message: Unevaluated properties are not allowed ('comment' was unexpected) [sn_schema_violation.local_fail] + + ''' +# --- +# name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_default].1 + dict({ + 'validated_needs_count': 1, + 'validation_warnings': dict({ + 'IMPL_1': list([ + dict({ + 'children': list([ + ]), + 'details': dict({ + 'need_path': 'IMPL_1', + 'schema_path': '[0] > local > unevaluatedProperties', + 'severity': 'violation', + 'validation_msg': "Unevaluated properties are not allowed ('comment' was unexpected)", + }), + 'log_lvl': 'error', + 'subtype': 'local_fail', + 'type': 'sn_schema_violation', + }), + ]), + }), + }) +# --- # name: test_schemas[schema/fixtures/unevaluated-unevaluated_error_extra_with_schema_given_in_rst_no_constraint] ''' ERROR: Need 'IMPL_1' has schema violations: diff --git a/tests/schema/fixtures/unevaluated.yml b/tests/schema/fixtures/unevaluated.yml index b57925e43..65a9770aa 100644 --- a/tests/schema/fixtures/unevaluated.yml +++ b/tests/schema/fixtures/unevaluated.yml @@ -163,6 +163,29 @@ unevaluated_error_extra_with_schema_given_in_rst_no_constraint: properties: {"asil": {}} unevaluatedProperties: false +unevaluated_error_extra_with_default: + # should complain, the comment field is set via default, and it will appear in the + # unevaluatedProperties evaluation as forbidden additional field + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.comment] + schema.type = "string" + default = "default" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + unevaluated_error_allof: # check whether unevaluatedProperties implementation applies to combined schemas (allOf) # should complain about "approved" given as additional field, but not for comment or asil @@ -262,7 +285,7 @@ unevaluated_core_status_set_not_in_schema: properties: {"asil": {}} unevaluatedProperties: false -unevaluated_core_field_in_schema_and_invalid: +unevaluated_core_status_in_schema_and_invalid: # should complain about status, because it is part of the user schema and actively set, # therefore it must be kept in the reduced need and validated conf: | @@ -283,6 +306,28 @@ unevaluated_core_field_in_schema_and_invalid: properties: {"asil": {}, "status": {"enum": ["CLOSED"]}} unevaluatedProperties: false +unevaluated_error_core_status_with_default: + # should complain, the status field has a default, and it will appear in the + # unevaluatedProperties evaluation as forbidden additional field + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.status] + default = "default" + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + unevaluated_core_tags_set_not_in_schema: # should complain, because the core field tags is treated like an extra field, # so it is kept when explicitly set and triggers unevaluatedProperties. @@ -329,6 +374,28 @@ unevaluated_core_tags_in_schema_and_invalid: items: {type: string, enum: ["tag_b"]} unevaluatedProperties: false +unevaluated_error_core_tags_with_default: + # should complain, the tags field has a default, and it will appear in the + # unevaluatedProperties evaluation as forbidden additional field + conf: | + extensions = ["sphinx_needs"] + needs_from_toml = "ubproject.toml" + needs_schema_definitions_from_json = "schemas.json" + ubproject: | + [needs.fields.asil] + [needs.fields.tags] + default = ["default"] + rst: | + .. impl:: title + :id: IMPL_1 + :asil: QM + schemas: + schemas: + - validate: + local: + properties: {"asil": {}} + unevaluatedProperties: false + unevaluated_core_docname_in_schema_and_invalid: # should complain about docname, because it is part of the user schema, # therefore it is kept in the reduced need and validated From c506d267e2a324dbef12ff1ef26217e8b7874cb5 Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Fri, 30 Jan 2026 18:15:20 +0100 Subject: [PATCH 09/10] Refine reduce_need documentation for clarity on handling default values and empty fields --- sphinx_needs/schema/core.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index fa048c05a..1f1815c5d 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -446,17 +446,17 @@ def reduce_need( Rules: - - ``None`` values are always dropped (schema validation cannot handle null types here). - - Extra fields are included only if a default is configured (independent of ``schema_properties``): + - ``None`` values are always dropped (schema validation cannot handle null types). + - Extra fields are included based on their configured default (independent of + ``schema_properties``): - If the default is ``None``, any non-``None`` value is kept. - - If the default is ``""``, the value is kept only if it is not ``""``. - - Defaults other than ``None``/``""`` are treated as inactive and are never included by this - reduction step. + - If the default is ``""`` (SN5-style empty), the value is kept only if it is not ``""``. + - For any other default value, the field is kept as-is. - Link fields are included only if they are non-empty (independent of ``schema_properties``). - Core fields are included if they are covered by ``schema_properties``. - Additionally, ``status`` is kept if it is non-``None`` and ``tags`` is kept if it is non-empty. + Additionally, ``status`` is kept if it is non-``None`` and ``tags`` is kept if it is non-empty. SN5 vs SN6 empty-vs-missing semantics for extra options: @@ -465,9 +465,9 @@ def reduce_need( - With a field schema (SN6), unset nullable options default to ``None``, while ``:opt:`` results in ``""``. This allows distinguishing "unset" from "set-but-empty". - In this reduction, that distinction matters for extra fields: ``""`` is dropped if the - default is also ``""`` (SN5-style), while ``""`` is kept if the default is ``None`` - (SN6-style), so set-but-empty can be treated as actively set. + In this reduction, that distinction matters for extra fields: ``""`` is dropped if the + default is also ``""`` (SN5-style), while ``""`` is kept if the default is ``None`` + (SN6-style), so set-but-empty can be treated as actively set. For link fields, missing vs empty cannot be distinguished (both result in ``[]``). From cb3169957f89757180590843ae291dc36820fbca Mon Sep 17 00:00:00 2001 From: Marco Heinemann Date: Mon, 2 Feb 2026 16:24:51 +0100 Subject: [PATCH 10/10] Refactor reduce_need function after sync --- sphinx_needs/schema/core.py | 27 ++++++++------------------- tests/schema/fixtures/unevaluated.yml | 11 +++++++++++ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/sphinx_needs/schema/core.py b/sphinx_needs/schema/core.py index 1f1815c5d..994c191c3 100644 --- a/sphinx_needs/schema/core.py +++ b/sphinx_needs/schema/core.py @@ -479,22 +479,8 @@ def reduce_need( reduced_need: dict[str, Any] = {} for field, value in need.iter_extra_items(): - if value is None: - # field is unset, and schema validation cannot handle null types - continue - - schema_field = field_properties[field] - if "default" in schema_field: - if schema_field["default"] is None: - # keep all non-None values - they are actively set - reduced_need[field] = value - elif schema_field["default"] == "": - # SN5 case: only keep non-empty values - if value != "": - reduced_need[field] = value - else: - # keep the value - there is a default that is not None/"" or a real RST value - reduced_need[field] = value + if value is not None: + reduced_need[field] = value for field, value in need.iter_links_items(): if value: @@ -506,13 +492,16 @@ def reduce_need( continue if field in schema_properties: - # actively included by schema - include to validate constraints + # actively included by schema - include to validate constraints such as docname + # useful for both 'select' and 'validate' schemas + # TODO(Marco): think about how to remove this dependency on schema_properties; + # it's a performance issue reduced_need[field] = value continue - schema_field = field_properties[field] - if field == "status" and value is not None: # default is None + if field == "status": # default is None reduced_need[field] = value + if field == "tags" and len(value) > 0: # default is [] reduced_need[field] = value diff --git a/tests/schema/fixtures/unevaluated.yml b/tests/schema/fixtures/unevaluated.yml index 65a9770aa..cca81e360 100644 --- a/tests/schema/fixtures/unevaluated.yml +++ b/tests/schema/fixtures/unevaluated.yml @@ -1,3 +1,11 @@ +# 1. unevaluatedProperties: Anything not listed in 'properties' or 'required' fields +# of schemas has to be null. +# 2. empty string is a value actively set +# 3. None is used for missing values of fields +# 4. [] is used for missing values of link fields and the `tags` field (empty fields arrays use +# None, this should be streamlined) +# TODO(Marco): streamline this behavior + unevaluated_extra_no_schema_missing_in_rst: # should not complain, the comment field is unset # as there is no schema, the default is empty string, so unevaluatedProperties does not see it @@ -23,6 +31,9 @@ unevaluated_extra_no_schema_given_in_rst: # should not complain, the comment field is set, but as there is no schema; # the default is also an empty string, so the field is removed from the need and # unevaluatedProperties does not see it + + # in a nullable world, comment would be an empty string as it is given; + # so should lean to an error conf: | extensions = ["sphinx_needs"] needs_from_toml = "ubproject.toml"