Coverage for dirimport/gen.py: 89%

77 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-09-05 14:22 +0000

1import difflib 

2import importlib 

3import os 

4import sys 

5import types 

6from logging import getLogger 

7 

8from jinja2 import Template 

9 

10log = getLogger(__name__) 

11 

12tmpl = Template("""# Code generated by dirimport - DO NOT EDIT. 

13{%- for d in dirs %} 

14from . import {{d}} # noqa 

15{%- endfor %} 

16{%- for f in files %} 

17from .{{f}} import * # noqa 

18{%- endfor %} 

19""") 

20 

21 

22def dig(dirname): 

23 resd = {} 

24 resf = [] 

25 for f in sorted(os.listdir(dirname)): 

26 if f.startswith(("_", ".")): 

27 continue 

28 fpath = os.path.join(dirname, f) 

29 if os.path.isdir(fpath): 

30 x = dig(fpath) 

31 if len(x[0]) == 0 and len(x[1]) == 0: 

32 continue 

33 resd[f] = x 

34 elif os.path.isfile(fpath) and f.endswith(".py"): 34 ↛ 25line 34 didn't jump to line 25 because the condition on line 34 was always true

35 resf.append(f[:-3]) 

36 return resd, resf 

37 

38 

39def generate(data, basename, filename="__init__.py"): 

40 dirs, files = data 

41 ofn = os.path.join(basename, filename) 

42 if len(dirs) == 0 and len(files) == 0: 42 ↛ 43line 42 didn't jump to line 43 because the condition on line 42 was never true

43 return 

44 with open(ofn, "w") as ofp: 

45 print(tmpl.render(dirs=dirs.keys(), files=files).strip(), file=ofp) 

46 for k, v in dirs.items(): 

47 generate(v, os.path.join(basename, k), filename) 

48 

49 

50def diff(data, basename, filename="__init__.py"): 

51 dirs, files = data 

52 ofn = os.path.join(basename, filename) 

53 if os.path.exists(ofn): 

54 with open(ofn) as ofp: 

55 orig = [line for line in (raw.strip() for raw in ofp) if line != ""] 

56 else: 

57 orig = [] 

58 newtxt = tmpl.render(dirs=dirs.keys(), files=files) 

59 newdata = list(filter(lambda f: f.strip() != "", newtxt.split("\n"))) 

60 res = list(difflib.unified_diff(orig, newdata, fromfile=ofn + ".orig", tofile=ofn)) 

61 for k, v in dirs.items(): 61 ↛ 62line 61 didn't jump to line 62 because the loop on line 61 never started

62 res.extend(diff(v, os.path.join(basename, k), filename)) 

63 return res 

64 

65 

66def clear(basename, filename="__init__.py"): 

67 for root, dirs, files in os.walk(basename): 

68 if filename in files: 68 ↛ 67line 68 didn't jump to line 67 because the condition on line 68 was always true

69 os.unlink(os.path.join(root, filename)) 

70 

71 

72def importdata(data, basename, rootdir="."): 

73 dirs, files = data 

74 res = types.ModuleType(basename) 

75 sys.path.insert(0, rootdir) 

76 log.debug("path %s", rootdir) 

77 for f in files: 

78 # from .f import * 

79 log.debug("loading(f) %s %s", basename, f) 

80 tmp = importlib.import_module(basename + "." + f) 

81 log.debug("loaded: %s, file=%s", tmp, tmp.__file__) 

82 for v in dir(tmp): 

83 if v.startswith("_"): 

84 continue 

85 if hasattr(res, v): 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true

86 log.info("duplicate symbol %s %s %s", basename, f, v) 

87 continue 

88 obj = getattr(tmp, v) 

89 log.debug("set attribute: %s.%s = %s", res, v, obj) 

90 setattr(res, v, obj) 

91 sys.path.pop(0) 

92 for k, v in dirs.items(): 92 ↛ 93line 92 didn't jump to line 93 because the loop on line 92 never started

93 log.debug("loading(d) %s %s %s", basename, k, v) 

94 setattr(res, k, importdata(v, basename + "." + k, rootdir)) 

95 return res 

96 

97 

98def importall(basename): 

99 data = dig(basename) 

100 log.debug("dig %s: %s", basename, data) 

101 return importdata(data, os.path.basename(basename), os.path.dirname(basename))