Resolve Python module to file path For relative imports (starting with .), resolves them relative to current_dir. For absolute imports, tries to resolve from output_dir. Returns path relative to output_dir with file extension.
(self, module_path: str)
| 152 | return results |
| 153 | |
| 154 | def _resolve_python_path(self, module_path: str) -> Optional[str]: |
| 155 | """Resolve Python module to file path |
| 156 | |
| 157 | For relative imports (starting with .), resolves them relative to current_dir. |
| 158 | For absolute imports, tries to resolve from output_dir. |
| 159 | |
| 160 | Returns path relative to output_dir with file extension. |
| 161 | """ |
| 162 | |
| 163 | # Helper function to safely convert to relative path |
| 164 | def safe_relpath(path): |
| 165 | """Convert path to relative from output_dir, handling mixed abs/rel paths. |
| 166 | |
| 167 | IMPORTANT: In Python imports, paths should always be absolute (from current_dir). |
| 168 | Always convert output_dir to absolute to avoid relpath bugs. |
| 169 | Use realpath to resolve symlinks (e.g., /var vs /private/var on macOS). |
| 170 | """ |
| 171 | # Use realpath to resolve symlinks and get canonical paths |
| 172 | abs_output_dir = os.path.realpath(os.path.abspath(self.output_dir)) |
| 173 | |
| 174 | # Path should be absolute (constructed from absolute current_dir) |
| 175 | # If somehow it's relative, make it absolute from output_dir |
| 176 | if os.path.isabs(path): |
| 177 | abs_path = os.path.realpath(path) |
| 178 | else: |
| 179 | # This shouldn't happen in Python imports, but handle it anyway |
| 180 | abs_path = os.path.realpath(os.path.join(abs_output_dir, path)) |
| 181 | |
| 182 | return os.path.relpath(abs_path, abs_output_dir) |
| 183 | |
| 184 | # Handle relative imports (., .., ...) |
| 185 | if module_path.startswith('.'): |
| 186 | # Count leading dots |
| 187 | dots = 0 |
| 188 | for char in module_path: |
| 189 | if char == '.': |
| 190 | dots += 1 |
| 191 | else: |
| 192 | break |
| 193 | |
| 194 | # Get the module part after dots |
| 195 | module_part = module_path[dots:] |
| 196 | |
| 197 | # Calculate the target directory |
| 198 | # . means current directory, .. means parent, etc. |
| 199 | target_dir = self.current_dir |
| 200 | for _ in range(dots - 1): # -1 because . means current dir |
| 201 | target_dir = os.path.dirname(target_dir) |
| 202 | |
| 203 | # If there's a module part, append it |
| 204 | if module_part: |
| 205 | module_file_path = module_part.replace('.', os.sep) |
| 206 | target_dir = os.path.join(target_dir, module_file_path) |
| 207 | |
| 208 | # Try as package |
| 209 | package_init = os.path.normpath( |
| 210 | os.path.join(target_dir, '__init__.py')) |
| 211 | if os.path.exists(package_init): |
no outgoing calls
no test coverage detected