Coverage for bookbuilderpy/preprocessor.py: 67%

81 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-17 23:15 +0000

1"""The preprocessor commands to be applied once the text has been loaded.""" 

2from typing import Callable, Final 

3 

4import bookbuilderpy.constants as bc 

5from bookbuilderpy.git import Repo 

6from bookbuilderpy.path import Path 

7from bookbuilderpy.preprocessor_code import get_programming_language, load_code 

8from bookbuilderpy.preprocessor_commands import create_preprocessor 

9from bookbuilderpy.strings import enforce_non_empty_str 

10 

11 

12def preprocess(text: str, 

13 input_dir: str, 

14 get_meta: Callable, 

15 get_repo: Callable, 

16 repo: Repo | None, 

17 output_dir: str) -> str: 

18 """ 

19 Apply all the preprocessor commands to the given text. 

20 

21 :param text: the text of the book to be preprocessed. 

22 :param input_dir: the input director 

23 :param get_meta: a command for obtaining meta information. 

24 :param get_repo: a command for obtaining a repository 

25 :param repo: the root repository of the project 

26 :param output_dir: the output directory 

27 """ 

28 src = Path.directory(input_dir) 

29 dst = Path.directory(output_dir) 

30 src.enforce_neither_contains(dst) 

31 

32 # execute all meta-data commands 

33 text = (create_preprocessor(name=bc.CMD_GET_META, 

34 func=get_meta, 

35 n=1, 

36 strip_white_space=False))(text) 

37 

38 def __get_repo(repo_id, info_id) -> str: 

39 gitrepo: Final[Repo] = get_repo(repo_id) 

40 if info_id == bc.META_REPO_INFO_URL: 

41 return gitrepo.get_base_url() 

42 if info_id == bc.META_REPO_INFO_DATE: 

43 return gitrepo.date_time 

44 if info_id == bc.META_REPO_INFO_COMMIT: 

45 return gitrepo.commit 

46 if info_id == bc.META_REPO_INFO_NAME: 

47 return gitrepo.get_name() 

48 raise ValueError( 

49 f"Invalid repo query '{info_id}' for repo '{repo_id}'.") 

50 

51 # execute all repo-info commands 

52 text = (create_preprocessor(name=bc.CMD_GET_REPO, 

53 func=__get_repo, 

54 n=2, 

55 strip_white_space=False))(text) 

56 

57 # make the definitions 

58 def_map: dict[str, str] = {} 

59 def_count: dict[str, int] = {} 

60 

61 def __make_def(deftype: str, 

62 label: str, 

63 body: str) -> str: 

64 nonlocal def_map 

65 nonlocal def_count 

66 prefix = enforce_non_empty_str(get_meta(f"{deftype}Title").strip()) 

67 count: int = def_count.get(deftype, 0) + 1 

68 def_count[deftype] = count 

69 anchor = f"{prefix} {count}" 

70 enforce_non_empty_str(label) 

71 if label in def_map: 

72 raise ValueError( 

73 f"Redefined label '{label}' of type '{deftype}'.") 

74 label_name = f"_def:{count}:{label}" 

75 def_map[label] = f"[{anchor}](#{label_name})" 

76 enforce_non_empty_str(body) 

77 return f'<div id="{label_name}">**{anchor}.**&nbsp;{body}</div>' 

78 

79 text = (create_preprocessor(name=bc.CMD_DEFINITION, 

80 func=__make_def, 

81 n=3, 

82 strip_white_space=True, 

83 wrap_in_newlines=3))(text) 

84 del def_count, __make_def 

85 

86 text = (create_preprocessor(name=bc.CMD_DEF_REF, 

87 func=def_map.__getitem__, 

88 n=1, 

89 strip_white_space=False))(text) 

90 del def_map 

91 

92 # create all figures 

93 def __make_absolute_figure(label: str, 

94 caption: str, 

95 path: str, 

96 args: str) -> str: 

97 nonlocal src 

98 nonlocal dst 

99 new_path = Path.copy_resource(src, path, dst) 

100 caption = enforce_non_empty_str(caption.strip()) 

101 use_path = enforce_non_empty_str(new_path.relative_to(dst).strip()) 

102 cmd = enforce_non_empty_str(" ".join([enforce_non_empty_str( 

103 label.strip()), args.strip()]).strip()) 

104 return f"![{caption}]({use_path}){{#fig:{cmd}}}" 

105 

106 text = (create_preprocessor(name=bc.CMD_ABSOLUTE_FIGURE, 

107 func=__make_absolute_figure, 

108 n=4, strip_white_space=True, 

109 wrap_in_newlines=2))(text) 

110 

111 # make a code section 

112 def __make_code(label: str, caption: str, code: str, file: Path, 

113 userepo: Repo | None) -> str: 

114 code = enforce_non_empty_str(code.strip()) 

115 label = enforce_non_empty_str(label.strip()) 

116 code = enforce_non_empty_str(code.strip()) 

117 plang: str | None = get_programming_language(file) 

118 plang = "" if plang is None else f" .{plang}" 

119 

120 caption = enforce_non_empty_str(caption.strip()) 

121 spacer = " " 

122 end = caption[len(caption) - 1] 

123 # Below, we check first whether the text ends in a Chinese punctuation 

124 # mark and if it does not, if it ends in a Western one. 

125 if end in ("\u3001\u3002\u3003\u3009\u300b\u300d\u3011\u3015\u3017" 

126 "\u3019\u301b\u301e\ufe16\ufe57\uff01\uff1f\uff61\uff64"): 

127 spacer = "" 

128 elif end not in ".;!?)]}\"'\u00a1\u00b7\u00bf\u037e\u0387": 

129 caption = f"{caption}." 

130 if userepo: 

131 userepo.path.enforce_contains(file) 

132 url = userepo.make_url(file.relative_to(userepo.path)) 

133 caption = f"{caption}{spacer}([src]({url}))" 

134 

135 return (f"Listing: {caption}\n\n" 

136 f"```{{#lst:{label}{plang} .numberLines}}\n{code}\n```") 

137 

138 # create all local code 

139 def __make_absolute_code(label: str, caption: str, path: str, lines: str, 

140 labels: str, args: str) -> str: 

141 nonlocal src 

142 file: Final[Path] = Path.file(path) 

143 src.enforce_contains(file) 

144 code = load_code(file, lines=lines, labels=labels, args=args) 

145 return __make_code(label=label, caption=caption, 

146 code=code, file=file, userepo=repo) 

147 

148 text = (create_preprocessor(name=bc.CMD_ABSOLUTE_CODE, 

149 func=__make_absolute_code, 

150 n=6, strip_white_space=True, 

151 wrap_in_newlines=2))(text) 

152 

153 # create all git code 

154 def __make_git_code(repoid: str, label: str, caption: str, path: str, 

155 lines: str, labels: str, args: str) -> str: 

156 userepo: Final[Repo] = get_repo(repoid) 

157 file: Final[Path] = userepo.path.resolve_inside(path) 

158 file.enforce_file() 

159 userepo.path.enforce_contains(file) 

160 code = load_code(file, lines=lines, labels=labels, args=args) 

161 return __make_code(label=label, caption=caption, 

162 code=code, file=file, userepo=userepo) 

163 

164 return (create_preprocessor(name=bc.CMD_GIT_CODE, 

165 func=__make_git_code, 

166 n=7, strip_white_space=True, 

167 wrap_in_newlines=2))(text)