4646from .metadata_tracker import build_field_path
4747
4848
49+ def _extract_extended_attributes (row : pd .Series , extended_attributes : tuple [str , ...]) -> dict [str , Any ]:
50+ """
51+ Extract extended attributes from a DataFrame row.
52+
53+ Args:
54+ row: pandas Series (DataFrame row) containing VSS node data
55+ extended_attributes: Tuple of extended attribute names to extract
56+
57+ Returns:
58+ Dictionary containing only the extended attributes that exist and are not NA
59+ """
60+ extracted = {}
61+ for ext_attr in extended_attributes :
62+ if ext_attr in row .index and pd .notna (row .get (ext_attr )):
63+ extracted [ext_attr ] = row [ext_attr ]
64+ return extracted
65+
66+
4967def create_unit_enums () -> tuple [dict [str , GraphQLEnumType ], dict [str , dict [str , dict [str , str ]]]]:
5068 """
5169 Create GraphQL enum types for VSS units grouped by quantity.
@@ -307,7 +325,12 @@ def create_struct_types(
307325 fields [field_name ] = GraphQLField (GraphQLNonNull (base_type ), description = prop_row .get ("description" , "" ))
308326
309327 field_path = build_field_path (type_name , field_name )
310- vspec_comments ["field_vss_types" ][field_path ] = {"element" : "STRUCT_PROPERTY" , "fqn" : prop_fqn }
328+ field_metadata = {"element" : "STRUCT_PROPERTY" , "fqn" : prop_fqn }
329+
330+ # Capture extended attributes if present
331+ field_metadata .update (_extract_extended_attributes (prop_row , extended_attributes ))
332+
333+ vspec_comments ["field_vss_types" ][field_path ] = field_metadata
311334
312335 if pd .notna (prop_row .get ("min" )) or pd .notna (prop_row .get ("max" )):
313336 vspec_comments ["field_ranges" ][field_path ] = {
@@ -321,7 +344,11 @@ def create_struct_types(
321344 struct_types [type_name ] = GraphQLObjectType (
322345 name = type_name , fields = fields , description = struct_row .get ("description" , "" )
323346 )
324- vspec_comments ["vss_types" ][type_name ] = {"element" : "STRUCT" , "fqn" : fqn }
347+
348+ # Store type-level metadata including extended attributes
349+ type_metadata = {"element" : "STRUCT" , "fqn" : fqn }
350+ type_metadata .update (_extract_extended_attributes (struct_row , extended_attributes ))
351+ vspec_comments ["vss_types" ][type_name ] = type_metadata
325352
326353 return struct_types
327354
@@ -367,6 +394,7 @@ def create_object_type(
367394 types_registry : dict [str , Any ],
368395 unit_enums : dict [str , GraphQLEnumType ],
369396 vspec_comments : dict [str , dict [str , Any ]],
397+ extended_attributes : tuple [str , ...] = (),
370398) -> GraphQLObjectType :
371399 """
372400 Create GraphQL object type for a VSS branch.
@@ -381,6 +409,7 @@ def create_object_type(
381409 types_registry: Registry of already-created types
382410 unit_enums: Unit enum types
383411 vspec_comments: Metadata tracking dictionary
412+ extended_attributes: Extended attribute names from CLI
384413
385414 Returns:
386415 GraphQL object type for the branch
@@ -415,7 +444,12 @@ def get_fields() -> dict[str, GraphQLField]:
415444 field_path = build_field_path (type_name , field_name )
416445
417446 if leaf_type := _get_vss_type_if_valid (leaf_row ):
418- vspec_comments ["field_vss_types" ][field_path ] = {"element" : leaf_type , "fqn" : child_fqn }
447+ field_metadata = {"element" : leaf_type , "fqn" : child_fqn }
448+
449+ # Capture extended attributes if present
450+ field_metadata .update (_extract_extended_attributes (leaf_row , extended_attributes ))
451+
452+ vspec_comments ["field_vss_types" ][field_path ] = field_metadata
419453
420454 if pd .notna (leaf_row .get ("min" )) or pd .notna (leaf_row .get ("max" )):
421455 vspec_comments ["field_ranges" ][field_path ] = {
@@ -434,12 +468,12 @@ def get_fields() -> dict[str, GraphQLField]:
434468 for child_fqn , child_row in branches_df [branches_df ["parent" ] == fqn ].iterrows ():
435469 field_name = convert_name_for_graphql_schema (child_row ["name" ], GraphQLElementType .FIELD , S2DM_CONVERSIONS )
436470 child_type = types_registry .get (child_fqn ) or create_object_type (
437- child_fqn , branches_df , leaves_df , types_registry , unit_enums , vspec_comments
471+ child_fqn , branches_df , leaves_df , types_registry , unit_enums , vspec_comments , extended_attributes
438472 )
439473 types_registry [child_fqn ] = child_type
440474
441475 hoisted_fields = get_hoisted_fields (
442- child_fqn , child_row , leaves_df , types_registry , unit_enums , vspec_comments
476+ child_fqn , child_row , leaves_df , types_registry , unit_enums , vspec_comments , extended_attributes
443477 )
444478 fields .update (hoisted_fields )
445479
@@ -450,7 +484,10 @@ def get_fields() -> dict[str, GraphQLField]:
450484
451485 return fields
452486
453- vspec_comments ["vss_types" ][type_name ] = {"element" : "BRANCH" , "fqn" : fqn }
487+ # Store type-level metadata including extended attributes
488+ type_metadata = {"element" : "BRANCH" , "fqn" : fqn }
489+ type_metadata .update (_extract_extended_attributes (branch_row , extended_attributes ))
490+ vspec_comments ["vss_types" ][type_name ] = type_metadata
454491
455492 return GraphQLObjectType (name = type_name , fields = get_fields , description = branch_row .get ("description" , "" ))
456493
@@ -462,6 +499,7 @@ def get_hoisted_fields(
462499 types_registry : dict [str , Any ],
463500 unit_enums : dict [str , GraphQLEnumType ],
464501 vspec_comments : dict [str , dict [str , Any ]],
502+ extended_attributes : tuple [str , ...] = (),
465503) -> dict [str , GraphQLField ]:
466504 """Get fields to hoist from instantiated child branch to parent."""
467505 hoisted : dict [str , GraphQLField ] = {}
@@ -492,12 +530,17 @@ def get_hoisted_fields(
492530 field_path = build_field_path (parent_type_name , hoisted_field_name )
493531
494532 if leaf_type := _get_vss_type_if_valid (leaf_row ):
495- vspec_comments [ "field_vss_types" ][ field_path ] = {
533+ field_metadata = {
496534 "element" : leaf_type ,
497535 "fqn" : leaf_fqn ,
498536 "instantiate" : False ,
499537 }
500538
539+ # Capture extended attributes if present
540+ field_metadata .update (_extract_extended_attributes (leaf_row , extended_attributes ))
541+
542+ vspec_comments ["field_vss_types" ][field_path ] = field_metadata
543+
501544 if pd .notna (leaf_row .get ("min" )) or pd .notna (leaf_row .get ("max" )):
502545 vspec_comments ["field_ranges" ][field_path ] = {
503546 "min" : leaf_row .get ("min" ) if pd .notna (leaf_row .get ("min" )) else None ,
0 commit comments