Coverage for circular_deps / resolvers / python.py: 54%

54 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 15:04 -0800

1from __future__ import annotations 

2 

3import os 

4 

5from circular_deps.resolvers.base import PathResolver 

6 

7 

8class PythonPathResolver(PathResolver): 

9 def resolve(self, module, current_file, root_dir): 

10 if module.startswith("."): 

11 return self._resolve_relative(module, current_file, root_dir) 

12 else: 

13 return self._resolve_absolute(module, current_file, root_dir) 

14 

15 def _resolve_relative(self, module, current_file, root_dir): 

16 current_dir = os.path.dirname(current_file) 

17 

18 if module.startswith(".."): 

19 parts = [] 

20 i = 0 

21 while i < len(module) and module[i] == ".": 

22 parts.append("..") 

23 i += 1 

24 if parts: 

25 module_suffix = module[len(parts) :].strip(".") 

26 target_dir = os.path.abspath(os.path.join(current_dir, *parts)) 

27 if module_suffix: 

28 target_path = os.path.join( 

29 target_dir, module_suffix.replace(".", os.sep) 

30 ) 

31 else: 

32 target_path = target_dir 

33 else: 

34 target_path = os.path.abspath( 

35 os.path.join(current_dir, module.lstrip(".")) 

36 ) 

37 else: 

38 module_name = module.lstrip(".").strip(".") 

39 if module_name: 

40 target_path = os.path.abspath( 

41 os.path.join(current_dir, module_name.replace(".", os.sep)) 

42 ) 

43 else: 

44 return None 

45 

46 return self._find_py_file(target_path, root_dir) 

47 

48 def _resolve_absolute(self, module, current_file, root_dir): 

49 module_path = module.replace(".", os.sep) 

50 current_dir = os.path.dirname(current_file) 

51 

52 for search_path in [current_dir, root_dir]: 

53 target_path = os.path.join(search_path, module_path) 

54 resolved = self._find_py_file(target_path, root_dir) 

55 if resolved: 

56 return resolved 

57 

58 return None 

59 

60 def _find_py_file(self, target_path, root_dir): 

61 if os.path.isfile(target_path) and target_path.endswith(".py"): 

62 return self._check_under_root(target_path, root_dir) 

63 

64 if os.path.isdir(target_path): 

65 init_py = os.path.join(target_path, "__init__.py") 

66 if os.path.isfile(init_py): 

67 return self._check_under_root(init_py, root_dir) 

68 

69 py_file = target_path + ".py" 

70 if os.path.isfile(py_file): 

71 return self._check_under_root(py_file, root_dir) 

72 

73 return None 

74 

75 def _check_under_root(self, path, root_dir): 

76 abs_path = os.path.abspath(path) 

77 abs_root = os.path.abspath(root_dir) 

78 if abs_path.startswith(abs_root): 

79 return abs_path 

80 return None