ldcheck.py

changeset 147
bec55b021ae7
parent 146
3555679d276b
parent 145
fde18c4d6784
child 149
7c01f9876b69
--- 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

mercurial