from __future__ import print_function import _ast import ast import os import os.path import sys import pycommand def get_setting_specs(module): sys.path.insert(0, os.getcwd()) return __import__(module) def write_settings(specs, out, values=None): values = values or {} print('# {file} file generated by topple' .format(file=specs.OUTPUT_FILE), file=out) for key, spec in specs.SETTINGS: value = values.get(key, spec['initial']) if (spec['predicate'](value)): print(key, repr(value), sep=' = ', file=out) else: raise RuntimeError('Invalid type for %s: %s' % (key, repr(value))) class PreviewMixin(object): optionList = ( ('preview', ('p', None, 'Print generated file to stdout')), ) def __new__(cls, *args, **kwargs): cls.optionList = (cls.optionList or tuple()) + PreviewMixin.optionList instance = super(PreviewMixin, cls).__new__(cls, *args, **kwargs) return instance def write(self, output, specs, values=None): preview = self.flags['preview'] if not preview: with open(output, 'w') as f: write_settings(specs, f, values) else: write_settings(specs, sys.stdout, values) class Generate(pycommand.CommandBase, PreviewMixin): usagestr = 'usage: topple generate [--preview|-p]' def run(self): sys.path.insert(0, os.getcwd()) specs = get_setting_specs(self.parentFlags['module'] or 'toppler') self.write(specs.OUTPUT_FILE, specs) class Update(pycommand.CommandBase, PreviewMixin): usagestr = 'usage: topple update [--preview|-p]' def _get_value(self, node): if isinstance(node, _ast.Name): return eval(node.id) elif isinstance(node, _ast.Str): return node.s elif isinstance(node, _ast.Num): return node.n else: return node def _get_values(self, filename): res = {} with open(filename, 'r') as f: tree = ast.parse('\n'.join(f.readlines()), filename) for node in ast.iter_child_nodes(tree): if isinstance(node, _ast.Assign): res[node.targets[0].id] = self._get_value(node.value) else: print(node, node._fields) return res def run(self): sys.path.insert(0, os.getcwd()) filename = 'settings_local.py' res = self._get_values(filename) specs = get_setting_specs(self.parentFlags['module'] or 'toppler') self.write(specs.OUTPUT_FILE, specs, res) class Topple(pycommand.CommandBase): usagestr = 'usage: topple [--module=modulename|-m modulename] generate' description = ( 'Commands:\n' ' generate Generate a new settings file.\n' ' update Update an existing settings file.\n' ) optionList = (('module', ('m', '', 'Use specified python module.')),) def run(self): module = self.flags['module'] or 'toppler' if not os.path.exists(module + '.py'): print('No {module}.py module found!'.format(module=module)) return 1 if not self.args: print(self.usage) return 2 if self.args[0] == 'generate': cmd = Generate(argv=self.args[1:]) elif self.args[0] == 'update': cmd = Update(argv=self.args[1:]) else: print('Undefined command: {cmd}'.format(cmd=self.args[0])) return 1 cmd.registerParentFlag('module', self.flags['module']) if cmd.error: print('Error during {cmd}: {err}'.format(cmd=self.args[0], err=cmd.error)) return 1 else: return cmd.run()