1 #!/usr/bin/env python3 |
1 #!/usr/bin/env python3 |
|
2 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
2 import sys |
3 import sys |
3 if sys.version_info < (3, 4): |
4 if sys.version_info < (3, 4): |
|
5 ======= |
|
6 import argparse |
|
7 from sys import version_info |
|
8 if version_info < (3, 4): |
|
9 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
4 raise RuntimeError('Python 3.4 or newer required') |
10 raise RuntimeError('Python 3.4 or newer required') |
|
11 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
5 from colours import load_colours |
12 from colours import load_colours |
|
13 ======= |
|
14 |
|
15 appname = 'ldcheck' |
|
16 version = (1, 0, 9999) |
|
17 version_string = str.join('.', map(str, version)) |
|
18 |
|
19 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
6 from geometry import * |
20 from geometry import * |
7 from pathlib import Path |
21 from pathlib import Path |
8 import linetypes |
22 import linetypes |
9 import header |
23 import header |
10 import parse |
24 import parse |
11 |
25 |
|
26 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
12 def check_library_paths(library_paths): |
27 def check_library_paths(library_paths): |
13 for library_path in library_paths: |
28 for library_path in library_paths: |
14 if not library_path.exists(): |
29 if not library_path.exists(): |
15 raise RuntimeError(str.format( |
30 raise RuntimeError(str.format( |
16 'error: library path {} does not exist', |
31 'error: library path {} does not exist', |
35 if not ldconfig_ldr_paths: |
50 if not ldconfig_ldr_paths: |
36 raise RuntimeError('could not find any LDConfig.ldr') |
51 raise RuntimeError('could not find any LDConfig.ldr') |
37 for ldconfig_ldr_path in ldconfig_ldr_paths: |
52 for ldconfig_ldr_path in ldconfig_ldr_paths: |
38 with ldconfig_ldr_path.open() as ldconfig_ldr: |
53 with ldconfig_ldr_path.open() as ldconfig_ldr: |
39 load_colours(ldconfig_ldr) |
54 load_colours(ldconfig_ldr) |
40 |
55 ======= |
|
56 from os.path import realpath |
|
57 script_directory = Path(realpath(__file__)).parent |
|
58 |
|
59 def config_dirs(): |
|
60 import appdirs |
|
61 appauthor = 'Teemu Piippo' |
|
62 dirs = appdirs.AppDirs(appname, appauthor) |
|
63 return { |
|
64 'user': Path(dirs.user_config_dir), |
|
65 'system': Path(dirs.site_config_dir), |
|
66 } |
|
67 |
|
68 def ldraw_dirs_from_config(): |
|
69 libraries = [] |
|
70 dirs = config_dirs() |
|
71 for dirpath in [dirs['system'], dirs['user']]: |
|
72 filename = dirpath / 'ldcheck.cfg' |
|
73 from configobj import ConfigObj |
|
74 config = ConfigObj(str(filename), encoding = 'UTF8') |
|
75 if 'libraries' in config: |
|
76 libraries = expand_paths(config['libraries']) |
|
77 return libraries |
|
78 |
|
79 def expand_paths(paths): |
|
80 return [ |
|
81 Path(library).expanduser() |
|
82 for library in paths |
|
83 ] |
|
84 |
|
85 class LDrawContext: |
|
86 ''' |
|
87 Contains context-dependant LDraw information, like library directory |
|
88 paths and the colour table. |
|
89 ''' |
|
90 def __init__(self, libraries = None): |
|
91 self._libraries = libraries |
|
92 if not self._libraries: |
|
93 self._libraries = ldraw_dirs_from_config() |
|
94 self.ldconfig_colour_data = self.load_ldconfig_ldr() |
|
95 self.check_library_paths() |
|
96 @property |
|
97 def libraries(self): |
|
98 return self._libraries |
|
99 @property |
|
100 def colours(self): |
|
101 return self.ldconfig_colour_data |
|
102 def ldconfig_ldr_discovery(self): |
|
103 for library_path in self.libraries: |
|
104 yield from [ |
|
105 library_path / path |
|
106 for path in ['LDConfig.ldr', 'ldconfig.ldr'] |
|
107 if (library_path / path).is_file() |
|
108 ] |
|
109 def load_ldconfig_ldr(self): |
|
110 from colours import load_colours |
|
111 for ldconfig_ldr_path in self.ldconfig_ldr_discovery(): |
|
112 with open(ldconfig_ldr_path) as ldconfig_ldr: |
|
113 return load_colours(ldconfig_ldr) |
|
114 def check_library_paths(self): |
|
115 from sys import stderr |
|
116 problems = False |
|
117 have_paths = False |
|
118 for library_path in self.libraries: |
|
119 have_paths = True |
|
120 if not library_path.exists(): |
|
121 problems = True |
|
122 print(str.format( |
|
123 'Library path {} does not exist', |
|
124 library_path, |
|
125 ), file = stderr) |
|
126 elif not library_path.exists(): |
|
127 problems = True |
|
128 print(str.format( |
|
129 'Library path {} is not a directory', |
|
130 library_path, |
|
131 ), file = stderr) |
|
132 if not have_paths: |
|
133 raise RuntimeError('no LDraw library paths specified') |
|
134 def is_ldconfig_colour(self, colour): |
|
135 return colour.index in self.colours |
|
136 def colour_name(self, colour): |
|
137 if self.is_ldconfig_colour(colour): |
|
138 return self.colours[self.index]['name'] |
|
139 else: |
|
140 return str(colour) |
|
141 def colour_face(self, colour): |
|
142 if self.is_ldconfig_colour(colour): |
|
143 return self.colours[self.index]['value'] |
|
144 elif colour.is_direct_colour: |
|
145 return '#%06X' % (self.index & 0xffffff) |
|
146 else: |
|
147 return '#000000' |
|
148 def is_valid_colour(self, colour): |
|
149 return self.is_ldconfig_colour(colour) or colour.is_direct_colour |
|
150 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
|
151 |
|
152 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
41 def parse_commandline_arguments(): |
153 def parse_commandline_arguments(): |
42 import os |
154 import os |
43 rcpath = Path(os.path.expanduser('~/.config/ldcheckrc')) |
155 rcpath = Path(os.path.expanduser('~/.config/ldcheckrc')) |
44 if rcpath.exists(): |
156 if rcpath.exists(): |
45 with rcpath.open() as file: |
157 with rcpath.open() as file: |
58 name = warning_type.name, |
170 name = warning_type.name, |
59 severity = warning_type.severity, |
171 severity = warning_type.severity, |
60 message = warning_type.placeholder_message(), |
172 message = warning_type.placeholder_message(), |
61 )) |
173 )) |
62 sys.exit(0) |
174 sys.exit(0) |
|
175 ======= |
|
176 class ListProblemsAction(argparse.Action): |
|
177 def __init__(self, option_strings, dest, nargs = None, **kwargs): |
|
178 super().__init__(option_strings, dest, nargs = 0, **kwargs) |
|
179 def __call__(self, *args, **kwargs): |
|
180 import testsuite |
|
181 from sys import exit |
|
182 from re import sub |
|
183 test_suite = testsuite.load_tests() |
|
184 for warning_type in testsuite.all_problem_types(test_suite): |
|
185 print(str.format('{name}: {severity}: "{message}"', |
|
186 name = warning_type.name, |
|
187 severity = warning_type.severity, |
|
188 message = warning_type.placeholder_message(), |
|
189 )) |
|
190 exit(0) |
|
191 |
|
192 def format_report(report, model, test_suite, *, use_colors = True): |
|
193 from testsuite import problem_text |
|
194 messages = [] |
|
195 for problem in report['problems']: |
|
196 text_colour = '' |
|
197 if use_colors: |
|
198 if problem.severity == 'hold': |
|
199 text_colour = colorama.Fore.LIGHTRED_EX |
|
200 elif problem.severity == 'warning': |
|
201 text_colour = colorama.Fore.LIGHTBLUE_EX |
|
202 ldraw_code = model.body[problem.body_index].textual_representation() |
|
203 message = str.format( |
|
204 '{text_colour}{model_name}:{line_number}: {problem_type}: {message}' |
|
205 '{colour_reset}\n\t{ldraw_code}', |
|
206 text_colour = text_colour, |
|
207 model_name = model.name, |
|
208 line_number = problem.line_number, |
|
209 problem_type = problem.severity, |
|
210 message = problem_text(problem, test_suite), |
|
211 colour_reset = use_colors and colorama.Fore.RESET or '', |
|
212 ldraw_code = ldraw_code, |
|
213 ) |
|
214 messages.append(message) |
|
215 return '\n'.join(messages) |
|
216 |
|
217 if __name__ == '__main__': |
|
218 from sys import argv, stderr, exit |
|
219 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
63 parser = argparse.ArgumentParser() |
220 parser = argparse.ArgumentParser() |
64 parser.add_argument('filename') |
221 parser.add_argument('filename') |
65 parser.add_argument('--list', |
222 parser.add_argument('--list', |
66 action = ListProblemTypesAction, |
223 action = ListProblemTypesAction, |
67 help = 'lists all possible problem types and exit', |
224 help = 'lists all possible problem types and exit', |
77 ) |
234 ) |
78 parser.add_argument('--subfile', |
235 parser.add_argument('--subfile', |
79 action = 'store_true', |
236 action = 'store_true', |
80 help = 'finds a subfile by name and prints out information about it' |
237 help = 'finds a subfile by name and prints out information about it' |
81 ) |
238 ) |
|
239 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
82 parser.add_argument('-l', '--library', action = 'append') |
240 parser.add_argument('-l', '--library', action = 'append') |
83 arglist = rcargs + sys.argv[1:] |
241 arglist = rcargs + sys.argv[1:] |
84 return parser.parse_args(arglist) |
242 return parser.parse_args(arglist) |
85 |
243 |
86 def main(): |
244 def main(): |
95 # directory |
253 # directory |
96 import os |
254 import os |
97 libraries = [Path(os.path.expanduser(library)) for library in args.library] |
255 libraries = [Path(os.path.expanduser(library)) for library in args.library] |
98 check_library_paths(libraries) |
256 check_library_paths(libraries) |
99 load_ldconfig(libraries) |
257 load_ldconfig(libraries) |
|
258 ======= |
|
259 parser.add_argument('--color', |
|
260 action = 'store_true', |
|
261 help = 'use colors' |
|
262 ) |
|
263 parser.add_argument('-d', '--ldraw-dir', |
|
264 nargs = '+', |
|
265 help = 'specify LDraw directory path(s)', |
|
266 ) |
|
267 parser.add_argument('-v', '--version', |
|
268 action = 'version', |
|
269 version = str.format('{appname} {version}', |
|
270 appname = appname, |
|
271 version = version_string, |
|
272 ), |
|
273 ) |
|
274 args = parser.parse_args() |
|
275 libraries = ldraw_dirs_from_config() |
|
276 if args.ldraw_dir: |
|
277 libraries = expand_paths(args.ldraw_dir) |
|
278 try: |
|
279 context = LDrawContext(libraries) |
|
280 except RuntimeError as error: |
|
281 print('error:', str(error), file = stderr) |
|
282 exit(1) |
|
283 if args.color: |
|
284 try: |
|
285 import colorama |
|
286 colorama.init() |
|
287 except ImportError: |
|
288 print('Use of --color requires the colorama module, disabling colours', file = stderr) |
|
289 args.color = False |
|
290 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
100 if args.subfile: |
291 if args.subfile: |
101 # Subfile debug mode: searches for the specified subfile from the LDraw |
292 # Subfile debug mode: searches for the specified subfile from the LDraw |
102 # libraries, opens it as if it was referenced by something and prints |
293 # libraries, opens it as if it was referenced by something and prints |
103 # out all information that is calculated from this subfile. |
294 # out all information that is calculated from this subfile. |
104 import filecache |
295 import filecache |
|
296 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
105 cache = filecache.SubfileCache(ldraw_directories = libraries) |
297 cache = filecache.SubfileCache(ldraw_directories = libraries) |
|
298 ======= |
|
299 cache = filecache.SubfileCache(context = context) |
|
300 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
106 subfile = cache.prepare_file(args.filename) |
301 subfile = cache.prepare_file(args.filename) |
107 if not subfile.valid: |
302 if not subfile.valid: |
108 print(subfile.problem) |
303 print(subfile.problem) |
109 else: |
304 else: |
110 print('Flat dimensions:', repr(subfile.flatness)) |
305 print('Flat dimensions:', repr(subfile.flatness)) |
111 print('Description:', repr(subfile.description)) |
306 print('Description:', repr(subfile.description)) |
112 print('Contains studs:', repr(subfile.has_studs)) |
307 print('Contains studs:', repr(subfile.has_studs)) |
113 else: |
308 else: |
|
309 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
114 with open(args.filename, 'rb') as file: |
310 with open(args.filename, 'rb') as file: |
115 from os.path import basename |
311 from os.path import basename |
116 model = parse.read_ldraw( |
312 model = parse.read_ldraw( |
117 file, |
313 file, |
118 name = basename(args.filename), |
314 name = basename(args.filename), |
152 main() |
348 main() |
153 except RuntimeError as e: |
349 except RuntimeError as e: |
154 import sys |
350 import sys |
155 print('error:', str(e), file = sys.stderr) |
351 print('error:', str(e), file = sys.stderr) |
156 sys.exit(1) |
352 sys.exit(1) |
|
353 ======= |
|
354 try: |
|
355 with open(args.filename, 'rb') as file: |
|
356 from os.path import basename |
|
357 model = parse.read_ldraw( |
|
358 file, |
|
359 name = basename(args.filename), |
|
360 context = context) |
|
361 if args.dump: |
|
362 print('header: ' + type(model.header).__name__) |
|
363 for key in sorted(dir(model.header)): |
|
364 if not key.startswith('__'): |
|
365 print('\t' + key + ': ' + repr(getattr(model.header, key))) |
|
366 for i, entry in enumerate(model.body): |
|
367 if model.header.valid and i == model.header_size: |
|
368 print('--------- End of header') |
|
369 print(entry) |
|
370 elif args.rebuild: |
|
371 for entry in model.body: |
|
372 print(entry.textual_representation(), end = '\r\n') |
|
373 else: |
|
374 from testsuite import load_tests, check_model |
|
375 test_suite = load_tests() |
|
376 report = check_model(model, test_suite) |
|
377 print(format_report( |
|
378 report, |
|
379 model, |
|
380 test_suite, |
|
381 use_colors = args.color |
|
382 )) |
|
383 except FileNotFoundError: |
|
384 print(str.format( |
|
385 'no such file: {filename!r}', |
|
386 filename = args.filename |
|
387 ), file = stderr) |
|
388 exit(1) |
|
389 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |