Skip to content

Commit b9faa9a

Browse files
Heejong Leemeta-codesync[bot]
authored andcommitted
SyntaxGraph bridge for Python type system
Summary: Bridges the Python SyntaxGraph (the AST view of Thrift schemas) into the runtime TypeSystem, letting schema definitions be consumed through the unified TypeSystem interface. The SchemaRegistry becomes the on-demand, lazily-resolving entry point for turning schema URIs into runtime type nodes. Key changes: - Add a new SyntaxGraph → TypeSystem bridge that lazily materializes memoized, fully-resolved type nodes from AST definitions, with cycle-safe construction (recursive types resolve against stable identities) and atomic, all-or-nothing builds (a failed build leaves nothing partially cached) - Make SchemaRegistry itself the unbounded TypeSystem view, resolving types on demand via module discovery rather than enumerating all URIs up front - Encode the key schema semantics at the boundary: typedefs are erased to their true type, the IDL any type maps to the ANY primitive, unions force optional fields, and terse-write presence is preserved through the graph so field qualifiers map correctly Reviewed By: praihan Differential Revision: D107588115 fbshipit-source-id: 6ce2a7f4e30fbf59aafa95de6411921140f22d49
1 parent 6961552 commit b9faa9a

7 files changed

Lines changed: 710 additions & 27 deletions

File tree

third-party/thrift/src/thrift/lib/python/schema/schema_registry.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import zstandard # @manual=fbsource//third-party/pypi/zstandard:zstandard
3535
from apache.thrift.type.schema import thrift_types as _schema_types
3636
from thrift.lib.python.schema.syntax_graph import Definition, SyntaxGraph
37+
from thrift.lib.python.schema.type_system import DefinitionNode, TypeSystem
38+
from thrift.lib.python.schema.type_system_bridge import SyntaxGraphBridge
3739
from thrift.python.serializer import deserialize, Protocol
3840

3941

@@ -126,8 +128,13 @@ def _get_definition_key(thrift_type: type[Any]) -> bytes | None:
126128
return key
127129

128130

129-
class SchemaRegistry:
130-
"""Pure Python registry for looking up SyntaxGraph Definitions by type or URI."""
131+
class SchemaRegistry(TypeSystem):
132+
"""Pure Python registry for looking up SyntaxGraph Definitions by type or URI.
133+
134+
The registry is also the unbounded ``TypeSystem`` view: it bridges SyntaxGraph
135+
definitions into runtime ``DefinitionNode``s on demand, memoized.
136+
``get_known_uris()`` returns ``None`` because the registry is
137+
lazy/module-discovery based and cannot enumerate all URIs up front."""
131138

132139
_instance: SchemaRegistry | None = None
133140

@@ -154,6 +161,7 @@ def __init__(self) -> None:
154161
self._builder: IncrementalGraphBuilder = IncrementalGraphBuilder()
155162
self._module_resolver: Callable[[str], types.ModuleType | None] | None = None
156163
self._uri_module_map: dict[str, str] | None = None
164+
self._ts_bridge: SyntaxGraphBridge = SyntaxGraphBridge(self)
157165
self._omnibus_seeded: bool = False
158166

159167
@property
@@ -241,6 +249,29 @@ def get_definition_by_uri(self, uri: str) -> Definition:
241249

242250
raise KeyError(f"Definition not found for URI {uri!r}")
243251

252+
# -- TypeSystem interface (bridged from SyntaxGraph) --------------------
253+
254+
def definition_by_uri(self, uri: str) -> Definition | None:
255+
"""SyntaxResolver hook: AST Definition for ``uri`` (``None`` if unknown).
256+
257+
Wraps ``get_definition_by_uri`` -- triggering the same lazy module
258+
discovery -- but returns ``None`` instead of raising on a miss."""
259+
try:
260+
return self.get_definition_by_uri(uri)
261+
except KeyError:
262+
return None
263+
264+
def get_user_defined_type(self, uri: str) -> DefinitionNode | None:
265+
"""Resolve a URI to a TypeSystem ``DefinitionNode`` (bridged from the
266+
SyntaxGraph, memoized). ``None`` if unknown or not a user-defined type
267+
(typedefs/constants/services/interactions are excluded)."""
268+
return self._ts_bridge.get_user_defined_type(uri)
269+
270+
def get_known_uris(self) -> None:
271+
"""``None`` -- the registry is lazy/module-discovery based and cannot
272+
enumerate all URIs up front."""
273+
return None
274+
244275
def _resolve_module_for_uri(self, uri: str) -> types.ModuleType | None:
245276
"""Find the module that defines the given URI."""
246277
self._build_uri_to_module_index()

third-party/thrift/src/thrift/lib/python/schema/syntax_graph.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@
2626
from typing import Any, Generic, TypeVar
2727

