ldcheck.py

changeset 145
fde18c4d6784
parent 122
116a81996832
child 147
bec55b021ae7
equal deleted inserted replaced
144:eb4c767522ac 145:fde18c4d6784
1 #!/usr/bin/env python3 1 #!/usr/bin/env python3
2 import argparse
2 from sys import version_info 3 from sys import version_info
3 if version_info < (3, 4): 4 if version_info < (3, 4):
4 raise RuntimeError('Python 3.4 or newer required') 5 raise RuntimeError('Python 3.4 or newer required')
5 6
6 appname = 'ldcheck' 7 appname = 'ldcheck'
7 version = (1, 0, 9999) 8 version = (1, 0, 9999)
8 version_string = str.join('.', map(str, version)) 9 version_string = str.join('.', map(str, version))
9 10
10 from colours import load_colours
11 from geometry import * 11 from geometry import *
12 from pathlib import Path 12 from pathlib import Path
13 import linetypes 13 import linetypes
14 import header 14 import header
15 import parse 15 import parse
16 16
17 from os.path import realpath 17 from os.path import realpath
18 script_directory = Path(realpath(__file__)).parent 18 script_directory = Path(realpath(__file__)).parent
19 19
20 def load_config(filename = None): 20 def config_dirs():
21 if filename is None: 21 import appdirs
22 filename = script_directory / 'ldcheck.cfg' 22 appauthor = 'Teemu Piippo'
23 from configobj import ConfigObj 23 dirs = appdirs.AppDirs(appname, appauthor)
24 from copy import deepcopy 24 return {
25 config = ConfigObj(str(filename), encoding = 'UTF8') 25 'user': Path(dirs.user_config_dir),
26 read_config = deepcopy(config) 26 'system': Path(dirs.site_config_dir),
27 if 'libraries' not in config: 27 }
28 config['libraries'] = ['/path/to/ldraw'] 28
29 if config != read_config: 29 def ldraw_dirs_from_config():
30 config.write() 30 libraries = []
31 check_library_paths(config) 31 dirs = config_dirs()
32 load_ldconfig_ldr(config) 32 for dirpath in [dirs['system'], dirs['user']]:
33 return config 33 filename = dirpath / 'ldcheck.cfg'
34 34 from configobj import ConfigObj
35 def library_paths(config): 35 config = ConfigObj(str(filename), encoding = 'UTF8')
36 for library_path_string in config['libraries']: 36 if 'libraries' in config:
37 yield Path(library_path_string).expanduser() 37 libraries = expand_paths(config['libraries'])
38 38 return libraries
39 def check_library_paths(config): 39
40 from sys import exit 40 def expand_paths(paths):
41 problems = False 41 return [
42 have_paths = False 42 Path(library).expanduser()
43 for library_path in library_paths(config): 43 for library in paths
44 have_paths = True 44 ]
45 if not library_path.exists(): 45
46 problems = True 46 class LDrawContext:
47 print(str.format( 47 '''
48 'Library path {} does not exist', 48 Contains context-dependant LDraw information, like library directory
49 library_path, 49 paths and the colour table.
50 )) 50 '''
51 elif not library_path.exists(): 51 def __init__(self, libraries = None):
52 problems = True 52 self._libraries = libraries
53 print(str.format( 53 if not self._libraries:
54 'Library path {} is not a directory', 54 self._libraries = ldraw_dirs_from_config()
55 library_path, 55 self.ldconfig_colour_data = self.load_ldconfig_ldr()
56 )) 56 self.check_library_paths()
57 if not have_paths: 57 @property
58 print('No LDraw path specified') 58 def libraries(self):
59 problems = True 59 return self._libraries
60 if problems: 60 @property
61 print('Please fix ldcheck.cfg') 61 def colours(self):
62 exit(1) 62 return self.ldconfig_colour_data
63 63 def ldconfig_ldr_discovery(self):
64 def find_ldconfig_ldr_paths(config): 64 for library_path in self.libraries:
65 for library_path in library_paths(config): 65 yield from [
66 yield from [ 66 library_path / path
67 library_path / path 67 for path in ['LDConfig.ldr', 'ldconfig.ldr']
68 for path in ['LDConfig.ldr', 'ldconfig.ldr'] 68 if (library_path / path).is_file()
69 if (library_path / path).is_file() 69 ]
70 ] 70 def load_ldconfig_ldr(self):
71 71 from colours import load_colours
72 import argparse 72 for ldconfig_ldr_path in self.ldconfig_ldr_discovery():
73 with open(ldconfig_ldr_path) as ldconfig_ldr:
74 return load_colours(ldconfig_ldr)
75 def check_library_paths(self):
76 from sys import stderr
77 problems = False
78 have_paths = False
79 for library_path in self.libraries:
80 have_paths = True
81 if not library_path.exists():
82 problems = True
83 print(str.format(
84 'Library path {} does not exist',
85 library_path,
86 ), file = stderr)
87 elif not library_path.exists():
88 problems = True
89 print(str.format(
90 'Library path {} is not a directory',
91 library_path,
92 ), file = stderr)
93 if not have_paths:
94 raise RuntimeError('no LDraw library paths specified')
95 def is_ldconfig_colour(self, colour):
96 return colour.index in self.colours
97 def colour_name(self, colour):
98 if self.is_ldconfig_colour(colour):
99 return self.colours[self.index]['name']
100 else:
101 return str(colour)
102 def colour_face(self, colour):
103 if self.is_ldconfig_colour(colour):
104 return self.colours[self.index]['value']
105 elif colour.is_direct_colour:
106 return '#%06X' % (self.index & 0xffffff)
107 else:
108 return '#000000'
109 def is_valid_colour(self, colour):
110 return self.is_ldconfig_colour(colour) or colour.is_direct_colour
73 111
74 class ListProblemsAction(argparse.Action): 112 class ListProblemsAction(argparse.Action):
75 def __init__(self, option_strings, dest, nargs = None, **kwargs): 113 def __init__(self, option_strings, dest, nargs = None, **kwargs):
76 super().__init__(option_strings, dest, nargs = 0, **kwargs) 114 super().__init__(option_strings, dest, nargs = 0, **kwargs)
77 def __call__(self, *args, **kwargs): 115 def __call__(self, *args, **kwargs):
84 name = warning_type.name, 122 name = warning_type.name,
85 severity = warning_type.severity, 123 severity = warning_type.severity,
86 message = warning_type.placeholder_message(), 124 message = warning_type.placeholder_message(),
87 )) 125 ))
88 exit(0) 126 exit(0)
89
90 def load_ldconfig_ldr(config):
91 for ldconfig_ldr_path in find_ldconfig_ldr_paths(config):
92 with ldconfig_ldr_path.open() as ldconfig_ldr:
93 load_colours(ldconfig_ldr)
94 127
95 def format_report(report, model, test_suite, *, use_colors = True): 128 def format_report(report, model, test_suite, *, use_colors = True):
96 from testsuite import problem_text 129 from testsuite import problem_text
97 messages = [] 130 messages = []
98 for problem in report['problems']: 131 for problem in report['problems']:
140 ) 173 )
141 parser.add_argument('--color', 174 parser.add_argument('--color',
142 action = 'store_true', 175 action = 'store_true',
143 help = 'use colors' 176 help = 'use colors'
144 ) 177 )
178 parser.add_argument('-d', '--ldraw-dir',
179 nargs = '+',
180 help = 'specify LDraw directory path(s)',
181 )
145 parser.add_argument('-v', '--version', 182 parser.add_argument('-v', '--version',
146 action = 'version', 183 action = 'version',
147 version = str.format('{appname} {version}', 184 version = str.format('{appname} {version}',
148 appname = appname, 185 appname = appname,
149 version = version_string, 186 version = version_string,
150 ), 187 ),
151 ) 188 )
152 args = parser.parse_args() 189 args = parser.parse_args()
153 config = load_config() 190 libraries = ldraw_dirs_from_config()
191 if args.ldraw_dir:
192 libraries = expand_paths(args.ldraw_dir)
193 try:
194 context = LDrawContext(libraries)
195 except RuntimeError as error:
196 print('error:', str(error), file = stderr)
197 exit(1)
154 if args.color: 198 if args.color:
155 try: 199 try:
156 import colorama 200 import colorama
157 colorama.init() 201 colorama.init()
158 except ImportError: 202 except ImportError:
159 print('Use of --color requires the colorama module, disabling colours', file = stderr) 203 print('Use of --color requires the colorama module, disabling colours', file = stderr)
160 args.color = False 204 args.color = False
161 if args.subfile: 205 if args.subfile:
162 import filecache 206 import filecache
163 cache = filecache.SubfileCache( 207 cache = filecache.SubfileCache(context = context)
164 ldraw_directories = config['libraries'],
165 )
166 subfile = cache.prepare_file(args.filename) 208 subfile = cache.prepare_file(args.filename)
167 if not subfile.valid: 209 if not subfile.valid:
168 print(subfile.problem) 210 print(subfile.problem)
169 else: 211 else:
170 print('Flat dimensions:', repr(subfile.flatness)) 212 print('Flat dimensions:', repr(subfile.flatness))
175 with open(args.filename, 'rb') as file: 217 with open(args.filename, 'rb') as file:
176 from os.path import basename 218 from os.path import basename
177 model = parse.read_ldraw( 219 model = parse.read_ldraw(
178 file, 220 file,
179 name = basename(args.filename), 221 name = basename(args.filename),
180 ldraw_directories = config['libraries']) 222 context = context)
181 if args.dump: 223 if args.dump:
182 print('header: ' + type(model.header).__name__) 224 print('header: ' + type(model.header).__name__)
183 for key in sorted(dir(model.header)): 225 for key in sorted(dir(model.header)):
184 if not key.startswith('__'): 226 if not key.startswith('__'):
185 print('\t' + key + ': ' + repr(getattr(model.header, key))) 227 print('\t' + key + ': ' + repr(getattr(model.header, key)))

mercurial