Skip to content

Commit e5ad4c5

Browse files
refactor(system): tighten discovery api and various small improvements
Signed-off-by: Roel <75250264+RoelBollens-TomTom@users.noreply.github.com>
1 parent 039c907 commit e5ad4c5

18 files changed

Lines changed: 603 additions & 444 deletions

File tree

packages/overture-schema-cli/src/overture/schema/cli/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
)
1212
from .types import (
1313
ErrorLocation,
14-
ModelDict,
1514
UnionType,
1615
ValidationErrorDict,
1716
)
@@ -25,7 +24,6 @@
2524
"perform_validation",
2625
"resolve_types",
2726
"ErrorLocation",
28-
"ModelDict",
2927
"UnionType",
3028
"ValidationErrorDict",
3129
]

packages/overture-schema-cli/src/overture/schema/cli/commands.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222
ModelKey,
2323
discover_models,
2424
filter_models,
25-
tags_by_key,
2625
)
26+
from overture.schema.system.discovery.tag import get_values_for_key
27+
from overture.schema.system.discovery.types import ModelDict
2728
from overture.schema.system.feature import Feature
2829
from overture.schema.system.json_schema import json_schema
2930

@@ -34,7 +35,7 @@
3435
select_most_likely_errors,
3536
)
3637
from .type_analysis import StructuralTuple, get_item_index, introspect_union
37-
from .types import ErrorLocation, ModelDict, UnionType
38+
from .types import ErrorLocation, UnionType
3839

