Coverage for src / layout / process.py: 39%

71 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-21 16:36 +0000

1import os 

2import shutil 

3import sys 

4 

5 

6def _if(item, env, ctx): 

7 retval = item["if"] 

8 if retval not in [True, False]: 

9 retval = env.from_string(retval).render(**ctx) 

10 return retval 

11 

12 

13def _ensure_is_relative(orig_name, path, validpath): 

14 # path must be a relative sub path of validpath 

15 real_validpath = os.path.realpath(validpath) 

16 real_path = os.path.realpath(path) 

17 if not real_path.startswith(real_validpath): 17 ↛ 18line 17 didn't jump to line 18 because the condition on line 17 was never true

18 sys.exit(f'error: File "{orig_name}" is not subpath of the output path "{validpath}"') 

19 

20 

21def _action_move(item, env, ctx, output_dir): 

22 

23 src = str(env.from_string(item["src"]).render(**ctx)) 

24 src = os.path.join(output_dir, src) 

25 

26 dst = str(env.from_string(item["dst"]).render(**ctx)) 

27 dst = os.path.join(output_dir, dst) 

28 

29 # if file does not exist, we don't care about subpath check 

30 if _if(item, env, ctx): 

31 if not os.path.exists(src): 

32 return 

33 _ensure_is_relative(src, src, output_dir) 

34 _ensure_is_relative(dst, dst, output_dir) 

35 shutil.move(src, dst) 

36 elif item.get("else") == "remove": 

37 if not os.path.exists(src): 

38 return 

39 _ensure_is_relative(src, src, output_dir) 

40 os.remove(src) 

41 

42 

43def _action_remove(item, env, ctx, output_dir): 

44 if not _if(item, env, ctx): 

45 return 

46 

47 for path in item["path"]: 

48 orig_path = path 

49 path = str(env.from_string(path).render(**ctx)) 

50 path = os.path.join(output_dir, path) 

51 

52 # if file does not exist, we don't care about subpath check 

53 if os.path.islink(path) or os.path.isfile(path): 

54 _ensure_is_relative(orig_path, path, output_dir) 

55 os.remove(path) 

56 elif os.path.isdir(path): 

57 _ensure_is_relative(orig_path, path, output_dir) 

58 shutil.rmtree(path) 

59 

60 

61def _action_remove_nl(item, env, ctx, output_dir): 

62 if not _if(item, env, ctx): 

63 return 

64 

65 for path in item["path"]: 

66 orig_path = path 

67 path = str(env.from_string(path).render(**ctx)) 

68 path = os.path.join(output_dir, path) 

69 

70 _ensure_is_relative(orig_path, path, output_dir) 

71 with open(path, encoding="UTF-8") as fd: 

72 data = fd.read() 

73 data = data.rstrip() 

74 with open(path, mode="w", encoding="UTF-8") as fd: 

75 fd.write(data) 

76 

77 

78def _process(tpl, env, ctx, output_dir): 

79 for item in tpl.actions: 

80 action = item["action"] 

81 if action == "move": 

82 _action_move(item, env, ctx, output_dir) 

83 elif action == "remove": 

84 _action_remove(item, env, ctx, output_dir) 

85 elif action == "remove-trailing-newline": 

86 _action_remove_nl(item, env, ctx, output_dir) 

87 # pragma: cover: off 

88 else: 

89 raise NotImplementedError("deadcode reached") 

90 # pragma: cover: on 

91 return tpl 

92 

93 

94def process(tpl, env, ctx, **kwargs): 

95 output_dir = kwargs["output_dir"] 

96 

97 items = reversed(tpl.includes) 

98 for item in items: 

99 _process(item, env, ctx, output_dir) 

100 

101 return tpl