2828
from apache.thrift.protocol.detail.protocol_detail.thrift_types import Value
29-
from apache.thrift.syntax_graph.syntax_graph.thrift_types import ( # noqa: F401 -- re-export
30-
FieldPresenceQualifier,
31-
Primitive,
32-
)
29+
from apache.thrift.syntax_graph.syntax_graph.thrift_types import Primitive
3330
from apache.thrift.type.schema import thrift_types as _schema_types
31+
from apache.thrift.type.schema.thrift_types import FieldQualifier
3432
from thrift.python.serializer import deserialize, Protocol
3533

3634

@@ -440,7 +438,7 @@ class FieldNode:
440438
"_id",
441439
"_name",
442440
"_type",
443-
"_presence",
441+
"_qualifier",
444442
"_doc_block",
445443
"_annotations",
446444
"_default_value",
@@ -449,7 +447,7 @@ class FieldNode:
449447
_id: int
450448
_name: str
451449
_type: TypeRef
452-
_presence: FieldPresenceQualifier
450+
_qualifier: FieldQualifier
453451
_doc_block: str | None
454452
_annotations: list[Annotation]
455453
_default_value: Value | None
@@ -461,15 +459,15 @@ def __init__(
461459
id: int,
462460
name: str,
463461
type: TypeRef,
464-
presence: FieldPresenceQualifier,
462+
qualifier: FieldQualifier,
465463
doc_block: str | None,
466464
annotations: list[Annotation],
467465
default_value: Value | None = None,
468466
) -> None:
469467
self._id = id
470468
self._name = name
471469
self._type = type
472-
self._presence = presence
470+
self._qualifier = qualifier
473471
self._doc_block = doc_block
474472
self._annotations = annotations
475473
self._default_value = default_value
@@ -488,8 +486,8 @@ def type(self) -> TypeRef:
488486
return self._type
489487

490488
@property
491-
def presence(self) -> FieldPresenceQualifier:
492-
return self._presence
489+
def qualifier(self) -> FieldQualifier:
490+
return self._qualifier
493491

494492
@property
495493
def doc_block(self) -> str | None:

third-party/thrift/src/thrift/lib/python/schema/syntax_graph_builder.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@
2424
import functools
2525
from typing import Any
2626

27-
from apache.thrift.syntax_graph.syntax_graph.thrift_types import (
28-
FieldPresenceQualifier,
29-
Primitive,
30-
)
27+
from apache.thrift.syntax_graph.syntax_graph.thrift_types import Primitive
3128
from apache.thrift.type.schema import thrift_types as _schema_types
3229
from apache.thrift.type.standard import thrift_types as _standard_types
3330
from apache.thrift.type.type_rep import thrift_types as _type_rep_types
@@ -348,13 +345,6 @@ def _type_of_or_none(
348345

349346
# -- Field conversion --------------------------------------------------
350347

351-
def _presence_of(
352-
self, qualifier: _schema_types.FieldQualifier
353-
) -> FieldPresenceQualifier:
354-
if qualifier == _schema_types.FieldQualifier.Optional:
355-
return FieldPresenceQualifier.OPTIONAL
356-
return FieldPresenceQualifier.UNQUALIFIED
357-
358348
def _doc_block_of(self, attrs: _schema_types.DefinitionAttrs) -> str | None:
359349
if attrs.docs and attrs.docs.contents:
360350
return attrs.docs.contents
@@ -373,7 +363,7 @@ def _create_field(self, field: _schema_types.Field) -> FieldNode:
373363
id=field.id,
374364
name=field.attrs.name,
375365
type=self._type_of(field.type),
376-
presence=self._presence_of(field.qualifier),
366+
qualifier=field.qualifier,
377367
doc_block=self._doc_block_of(field.attrs),
378368
annotations=self._create_annotations(field.attrs),
379369
default_value=self._resolve_value(field.customDefault),

third-party/thrift/src/thrift/lib/python/schema/tests/test_syntax_graph.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
ExceptionNode,
2727
ExceptionTypeRef,
2828
FieldNode,
29-
FieldPresenceQualifier,
29+
FieldQualifier,
3030
FunctionNode,
3131
InteractionNode,
3232
ListTypeRef,
@@ -104,11 +104,11 @@ def test_struct_fields(self) -> None:
104104
self.assertEqual(fields[0].name, "field1")
105105
self.assertIsInstance(fields[0].type, PrimitiveTypeRef)
106106
self.assertEqual(fields[0].type.primitive, Primitive.I32)
107-
self.assertEqual(fields[0].presence, FieldPresenceQualifier.UNQUALIFIED)
107+
self.assertEqual(fields[0].qualifier, FieldQualifier.Default)
108108

109109
self.assertEqual(fields[1].id, 2)
110110
self.assertEqual(fields[1].name, "field2")
111-
self.assertEqual(fields[1].presence, FieldPresenceQualifier.OPTIONAL)
111+
self.assertEqual(fields[1].qualifier, FieldQualifier.Optional)
112112

113113
def test_enum_basic(self) -> None:
114114
prog = self.graph.get_program_by_name("syntax_graph")

0 commit comments

Comments
 (0)