1 #!/usr/bin/env python3 |
1 #!/usr/bin/env python3 |
2 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
|
3 import sys |
2 import sys |
4 if sys.version_info < (3, 4): |
3 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 |
|
10 raise RuntimeError('Python 3.4 or newer required') |
4 raise RuntimeError('Python 3.4 or newer required') |
11 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
|
12 from colours import load_colours |
5 from colours import load_colours |
13 ======= |
6 |
|
7 try: |
|
8 import colorama |
|
9 except ImportError: |
|
10 colorama = None |
14 |
11 |
15 appname = 'ldcheck' |
12 appname = 'ldcheck' |
16 version = (1, 0, 9999) |
13 version = (1, 0, 9999) |
17 version_string = str.join('.', map(str, version)) |
14 version_string = str.join('.', map(str, version)) |
18 |
|
19 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
|
20 from geometry import * |
15 from geometry import * |
21 from pathlib import Path |
16 from pathlib import Path |
22 import linetypes |
17 import linetypes |
23 import header |
18 import header |
24 import parse |
19 import parse |
25 |
20 |
26 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
|
27 def check_library_paths(library_paths): |
21 def check_library_paths(library_paths): |
28 for library_path in library_paths: |
22 for library_path in library_paths: |
29 if not library_path.exists(): |
23 if not library_path.exists(): |
30 raise RuntimeError(str.format( |
24 raise RuntimeError(str.format( |
31 'error: library path {} does not exist', |
25 'error: library path {} does not exist', |
50 if not ldconfig_ldr_paths: |
44 if not ldconfig_ldr_paths: |
51 raise RuntimeError('could not find any LDConfig.ldr') |
45 raise RuntimeError('could not find any LDConfig.ldr') |
52 for ldconfig_ldr_path in ldconfig_ldr_paths: |
46 for ldconfig_ldr_path in ldconfig_ldr_paths: |
53 with ldconfig_ldr_path.open() as ldconfig_ldr: |
47 with ldconfig_ldr_path.open() as ldconfig_ldr: |
54 load_colours(ldconfig_ldr) |
48 load_colours(ldconfig_ldr) |
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 |
49 |
85 class LDrawContext: |
50 class LDrawContext: |
86 ''' |
51 ''' |
87 Contains context-dependant LDraw information, like library directory |
52 Contains context-dependant LDraw information, like library directory |
88 paths and the colour table. |
53 paths and the colour table. |
170 name = warning_type.name, |
133 name = warning_type.name, |
171 severity = warning_type.severity, |
134 severity = warning_type.severity, |
172 message = warning_type.placeholder_message(), |
135 message = warning_type.placeholder_message(), |
173 )) |
136 )) |
174 sys.exit(0) |
137 sys.exit(0) |
175 ======= |
138 parser = argparse.ArgumentParser() |
176 class ListProblemsAction(argparse.Action): |
139 parser.add_argument('filename') |
177 def __init__(self, option_strings, dest, nargs = None, **kwargs): |
140 parser.add_argument('-l', '--library', action = 'append') |
178 super().__init__(option_strings, dest, nargs = 0, **kwargs) |
141 parser.add_argument('--list', |
179 def __call__(self, *args, **kwargs): |
142 action = ListProblemTypesAction, |
180 import testsuite |
143 help = 'lists all possible problem types and exit', |
181 from sys import exit |
144 ) |
182 from re import sub |
145 parser.add_argument('--dump', |
183 test_suite = testsuite.load_tests() |
146 action = 'store_true', |
184 for warning_type in testsuite.all_problem_types(test_suite): |
147 help = 'dumps the internal parsed structure of the part file', |
185 print(str.format('{name}: {severity}: "{message}"', |
148 ) |
186 name = warning_type.name, |
149 parser.add_argument('--rebuild', |
187 severity = warning_type.severity, |
150 action = 'store_true', |
188 message = warning_type.placeholder_message(), |
151 help = 'parses the part file and prints it back out, used for ' |
189 )) |
152 'testing whether the program interprets part files correctly', |
190 exit(0) |
153 ) |
|
154 parser.add_argument('--subfile', |
|
155 action = 'store_true', |
|
156 help = 'finds a subfile by name and prints out information about it' |
|
157 ) |
|
158 parser.add_argument('--color', |
|
159 action = 'store_true', |
|
160 help = 'use colors' |
|
161 ) |
|
162 parser.add_argument('-d', '--ldraw-dir', |
|
163 nargs = '+', |
|
164 help = 'specify LDraw directory path(s)', |
|
165 ) |
|
166 parser.add_argument('-v', '--version', |
|
167 action = 'version', |
|
168 version = str.format('{appname} {version}', |
|
169 appname = appname, |
|
170 version = version_string, |
|
171 ), |
|
172 ) |
|
173 arglist = rcargs + sys.argv[1:] |
|
174 return parser.parse_args(arglist) |
191 |
175 |
192 def format_report(report, model, test_suite, *, use_colors = True): |
176 def format_report(report, model, test_suite, *, use_colors = True): |
193 from testsuite import problem_text |
177 from testsuite import problem_text |
194 messages = [] |
178 messages = [] |
195 for problem in report['problems']: |
179 for problem in report['problems']: |
212 ldraw_code = ldraw_code, |
196 ldraw_code = ldraw_code, |
213 ) |
197 ) |
214 messages.append(message) |
198 messages.append(message) |
215 return '\n'.join(messages) |
199 return '\n'.join(messages) |
216 |
200 |
217 if __name__ == '__main__': |
|
218 from sys import argv, stderr, exit |
|
219 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
|
220 parser = argparse.ArgumentParser() |
|
221 parser.add_argument('filename') |
|
222 parser.add_argument('--list', |
|
223 action = ListProblemTypesAction, |
|
224 help = 'lists all possible problem types and exit', |
|
225 ) |
|
226 parser.add_argument('--dump', |
|
227 action = 'store_true', |
|
228 help = 'dumps the internal parsed structure of the part file', |
|
229 ) |
|
230 parser.add_argument('--rebuild', |
|
231 action = 'store_true', |
|
232 help = 'parses the part file and prints it back out, used for ' |
|
233 'testing whether the program interprets part files correctly', |
|
234 ) |
|
235 parser.add_argument('--subfile', |
|
236 action = 'store_true', |
|
237 help = 'finds a subfile by name and prints out information about it' |
|
238 ) |
|
239 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
|
240 parser.add_argument('-l', '--library', action = 'append') |
|
241 arglist = rcargs + sys.argv[1:] |
|
242 return parser.parse_args(arglist) |
|
243 |
|
244 def main(): |
201 def main(): |
245 args = parse_commandline_arguments() |
202 args = parse_commandline_arguments() |
246 # Make sure that we have at least one library path specified. |
203 # Make sure that we have at least one library path specified. |
247 if not args.library: |
204 if not args.library: |
248 raise RuntimeError( |
205 raise RuntimeError( |
285 import colorama |
222 import colorama |
286 colorama.init() |
223 colorama.init() |
287 except ImportError: |
224 except ImportError: |
288 print('Use of --color requires the colorama module, disabling colours', file = stderr) |
225 print('Use of --color requires the colorama module, disabling colours', file = stderr) |
289 args.color = False |
226 args.color = False |
290 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
|
291 if args.subfile: |
227 if args.subfile: |
292 # Subfile debug mode: searches for the specified subfile from the LDraw |
228 # Subfile debug mode: searches for the specified subfile from the LDraw |
293 # libraries, opens it as if it was referenced by something and prints |
229 # libraries, opens it as if it was referenced by something and prints |
294 # out all information that is calculated from this subfile. |
230 # out all information that is calculated from this subfile. |
295 import filecache |
231 import filecache |
296 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
|
297 cache = filecache.SubfileCache(ldraw_directories = libraries) |
|
298 ======= |
|
299 cache = filecache.SubfileCache(context = context) |
232 cache = filecache.SubfileCache(context = context) |
300 >>>>>>> /tmp/ldcheck~other.ou_xbg_k.py |
|
301 subfile = cache.prepare_file(args.filename) |
233 subfile = cache.prepare_file(args.filename) |
302 if not subfile.valid: |
234 if not subfile.valid: |
303 print(subfile.problem) |
235 print(subfile.problem) |
304 else: |
236 else: |
305 print('Flat dimensions:', repr(subfile.flatness)) |
237 print('Flat dimensions:', repr(subfile.flatness)) |
306 print('Description:', repr(subfile.description)) |
238 print('Description:', repr(subfile.description)) |
307 print('Contains studs:', repr(subfile.has_studs)) |
239 print('Contains studs:', repr(subfile.has_studs)) |
308 else: |
240 else: |
309 <<<<<<< /home/teemu/dev/ldcheck/ldcheck.py |
|
310 with open(args.filename, 'rb') as file: |
241 with open(args.filename, 'rb') as file: |
311 from os.path import basename |
242 try: |
312 model = parse.read_ldraw( |
243 from os.path import basename |
313 file, |
244 model = parse.read_ldraw( |
314 name = basename(args.filename), |
245 file, |
315 ldraw_directories = libraries) |
246 name = basename(args.filename), |
316 if args.dump: |
247 context = context) |
317 # Dump mode: prints out the structure of the processed LDraw file |
248 if args.dump: |
318 print('header: ' + type(model.header).__name__) |
249 # Dump mode: prints out the structure of the processed LDraw file |
319 for key in sorted(dir(model.header)): |
250 print('header: ' + type(model.header).__name__) |
320 if not key.startswith('__'): |
251 for key in sorted(dir(model.header)): |
321 print('\t' + key + ': ' + repr(getattr(model.header, key))) |
252 if not key.startswith('__'): |
322 for i, entry in enumerate(model.body): |
253 print('\t' + key + ': ' + repr(getattr(model.header, key))) |
323 if model.header.valid and i == model.header_size: |
254 for i, entry in enumerate(model.body): |
324 # Mark where the header is considered to end |
255 if model.header.valid and i == model.header_size: |
325 print('--------- End of header') |
256 # Mark where the header is considered to end |
326 print(entry) |
257 print('--------- End of header') |
327 elif args.rebuild: |
258 print(entry) |
328 # Debug rebuild mode: open the file, parse it and turn it back |
259 elif args.rebuild: |
329 # into LDraw code and write it into stdout. This is used to ensure |
260 # Debug rebuild mode: open the file, parse it and turn it back |
330 # that LDCheck does not miss any information while parsing files. |
261 # into LDraw code and write it into stdout. This is used to ensure |
331 for entry in model.body: |
262 # that LDCheck does not miss any information while parsing files. |
332 print(entry.textual_representation(), end = '\r\n') |
263 for entry in model.body: |
333 else: |
264 print(entry.textual_representation(), end = '\r\n') |
334 # Regular mode |
265 else: |
335 from testsuite import load_tests, check_model, format_report |
266 # Regular mode |
336 # load the test suite |
267 from testsuite import load_tests, check_model |
337 # TODO: maybe add some command line argument to filter tests |
268 # load the test suite |
338 # in case the user wants to run some specific tests only or |
269 # TODO: maybe add some command line argument to filter tests |
339 # possibly leave some test out |
270 # in case the user wants to run some specific tests only or |
340 test_suite = load_tests() |
271 # possibly leave some test out |
341 # use the test suite to check the model |
272 test_suite = load_tests() |
342 report = check_model(model, test_suite) |
273 # use the test suite to check the model |
343 # print out the report |
274 report = check_model(model, test_suite) |
344 print(format_report(report, model, test_suite)) |
275 # print out the report |
|
276 print(format_report( |
|
277 report, |
|
278 model, |
|
279 test_suite, |
|
280 use_colors = args.color |
|
281 )) |
|
282 except FileNotFoundError: |
|
283 print(str.format( |
|
284 'no such file: {filename!r}', |
|
285 filename = args.filename |
|
286 ), file = stderr) |
|
287 exit(1) |
345 |
288 |
346 if __name__ == '__main__': |
289 if __name__ == '__main__': |
347 try: |
290 try: |
348 main() |
291 main() |
349 except RuntimeError as e: |
292 except RuntimeError as e: |
350 import sys |
293 import sys |
351 print('error:', str(e), file = sys.stderr) |
294 print('error:', str(e), file = sys.stderr) |
352 sys.exit(1) |
295 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 |
|