3940
# Console instances for rich output
4041
stdout = Console(highlight=False)
@@ -214,7 +215,9 @@ def resolve_types(
214215
models: ModelDict = discover_models()
215216

216217
# Filter models based on CLI options
217-
models = filter_models(models, tags, excluded_tags, type_names)
218+
models = filter_models(
219+
models, tags=tags, excluded_tags=excluded_tags, type_names=type_names
220+
)
218221

219222
if not models:
220223
raise ValueError("No models found matching the specified criteria")
@@ -749,14 +752,14 @@ def json_schema_command(
749752
)
750753
@click.option(
751754
"--group-by",
752-
help="Group types by tag prefix (e.g., 'overture:theme')",
755+
help="Group types by tag key (e.g., 'overture:theme')",
753756
)
754757
def list_types(
755758
tags: tuple[str, ...], excluded_tags: tuple[str, ...], group_by: str | None
756759
) -> None:
757-
r"""List all available types grouped by theme with descriptions.
760+
r"""List all available types.
758761
759-
Displays all registered Overture Maps types and can organized by grouping.
762+
Displays all registered models and can be organized by grouping.
760763
761764
\b
762765
Examples:
@@ -772,7 +775,7 @@ def list_types(
772775
grouped_models: dict[str, set[ModelKey]] = {}
773776

774777
for key in models.keys():
775-
if groups := tags_by_key(key.tags, group_by):
778+
if groups := get_values_for_key(key.tags, group_by):
776779
for group in groups:
777780
grouped_models.setdefault(group, set()).add(key)
778781

packages/overture-schema-cli/src/overture/schema/cli/types.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,10 @@
55
from pydantic import BaseModel
66
from pydantic_core import ErrorDetails
77

8-
from overture.schema.system.discovery import ModelKey
9-
108
# Type alias for union types created from Pydantic models
119
# This represents either a single model or a discriminated union of models
1210
UnionType: TypeAlias = type[BaseModel] | Any
1311

14-
# Dictionary mapping ModelKey to Pydantic model classes
15-
ModelDict: TypeAlias = dict[ModelKey, type[BaseModel]]
16-
1712
# Pydantic validation error dictionary structure
1813
# In Pydantic v2, ValidationError.errors() returns list[ErrorDetails]
1914
ValidationErrorDict: TypeAlias = ErrorDetails

packages/overture-schema-codegen/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ renderers, not extraction logic.
2222
overture-codegen generate --format markdown --output-dir docs/schema/reference
2323

2424
# Generate for a single theme
25-
overture-codegen generate --format markdown --theme buildings --output-dir out/
25+
overture-codegen generate --format markdown --tag overture:theme=buildings --output-dir out/
2626

2727
# List discovered models
2828
overture-codegen list

packages/overture-schema-codegen/tests/codegen_test_support.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
is_model_class,
2626
)
2727
from overture.schema.codegen.extraction.type_analyzer import TypeInfo, TypeKind
28-
from overture.schema.system.discovery import discover_models, tags_by_key
28+
from overture.schema.system.discovery import discover_models, filter_models
29+
from overture.schema.system.discovery.tag import get_values_for_key
2930
from overture.schema.system.doc import DocumentedEnum
3031
from overture.schema.system.field_constraint import UniqueItemsConstraint
3132
from overture.schema.system.model_constraint import require_any_of
@@ -303,7 +304,7 @@ def find_member(spec: EnumSpec, name: str) -> EnumMemberSpec:
303304

304305
def find_theme(tags: frozenset[str]) -> str | None:
305306
"""Extract the theme from a set of tags, if present."""
306-
return next(iter(tags_by_key(tags, "overture:theme")), None)
307+
return next(iter(get_values_for_key(tags, "overture:theme")), None)
307308

308309

309310
T = TypeVar("T")
@@ -337,7 +338,7 @@ def flat_specs_from_discovery(
337338
"""Build a flat list of ModelSpecs from discovery, with entry_point set."""
338339
models = discover_models()
339340
if theme:
340-
models = {k: v for k, v in models.items() if find_theme(k.tags) == theme}
341+
models = filter_models(models, tags=(f"overture:theme={theme}",))
341342
result = []
342343
for key, cls in models.items():
343344
if not is_model_class(cls):

packages/overture-schema-core/src/overture/schema/core/tag_providers.py

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
from typing import Annotated, Any, Literal, Union, get_args, get_origin
1+
from typing import get_args
22

33
from pydantic import BaseModel
44

55
from overture.schema.core import OvertureFeature
66
from overture.schema.system.discovery import ModelKey
7+
from overture.schema.system.typing_util import collect_types
78

89
APPROVED = {
910
"overture.schema.addresses:Address",
@@ -28,6 +29,22 @@
2829
def authority_provider(
2930
model_class: type[BaseModel], key: ModelKey, tags: set[str]
3031
) -> set[str]:
32+
"""Add the ``"overture"`` tag if the model originates from an approved Overture package.
33+
34+
Parameters
35+
----------
36+
model_class : type[BaseModel]
37+
Model class to inspect.
38+
key : ModelKey
39+
Key identifying the model.
40+
tags : set[str]
41+
Current tags; may be extended.
42+
43+
Returns
44+
-------
45+
set[str]
46+
Updated tags, with ``"overture"`` added if applicable.
47+
"""
3148
if _matches_manifest(key):
3249
tags.add("overture")
3350
return tags
@@ -36,8 +53,24 @@ def authority_provider(
3653
def theme_provider(
3754
model_class: type[BaseModel], key: ModelKey, tags: set[str]
3855
) -> set[str]:
39-
for tp in _extract_types(model_class):
40-
if isinstance(tp, type) and issubclass(tp, OvertureFeature):
56+
"""Add the ``"overture:theme={theme}"`` tag if the model is a subclass of OvertureFeature.
57+
58+
Parameters
59+
----------
60+
model_class : type[BaseModel]
61+
Model class to inspect.
62+
key : ModelKey
63+
Key identifying the model.
64+
tags : set[str]
65+
Current tags; may be extended.
66+
67+
Returns
68+
-------
69+
set[str]
70+
Updated tags, with ``"overture:theme={theme}"`` added if applicable.
71+
"""
72+
for tp in collect_types(model_class):
73+
if issubclass(tp, OvertureFeature):
4174
tags.add(
4275
"overture:theme=" + get_args(tp.model_fields["theme"].annotation)[0]
4376
)
@@ -46,34 +79,3 @@ def theme_provider(
4679

4780
def _matches_manifest(key: ModelKey) -> bool:
4881
return key.entry_point in APPROVED
49-
50-
51-
def _extract_types(tp: Any) -> set[type]: # noqa: ANN401
52-
result: set[type] = set()
53-
54-
def visit(t: Any) -> None: # noqa: ANN401
55-
origin = get_origin(t)
56-
if origin is Annotated:
57-
visit(get_args(t)[0])
58-
return
59-
60-
if hasattr(t, "__supertype__"):
61-
visit(t.__supertype__)
62-
return
63-
64-
origin = get_origin(t)
65-
66-
if origin is Union:
67-
for arg in get_args(t):
68-
visit(arg)
69-
return
70-
71-
if origin is Literal:
72-
for val in get_args(t):
73-
result.add(type(val))
74-
return
75-
76-
result.add(t)
77-
78-
visit(tp)
79-
return result

packages/overture-schema-core/tests/test_approved_models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
def test_overture_feature_models_are_official() -> None:
55
models = discover_models()
66
for key in models:
7-
if "overture:feature" in key.tags:
8-
assert "overture:official" in key.tags, (
7+
if "feature" in key.tags:
8+
assert "overture" in key.tags, (
99
f"Model {key.name} is missing 'overture:official' tag."
1010
)

packages/overture-schema-system/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@ ignore = [
5757
per-file-ignores = {"__init__.py" = ["F401"]}
5858

5959
[project.entry-points."overture.tag_providers"]
60-
feature = "overture.schema.system.discovery:feature_provider"
60+
feature = "overture.schema.system.discovery.tag_providers:feature_provider"

0 commit comments

Comments
 (0)