diff -r 3555679d276b -r bec55b021ae7 ldcheck.py --- a/ldcheck.py Thu Aug 26 19:16:25 2021 +0300 +++ b/ldcheck.py Thu Aug 26 19:36:44 2021 +0300 @@ -1,14 +1,29 @@ #!/usr/bin/env python3 +<<<<<<< /home/teemu/dev/ldcheck/ldcheck.py import sys if sys.version_info < (3, 4): +======= +import argparse +from sys import version_info +if version_info < (3, 4): +>>>>>>> /tmp/ldcheck~other.ou_xbg_k.py raise RuntimeError('Python 3.4 or newer required') +<<<<<<< /home/teemu/dev/ldcheck/ldcheck.py from colours import load_colours +======= + +appname = 'ldcheck' +version = (1, 0, 9999) +version_string = str.join('.', map(str, version)) + +>>>>>>> /tmp/ldcheck~other.ou_xbg_k.py from geometry import * from pathlib import Path import linetypes import header import parse +<<<<<<< /home/teemu/dev/ldcheck/ldcheck.py def check_library_paths(library_paths): for library_path in library_paths: if not library_path.exists(): @@ -37,7 +52,104 @@ for ldconfig_ldr_path in ldconfig_ldr_paths: with ldconfig_ldr_path.open() as ldconfig_ldr: load_colours(ldconfig_ldr) +======= +from os.path import realpath +script_directory = Path(realpath(__file__)).parent +def config_dirs(): + import appdirs + appauthor = 'Teemu Piippo' + dirs = appdirs.AppDirs(appname, appauthor) + return { + 'user': Path(dirs.user_config_dir), + 'system': Path(dirs.site_config_dir), + } + +def ldraw_dirs_from_config(): + libraries = [] + dirs = config_dirs() + for dirpath in [dirs['system'], dirs['user']]: + filename = dirpath / 'ldcheck.cfg' + from configobj import ConfigObj + config = ConfigObj(str(filename), encoding = 'UTF8') + if 'libraries' in config: + libraries = expand_paths(config['libraries']) + return libraries + +def expand_paths(paths): + return [ + Path(library).expanduser() + for library in paths + ] + +class LDrawContext: + ''' + Contains context-dependant LDraw information, like library directory + paths and the colour table. + ''' + def __init__(self, libraries = None): + self._libraries = libraries + if not self._libraries: + self._libraries = ldraw_dirs_from_config() + self.ldconfig_colour_data = self.load_ldconfig_ldr() + self.check_library_paths() + @property + def libraries(self): + return self._libraries + @property + def colours(self): + return self.ldconfig_colour_data + def ldconfig_ldr_discovery(self): + for library_path in self.libraries: + yield from [ + library_path / path + for path in ['LDConfig.ldr', 'ldconfig.ldr'] + if (library_path / path).is_file() + ] + def load_ldconfig_ldr(self): + from colours import load_colours + for ldconfig_ldr_path in self.ldconfig_ldr_discovery(): + with open(ldconfig_ldr_path) as ldconfig_ldr: + return load_colours(ldconfig_ldr) + def check_library_paths(self): + from sys import stderr + problems = False + have_paths = False + for library_path in self.libraries: + have_paths = True + if not library_path.exists(): + problems = True + print(str.format( + 'Library path {} does not exist', + library_path, + ), file = stderr) + elif not library_path.exists(): + problems = True + print(str.format( + 'Library path {} is not a directory', + library_path, + ), file = stderr) + if not have_paths: + raise RuntimeError('no LDraw library paths specified') + def is_ldconfig_colour(self, colour): + return colour.index in self.colours + def colour_name(self, colour): + if self.is_ldconfig_colour(colour): + return self.colours[self.index]['name'] + else: + return str(colour) + def colour_face(self, colour): + if self.is_ldconfig_colour(colour): + return self.colours[self.index]['value'] + elif colour.is_direct_colour: + return '#%06X' % (self.index & 0xffffff) + else: + return '#000000' + def is_valid_colour(self, colour): + return self.is_ldconfig_colour(colour) or colour.is_direct_colour +>>>>>>> /tmp/ldcheck~other.ou_xbg_k.py + +<<<<<<< /home/teemu/dev/ldcheck/ldcheck.py def parse_commandline_arguments(): import os rcpath = Path(os.path.expanduser('~/.config/ldcheckrc')) @@ -60,6 +172,51 @@ message = warning_type.placeholder_message(), )) sys.exit(0) +======= +class ListProblemsAction(argparse.Action): + def __init__(self, option_strings, dest, nargs = None, **kwargs): + super().__init__(option_strings, dest, nargs = 0, **kwargs) + def __call__(self, *args, **kwargs): + import testsuite + from sys import exit + from re import sub + test_suite = testsuite.load_tests() + for warning_type in testsuite.all_problem_types(test_suite): + print(str.format('{name}: {severity}: "{message}"', + name = warning_type.name, + severity = warning_type.severity, + message = warning_type.placeholder_message(), + )) + exit(0) + +def format_report(report, model, test_suite, *, use_colors = True): + from testsuite import problem_text + messages = [] + for problem in report['problems']: + text_colour = '' + if use_colors: + if problem.severity == 'hold': + text_colour = colorama.Fore.LIGHTRED_EX + elif problem.severity == 'warning': + text_colour = colorama.Fore.LIGHTBLUE_EX + ldraw_code = model.body[problem.body_index].textual_representation() + message = str.format( + '{text_colour}{model_name}:{line_number}: {problem_type}: {message}' + '{colour_reset}\n\t{ldraw_code}', + text_colour = text_colour, + model_name = model.name, + line_number = problem.line_number, + problem_type = problem.severity, + message = problem_text(problem, test_suite), + colour_reset = use_colors and colorama.Fore.RESET or '', + ldraw_code = ldraw_code, + ) + messages.append(message) + return '\n'.join(messages) + +if __name__ == '__main__': + from sys import argv, stderr, exit +>>>>>>> /tmp/ldcheck~other.ou_xbg_k.py parser = argparse.ArgumentParser() parser.add_argument('filename') parser.add_argument('--list', @@ -79,6 +236,7 @@ action = 'store_true', help = 'finds a subfile by name and prints out information about it' ) +<<<<<<< /home/teemu/dev/ldcheck/ldcheck.py parser.add_argument('-l', '--library', action = 'append') arglist = rcargs + sys.argv[1:] return parser.parse_args(arglist) @@ -97,12 +255,49 @@ libraries = [Path(os.path.expanduser(library)) for library in args.library] check_library_paths(libraries) load_ldconfig(libraries) +======= + parser.add_argument('--color', + action = 'store_true', + help = 'use colors' + ) + parser.add_argument('-d', '--ldraw-dir', + nargs = '+', + help = 'specify LDraw directory path(s)', + ) + parser.add_argument('-v', '--version', + action = 'version', + version = str.format('{appname} {version}', + appname = appname, + version = version_string, + ), + ) + args = parser.parse_args() + libraries = ldraw_dirs_from_config() + if args.ldraw_dir: + libraries = expand_paths(args.ldraw_dir) + try: + context = LDrawContext(libraries) + except RuntimeError as error: + print('error:', str(error), file = stderr) + exit(1) + if args.color: + try: + import colorama + colorama.init() + except ImportError: + print('Use of --color requires the colorama module, disabling colours', file = stderr) + args.color = False +>>>>>>> /tmp/ldcheck~other.ou_xbg_k.py if args.subfile: # Subfile debug mode: searches for the specified subfile from the LDraw # libraries, opens it as if it was referenced by something and prints # out all information that is calculated from this subfile. import filecache +<<<<<<< /home/teemu/dev/ldcheck/ldcheck.py cache = filecache.SubfileCache(ldraw_directories = libraries) +======= + cache = filecache.SubfileCache(context = context) +>>>>>>> /tmp/ldcheck~other.ou_xbg_k.py subfile = cache.prepare_file(args.filename) if not subfile.valid: print(subfile.problem) @@ -111,6 +306,7 @@ print('Description:', repr(subfile.description)) print('Contains studs:', repr(subfile.has_studs)) else: +<<<<<<< /home/teemu/dev/ldcheck/ldcheck.py with open(args.filename, 'rb') as file: from os.path import basename model = parse.read_ldraw( @@ -154,3 +350,40 @@ import sys print('error:', str(e), file = sys.stderr) sys.exit(1) +======= + try: + with open(args.filename, 'rb') as file: + from os.path import basename + model = parse.read_ldraw( + file, + name = basename(args.filename), + context = context) + if args.dump: + print('header: ' + type(model.header).__name__) + for key in sorted(dir(model.header)): + if not key.startswith('__'): + print('\t' + key + ': ' + repr(getattr(model.header, key))) + for i, entry in enumerate(model.body): + if model.header.valid and i == model.header_size: + print('--------- End of header') + print(entry) + elif args.rebuild: + for entry in model.body: + print(entry.textual_representation(), end = '\r\n') + else: + from testsuite import load_tests, check_model + test_suite = load_tests() + report = check_model(model, test_suite) + print(format_report( + report, + model, + test_suite, + use_colors = args.color + )) + except FileNotFoundError: + print(str.format( + 'no such file: {filename!r}', + filename = args.filename + ), file = stderr) + exit(1) +>>>>>>> /tmp/ldcheck~other.ou_xbg_k.py