ldcheck.py

Tue, 28 May 2019 19:11:11 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Tue, 28 May 2019 19:11:11 +0300
changeset 51
913ec927c95d
parent 47
4da025d0b283
child 54
0c686d10eb49
permissions
-rwxr-xr-x

added categories.txt

#!/usr/bin/env python3
from sys import version_info
if version_info < (3, 4):
    raise RuntimeError('Python 3.4 or newer required')

from parse import parse_ldraw_code
from colours import load_colours
from geometry import *
from pathlib import Path
import linetypes
import header

from os.path import realpath
script_directory = Path(realpath(__file__)).parent

def load_config(filename):
    from configobj import ConfigObj
    from copy import deepcopy
    config = ConfigObj(filename, encoding = 'UTF8')
    read_config = deepcopy(config)
    if 'libraries' not in config:
        config['libraries'] = ['/path/to/ldraw']
    if config != read_config:
        config.write()
    check_library_paths(config)
    return config

def read_ldraw(file, *, name = '', config):
    model_body = [
        parse_ldraw_code(line)
        for line in file
    ]
    headerparser = header.HeaderParser()
    try:
        header_parse_result = headerparser.parse(model_body)
        header_object = header_parse_result['header']
        end = header_parse_result['end-index']
    except header.HeaderError as error:
        header_object = header.BadHeader(error.index, error.reason)
        end = 0
    model = Model(
        header = header_object,
        body = model_body,
        header_size = end)
    model.name = name
    return model

def library_paths(config):
    for library_path_string in config['libraries']:
        yield Path(library_path_string).expanduser()

def check_library_paths(config):
    from sys import exit
    problems = False
    have_paths = False
    for library_path in library_paths(config):
        have_paths = True
        if not library_path.exists():
            problems = True
            print(str.format(
                'Library path {} does not exist',
                library_path,
            ))
        elif not library_path.exists():
            problems = True
            print(str.format(
                'Library path {} is not a directory',
                library_path,
            ))
    if not have_paths:
        print('No LDraw path specified')
        problems = True
    if problems:
        print('Please fix ldcheck.cfg')
        exit(1)

def find_ldconfig_ldr_paths(config):
    for library_path in library_paths(config):
        yield from [
            library_path / path
            for path in ['LDConfig.ldr', 'ldconfig.ldr']
            if (library_path / path).is_file()
        ]

class Model:
    def __init__(self, header, body, *, header_size = 0):
        self.header = header
        self.body = body
        self.header_size = header_size
    def filter_by_type(self, type):
        yield from [
            element
            for element in self.body
            if isinstance(element, type)
        ]
    @property
    def subfile_references(self):
        yield from self.filter_by_type(linetypes.SubfileReference)
    @property
    def line_segments(self):
        yield from self.filter_by_type(linetypes.LineSegment)
    @property
    def triangles(self):
        yield from self.filter_by_type(linetypes.Triangle)
    @property
    def quadrilaterals(self):
        yield from self.filter_by_type(linetypes.Quadrilateral)
    @property
    def has_header(self):
        return self.header and not isinstance(self.header, header.BadHeader)

import argparse
class ListTestSuiteAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs = None, **kwargs):
        super().__init__(option_strings, dest, nargs = 0, **kwargs)
    def __call__(self, *args, **kwargs):
        from testsuite import load_tests
        from sys import exit
        from re import sub
        test_suite = load_tests()
        for test_name in sorted(test_suite['tests'].keys()):
            test_function = test_suite['tests'][test_name]
            help = sub(r'\s+', ' ', test_function.__doc__ or '').strip()
            print(test_name + ': ' + help)
        exit(0)

if __name__ == '__main__':
    from sys import argv
    parser = argparse.ArgumentParser()
    parser.add_argument('filename')
    parser.add_argument('--list',
        action = ListTestSuiteAction,
        help = 'Lists all possible checks and exit',
    )
    parser.add_argument('--dump-structure', action = 'store_true')
    parser.add_argument('--rebuild', action = 'store_true')
    args = parser.parse_args()
    config = load_config('ldcheck.cfg')
    for ldconfig_ldr_path in find_ldconfig_ldr_paths(config):
        with ldconfig_ldr_path.open() as ldconfig_ldr:
            load_colours(ldconfig_ldr)
    with open(args.filename) as file:
        from os.path import basename
        model = read_ldraw(
            file,
            name = basename(args.filename),
            config = config,
        )
        if args.dump_structure:
            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 entry in model.body:
                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, format_report
            test_suite = load_tests()
            report = check_model(model, test_suite)
            print(format_report(report, model, test_suite))

mercurial