@@ -19,6 +19,18 @@ def _cleanup_generated_files():
1919atexit .register (_cleanup_generated_files )
2020
2121
22+ def qualified_module_name (path ):
23+ """Return the fully qualified module name for the given file
24+ by walking up the directory tree for as long as the directories
25+ are packages (contain an __init__.py file)."""
26+ parts = [path .stem ]
27+ directory = path .parent
28+ while (directory / "__init__.py" ).exists ():
29+ parts .insert (0 , directory .name )
30+ directory = directory .parent
31+ return "." .join (parts )
32+
33+
2234def hook (hook_api ):
2335 collagraph_uses = hook_api .analysis .graph .get_code_using ("collagraph" )
2436
@@ -37,7 +49,7 @@ def hook(hook_api):
3749 # removing the need to bundle .cgx files and compile them
3850 # at runtime
3951 for cgx_path in cgx_files :
40- module_name = cgx_path . stem
52+ module_name = qualified_module_name ( cgx_path )
4153 tree , _name = construct_ast (cgx_path )
4254 python_source = ast .unparse (tree )
4355
@@ -104,8 +116,10 @@ def collect_hidden_imports(cgx_files):
104116 # Get the AST from the script tag
105117 script_tree = get_script_ast (parser , path )
106118
107- # Find a list of imported module names
108- imported_names = ImportsCollector ()
119+ # Find a list of imported module names, resolving relative
120+ # imports against the package that contains the CGX file
121+ package , _ , _ = qualified_module_name (path ).rpartition ("." )
122+ imported_names = ImportsCollector (package )
109123 imported_names .visit (script_tree )
110124
111125 hidden_imports |= imported_names .names
@@ -114,12 +128,26 @@ def collect_hidden_imports(cgx_files):
114128
115129
116130class ImportsCollector (ast .NodeVisitor ):
117- def __init__ (self ):
131+ def __init__ (self , package = "" ):
132+ self .package = package
118133 self .names = set ()
119134
120135 def visit_ImportFrom (self , node ):
136+ if node .level == 0 :
137+ if node .module :
138+ self .names .add (node .module )
139+ return
140+
141+ # Resolve relative import against the containing package
142+ parts = self .package .split ("." ) if self .package else []
143+ if node .level - 1 > len (parts ):
144+ return
145+ base = parts [: len (parts ) - (node .level - 1 )]
121146 if node .module :
122- self .names .add (node .module )
147+ self .names .add ("." .join ([* base , node .module ]))
148+ else :
149+ for alias in node .names :
150+ self .names .add ("." .join ([* base , alias .name ]))
123151
124152 def visit_Import (self , node ):
125153 for alias in node .names :
0 commit comments