@@ -992,36 +992,42 @@ def print_state_tree(
992992)
993993@click .argument ("minified_path" )
994994def state_lookup (output_json : bool , minified_path : str ):
995- """Lookup a state by its minified path (e.g., 'a.bU')."""
996- from reflex .state import _minified_name_to_int , _state_id_registry
995+ """Lookup a state by its minified path (e.g., 'a.bU').
996+
997+ Walks the state tree from the root to resolve each segment.
998+ """
999+ from reflex .state import State
9971000 from reflex .utils import prerequisites
9981001
9991002 # Load the user's app to register all state classes
10001003 prerequisites .get_app ()
10011004
1002- # Parse the dotted path
1003- parts = minified_path .split ("." )
1005+ try :
1006+ State .get_class_substate (minified_path )
1007+ except ValueError :
1008+ msg = f"No state found for path: { minified_path } "
1009+ console .error (msg )
1010+ raise ValueError (msg ) from None
10041011
1005- # Resolve each part
1012+ # Build info for each ancestor segment
1013+ parts = minified_path .split ("." )
10061014 result_parts = []
1007- for part in parts :
1008- try :
1009- state_id = _minified_name_to_int (part )
1010- except ValueError as err :
1011- console .error (f"Invalid minified name: { part } " )
1012- raise SystemExit (1 ) from err
1013-
1014- state_cls = _state_id_registry .get (state_id )
1015- if state_cls is None :
1016- console .error (f"No state registered with state_id={ state_id } " )
1017- raise SystemExit (1 )
1018-
1015+ current = State
1016+ result_parts .append ({
1017+ "minified" : parts [0 ],
1018+ "state_id" : current ._state_id ,
1019+ "module" : current .__module__ ,
1020+ "class" : current .__name__ ,
1021+ "full_name" : current .get_full_name (),
1022+ })
1023+ for part in parts [1 :]:
1024+ current = current .get_class_substate (part )
10191025 result_parts .append ({
10201026 "minified" : part ,
1021- "state_id" : state_id ,
1022- "module" : state_cls .__module__ ,
1023- "class" : state_cls .__name__ ,
1024- "full_name" : state_cls .get_full_name (),
1027+ "state_id" : current . _state_id ,
1028+ "module" : current .__module__ ,
1029+ "class" : current .__name__ ,
1030+ "full_name" : current .get_full_name (),
10251031 })
10261032
10271033 if output_json :
@@ -1034,31 +1040,83 @@ def state_lookup(output_json: bool, minified_path: str):
10341040 console .log (f"{ info ['module' ]} .{ info ['class' ]} " )
10351041
10361042
1043+ def _resolve_parent_state (parent : str ):
1044+ """Resolve a parent argument to a state class.
1045+
1046+ Accepts either a state path (minified like 'a.b' or full name) or a class
1047+ name (e.g., 'State', 'MySubState'). Tries path resolution first via
1048+ get_class_substate, then falls back to searching by class name.
1049+
1050+ Args:
1051+ parent: Class name or state path identifying the parent state.
1052+
1053+ Returns:
1054+ The resolved state class.
1055+
1056+ Raises:
1057+ SystemExit: If the parent cannot be resolved.
1058+ """
1059+ from reflex .state import BaseState , State
1060+
1061+ # Try as a state path (minified or full name)
1062+ try :
1063+ return State .get_class_substate (parent )
1064+ except ValueError :
1065+ pass
1066+
1067+ # Fall back to searching by class name
1068+ def _find_by_name (cls : type [BaseState ], name : str ) -> type [BaseState ] | None :
1069+ if cls .__name__ == name :
1070+ return cls
1071+ for child in cls .class_subclasses :
1072+ result = _find_by_name (child , name )
1073+ if result is not None :
1074+ return result
1075+ return None
1076+
1077+ result = _find_by_name (State , parent )
1078+ if result is not None :
1079+ return result
1080+
1081+ console .error (f"No state found matching '{ parent } '" )
1082+ raise SystemExit (1 )
1083+
1084+
10371085@cli .command (name = "state-next-id" )
10381086@loglevel_option
10391087@click .option (
10401088 "--after-max" ,
10411089 is_flag = True ,
10421090 help = "Return max(state_id) + 1 instead of first gap." ,
10431091)
1044- def state_next_id (after_max : bool ):
1045- """Print the next available state_id."""
1046- from reflex .state import _state_id_registry
1092+ @click .argument ("parent" )
1093+ def state_next_id (after_max : bool , parent : str ):
1094+ """Print the next available state_id under PARENT.
1095+
1096+ PARENT can be a class name (e.g., 'State', 'MySubState') or a
1097+ minified path (e.g., 'a', 'a.b'). Auto-determined from input.
1098+ """
10471099 from reflex .utils import prerequisites
10481100
10491101 # Load the user's app to register all state classes
10501102 prerequisites .get_app ()
10511103
1052- if not _state_id_registry :
1104+ parent_cls = _resolve_parent_state (parent )
1105+
1106+ # Collect sibling state_ids under the parent
1107+ used_ids = {
1108+ child ._state_id
1109+ for child in parent_cls .class_subclasses
1110+ if child ._state_id is not None
1111+ }
1112+
1113+ if not used_ids :
10531114 console .log ("0" )
10541115 return
10551116
10561117 if after_max :
1057- # Return max + 1
1058- next_id = max (_state_id_registry .keys ()) + 1
1118+ next_id = max (used_ids ) + 1
10591119 else :
1060- # Find first gap starting from 0
1061- used_ids = set (_state_id_registry .keys ())
10621120 next_id = 0
10631121 while next_id in used_ids :
10641122 next_id += 1
0 commit comments