def run(self): if sphinx is None: display(_('package sphinx is required.'), color=RED) return 1 if self.clean and os.path.isdir(self.build_dir): msg = _('removing %(dir)s') % {'dir': self.build_dir} display(msg, color=GREEN) shutil.rmtree(self.build_dir) sphinx_opts = shlex.split(self.ALLSPHINXOPTS % (self.build_dir, os.getenv('SPHINXOPTS') or '', self.doc_dir)) count = 0 for orig_fmt, txt in self.outputs.items(): fmt = 'latex' if orig_fmt == 'latexpdf' else orig_fmt if orig_fmt not in self.distribution.command_options['gen_doc']: continue count = 1 options = ['sphinx-build', '-b', fmt, ] + sphinx_opts + [os.path.join(self.build_dir, fmt), ] result = sphinx.main(options) if result == 0: msg = txt % self.build_dir display(msg, color=GREEN) if orig_fmt == 'latexpdf': subprocess.check_call('make -C %s/latex all-pdf' % self.build_dir, shell=True) msg = "pdflatex finished; the PDF files are in %s/latex." % self.build_dir display(msg, color=GREEN) if not count: display(_("please select at least one output format (e.g. gen_doc --html)"), color=YELLOW)
def run(self): if polib is None: print(red(_('package polib is required.'))) return 1 module_names = find_packages() dst_rel_path = 'locale' if self.dest is None else self.dest # group by top-level packages and compute their directories: top_levels_modules = {} for module_name in module_names: top_level = module_name.partition('.')[0] if top_level not in top_levels_modules: locale_dir = os.path.join(os.path.dirname(load_module(top_level).__file__), dst_rel_path) top_levels_modules[top_level] = locale_dir for module_name, locale_dir in top_levels_modules.items(): po_filename = os.path.join(locale_dir, self.language, 'LC_MESSAGES', '%s.po' % module_name) if not os.path.isfile(po_filename): print(yellow(_('Missing file: %(filename)s. Please run the makemessages -l xx_XX command first.') % {'filename': po_filename})) continue po_content = polib.pofile(po_filename) for entry in po_content: entry.msgstr = translate_string(entry.msgid) print(green(_('Processed file: %(filename)s.') % {'filename': po_filename})) po_content.save(po_filename)
def main(): global INTERACTIVE parser = OptionParser(usage=_('%(bin)s [-extension my.extension.module:class]') % {'bin': sys.argv[0]}) parser.add_option('--extension', '-e', action='append', dest='extensions', default=[], help=_('extra extension module')) parser.add_option('-v', '--verbose', action='store_true', help='print more messages', default=False) parser.add_option('--nointeractive', '-n', action='store_true', dest='nointeractive', default=False, help=_('no interactive mode')) parser.add_option('--target', '-t', action='store', dest='target', default='.', help=_('base folder for the new project')) options, args = parser.parse_args(sys.argv[1:]) if options.nointeractive: INTERACTIVE = False log_config = starterpyth.log.CONSOLE if options.verbose: log_config['root']['level'] = 'DEBUG' else: log_config['root']['level'] = 'WARNING' starterpyth.log.dict_config(log_config) extensions = DEFAULT_EXTENSIONS + options.extensions context = {'project_root': options.target, 'entry_points': {}, 'install_requires': [], 'setup_requires': [], 'tests_requires': [], 'doc_urls': {}, 'ext_modules': [], 'extra_setup': [], 'classifiers': [], } filters = {} classes = [] for extension in extensions: module_name, class_name = extension.split(':', 1) module = load_module(module_name) cls = getattr(module, class_name)() classes.append(cls) for cls in classes: cls.update_global_context(context, filters) for cls in classes: if cls.is_selected(): cls.write_files(context, filters)
def run(self): if polib is None: print(red(_('package polib is required.'))) return 1 module_names = find_packages() dst_rel_path = 'locale' if self.dest is None else self.dest # group by top-level packages and compute their directories: top_levels_modules = {} for module_name in module_names: top_level = module_name.partition('.')[0] if top_level not in top_levels_modules: locale_dir = os.path.join( os.path.dirname(load_module(top_level).__file__), dst_rel_path) top_levels_modules[top_level] = locale_dir for module_name, locale_dir in top_levels_modules.items(): po_filename = os.path.join(locale_dir, self.language, 'LC_MESSAGES', '%s.po' % module_name) if not os.path.isfile(po_filename): print( yellow( _('Missing file: %(filename)s. Please run the makemessages -l xx_XX command first.' ) % {'filename': po_filename})) continue po_content = polib.pofile(po_filename) for entry in po_content: entry.msgstr = translate_string(entry.msgid) print( green( _('Processed file: %(filename)s.') % {'filename': po_filename})) po_content.save(po_filename)
def widget(self, initial=None): def l(i, x): if initial is not None and x[0] == initial: return _(' [%d] %s') % (i + 1, x[1]) return _(' %d %s') % (i + 1, x[1]) choices = _('\n').join([l(i, x) for (i, x) in enumerate(self.choices)]) return _('%(label)s:\n%(choices)s\n ') % {'label': self.label, 'choices': choices}
def input(self): prompt = _('%(label)s:') % {'label': self.label} if self.default is not None: prompt = _('%(label)s [default=%(default)s]: ') % { 'label': self.label, 'default': self.default } valid = False value = '' while not valid: if starterpyth.core.INTERACTIVE: if sys.version_info[0] == 3: value = input(prompt) else: value = raw_input(prompt).decode('utf-8') else: value = self.default logging.warning(prompt + value) if not value and self.default is not None: value = self.default try: self.validator(value) valid = True except ValidationError as exception: logging.error(exception.msg) value = self.convert_value(value=value) return value
def validator(self, value): if len(value) > self.max_length: raise ValidationError( _('Error: Maximum length for value is %(max_length)d') % {'max_length': self.max_length}) if not self.blank and not value: raise ValidationError(_('Error: Empty values are not allowed'))
class BooleanInput(CharInput): true_values = [_('yes'), _('y')] false_values = [_('no'), _('n')] def to_python(self, value): value = value.lower() if value in self.true_values: return True elif value in self.false_values: return False raise InvalidValue(_('Value must be one of %(l)s') % {'l': ', '.join(self.true_values + self.false_values)}) def to_str(self, value): if value: return self.true_values[0] return self.false_values[0] def widget(self, initial=None): if initial is None: choices = _('%s/%s') % (self.true_values[0], self.false_values[0]) elif initial: choices = _('%s/%s') % (self.true_values[0].upper(), self.false_values[0]) else: choices = _('%s/%s') % (self.true_values[0], self.false_values[0].upper()) return _('%(label)s [%(choices)s]: ') % {'label': self.label, 'choices': choices}
def widget(self, initial=None): if initial is None: choices = _('%s/%s') % (self.true_values[0], self.false_values[0]) elif initial: choices = _('%s/%s') % (self.true_values[0].upper(), self.false_values[0]) else: choices = _('%s/%s') % (self.true_values[0], self.false_values[0].upper()) return _('%(label)s [%(choices)s]: ') % {'label': self.label, 'choices': choices}
def to_python(self, value): if not value and self.required: raise InvalidValue(_('Please enter a valid choice')) elif not value: return None if not self.int_re.match(value) or not (1 <= int(value) <= len(self.choices)): raise InvalidValue(_('Please enter a number between 1 and %(max)d') % {'max': len(self.choices)}) return self.choices[int(value) - 1][0]
def to_python(self, value): if not value and self.required: raise InvalidValue(_('Please enter a valid path')) elif not value: return None if self.cwd: value = os.path.join(self.cwd, value) if not os.path.exists(value): raise InvalidValue(_('%(l)s is not a valid path') % {'l': value}) return value
def to_python(self, value): if not self.required and not value: return None if not self.regexp.match(value): raise InvalidValue(_('Value must be a integer')) value = int(value) if self.min_value is not None and self.min_value > value: raise InvalidValue(_('Value must be greater than %(l)d ') % {'l': self.min_value}) if self.max_value is not None and self.max_value < value: raise InvalidValue(_('Value must be less than %(l)d') % {'l': self.max_value}) return value
def main(): parser = OptionParser(usage=_('%(bin)s [-extension my.extension.module:class]') % {'bin': sys.argv[0]}) parser.add_option('--extension', '-e', action='append', dest='extensions', default=[], help=_('extra extension module')) parser.add_option('-v', '--verbose', action='store_true', help='print more messages', default=False) parser.add_option('--nointeractive', '-n', action='store_false', dest='nointeractive', default=True, help=_('no interactive mode')) options, args = parser.parse_args(sys.argv[1:]) base_form = BaseInfoForm(initial={'model': CliModel, 'overwrite': True, 'use_py26': True, 'use_six': False}) base_context = base_form.read(interactive=options.nointeractive) model = base_context['model'](base_context=base_context) model.run(interactive=options.nointeractive)
def run(self): if Environment is None: logging.critical(_('package jinja2 is required.')) return 1 env = Environment(loader=PackageLoader('starterpyth.commands', 'templates')) def write_template(template_, path, context): """ Write a template file. :param template_: Jinja2 template :type template_: :class:`Template` :param path: destination path :type path: basestring :param context: context :type context: :class:`dict` """ dirname = os.path.dirname(path) if not os.path.isdir(dirname): os.makedirs(dirname) if not os.path.isfile(path) or self.overwrite: tpl_fd = codecs.open(path, 'w', encoding='utf-8') tpl_fd.write(template_.render(context)) tpl_fd.close() logging.info('writing %s' % path) src_module_names = find_packages() if self.pre_rm and os.path.isdir(self.api_dir): logging.info('removing %s' % self.api_dir) shutil.rmtree(self.api_dir) module_names = [] excluded_module_names = set([x.strip() for x in self.modules_to_exclude.split(',') if x.strip()]) for module_name in src_module_names: module = load_module(module_name) logging.warning('Processing %s.' % module_name) if not any(fnmatch.fnmatch(module_name, x) for x in excluded_module_names): module_names.append(module_name) module_root = os.path.dirname(module.__file__) for filename in os.listdir(module_root): basename, sep, ext = filename.rpartition('.') if ext not in ('pyx', 'py', 'so') or filename == '__init__.py': continue submodule_name = '%s.%s' % (module_name, basename) try: load_module(submodule_name) if not any(fnmatch.fnmatch(submodule_name, x) for x in excluded_module_names): module_names.append(submodule_name) except ImportError as e: msg = 'Unable to import %s [%s].' % (submodule_name, e) logging.warning(msg) template = env.get_template('index.rst_tpl') all_module_names = [mod_name.replace('.', '/') for mod_name in module_names] all_module_names.sort() write_template(template, os.path.join(self.api_dir, 'index.rst'), {'module_paths': all_module_names}) template = env.get_template('module.rst_tpl') for mod_name in module_names: path_components = mod_name.split('.') path_components[-1] += '.rst' write_template(template, os.path.join(self.api_dir, *path_components), {'module_name': mod_name})
def run(self, interactive=True): project_root = self.global_context['project_root'] if os.path.exists(project_root): if self.global_context['overwrite']: if os.path.isdir(project_root): shutil.rmtree(project_root) else: os.remove(project_root) else: display(_('Destination path already exists!'), color=RED, bold=True) return context = self.get_context() self.global_context.update(context) extra_form = self.get_extraform(interactive=interactive) self.global_context.update(extra_form) extra_context = self.get_extracontext() self.global_context.update(extra_context) filters = self.get_template_filters() self.set_virtualenvs() for modname, dirname in self.template_roots: display('dirname %s' % dirname, color=CYAN) env = self.get_environment(modname, dirname, filters) self.write_files(modname, dirname, env)
def run(self): module_names = find_packages() dst_rel_path = 'locale' if self.dest is None else self.dest # group by top-level packages and compute their directories: top_levels_modules = {} for module_name in module_names: tl, sep, bl = module_name.partition('.') if tl not in top_levels_modules: locale_dir = os.path.join( os.path.dirname(load_module(tl).__file__), dst_rel_path) top_levels_modules[tl] = locale_dir for module_name, locale_dir in top_levels_modules.items(): mo_file = os.path.join(locale_dir, self.language, 'LC_MESSAGES', '%s.mo' % module_name) po_file = os.path.join(locale_dir, self.language, 'LC_MESSAGES', '%s.po' % module_name) if not os.path.isdir(os.path.dirname(mo_file)): os.makedirs(os.path.dirname(mo_file)) if os.path.isfile(po_file): cmd = 'msgfmt --output-file %s %s' % (mo_file, po_file) display(_('Processing file %(filename)s.') % {'filename': po_file}, color=GREEN) subprocess.check_call(cmd, shell=True, stderr=subprocess.PIPE)
def run(self, interactive=True): project_root = self.global_context['project_root'] if os.path.exists(project_root): if self.global_context['overwrite']: if os.path.isdir(project_root): shutil.rmtree(project_root) else: os.remove(project_root) else: display(_('Destination path already exists!'), color=RED, bold=True) return context = self.get_context() self.global_context.update(context) extra_form = self.get_extraform(interactive=interactive) self.global_context.update(extra_form) extra_context = self.get_extracontext() self.global_context.update(extra_context) filters = self.get_template_filters() self.set_virtualenvs() for modname, dirname in self.template_roots: display('dirname %s' % dirname, color=CYAN) env = self.get_environment(modname, dirname, filters) self.write_files(modname, dirname, env)
def to_python(self, value): value = value.lower() if value in self.true_values: return True elif value in self.false_values: return False raise InvalidValue(_('Value must be one of %(l)s') % {'l': ', '.join(self.true_values + self.false_values)})
def validator(self, value): result = value.lower() if result not in (self.yes_str, self.no_str): raise ValidationError( _('Error: Value must be %(yes)s of %(no)s') % { 'yes': self.yes_str, 'no': self.no_str })
class PackageModel(Model): name = _('Python package') @property def template_roots(self): result = [('starterpyth', 'templates/common'), ('starterpyth', 'templates/package/package'), ] if self.global_context.get('use_i18n'): result += [('starterpyth', 'templates/package'), ('starterpyth', 'templates/package/translation'), ] return result
def update_global_context(self, context, filters): self.selected = BooleanInput(_('Create a Cython application'), default=True).input() if not self.selected: return context['doc_urls']['cython'] = ('http://docs.cython.org/', 'externals/cython_0.19.inv') # context['ext_modules'].append('Extension("sample_cython", ["sample_cython.pyx"])') # context['extra_imports'].append(('distutils.extension', 'Extension', None)) # context['extra_imports'].append(('Cython.Distutils', 'build_ext', 'cython_build_ext')) cython_setup = self.get_template(context, 'starterpyth.plugins.starter_cython', 'extra_templates/setup.py_tpl') context['extra_setup'].append(cython_setup)
def update_global_context(self, context, filters): self.selected = BooleanInput(_('Create a shell application'), default=True).input() if not self.selected: return script = '%(module_name)s-bin = %(module_name)s.cli:main' % { 'module_name': context['module_name'] } context['entry_points'].setdefault('console_scripts', []).append(script)
def update_global_context(self, context, filters): self.selected = BooleanInput(_('Create a Django website'), default=True).input() if not self.selected: return if 3.0 <= context['pyversion'] <= 3.2: logging.warning(_('WARNING: django-tastypie is not compatible with Python 3.0 -> 3.2')) self.use_tastypie = BooleanInput(_('Create sample REST API with Tastypie'), default=True).input() self.use_tastypie_swagger = False if self.use_tastypie: self.use_tastypie_swagger = BooleanInput(_('Create API doc with Tastypie Swagger'), default=True).input() script = '%(module_name)s-manage = %(module_name)s.djangoproject.manage:main' % \ {'module_name': context['module_name']} context['entry_points'].setdefault('console_scripts', []).append(script) context['install_requires'].append('django') if self.use_tastypie: context['install_requires'].append('django-tastypie') context['install_requires'].append('python-mimeparse') context['install_requires'].append('python-dateutil') if self.use_tastypie_swagger: context['install_requires'].append('django-tastypie-swagger')
def run(self): if sphinx is None: display(_('package sphinx is required.'), color=RED) return 1 if self.clean and os.path.isdir(self.build_dir): msg = _('removing %(dir)s') % {'dir': self.build_dir} display(msg, color=GREEN) shutil.rmtree(self.build_dir) sphinx_opts = shlex.split( self.ALLSPHINXOPTS % (self.build_dir, os.getenv('SPHINXOPTS') or '', self.doc_dir)) count = 0 for orig_fmt, txt in self.outputs.items(): fmt = 'latex' if orig_fmt == 'latexpdf' else orig_fmt if orig_fmt not in self.distribution.command_options['gen_doc']: continue count = 1 options = [ 'sphinx-build', '-b', fmt, ] + sphinx_opts + [ os.path.join(self.build_dir, fmt), ] result = sphinx.main(options) if result == 0: msg = txt % self.build_dir display(msg, color=GREEN) if orig_fmt == 'latexpdf': subprocess.check_call('make -C %s/latex all-pdf' % self.build_dir, shell=True) msg = "pdflatex finished; the PDF files are in %s/latex." % self.build_dir display(msg, color=GREEN) if not count: display(_( "please select at least one output format (e.g. gen_doc --html)" ), color=YELLOW)
def update_global_context(self, context, filters): self.selected = BooleanInput(_('Create a Cython application'), default=True).input() if not self.selected: return context['doc_urls']['cython'] = ('http://docs.cython.org/', 'externals/cython_0.19.inv') # context['ext_modules'].append('Extension("sample_cython", ["sample_cython.pyx"])') # context['extra_imports'].append(('distutils.extension', 'Extension', None)) # context['extra_imports'].append(('Cython.Distutils', 'build_ext', 'cython_build_ext')) cython_setup = self.get_template(context, 'starterpyth.plugins.starter_cython', 'extra_templates/setup.py_tpl') context['extra_setup'].append(cython_setup)
def input(self): prompt = _('%(label)s:') % {'label': self.label} if self.default is not None: prompt = _('%(label)s [default=%(default)s]: ') % {'label': self.label, 'default': self.default} valid = False value = '' while not valid: if starterpyth.core.INTERACTIVE: if sys.version_info[0] == 3: value = input(prompt) else: value = raw_input(prompt).decode('utf-8') else: value = self.default logging.warning(prompt + value) if not value and self.default is not None: value = self.default try: self.validator(value) valid = True except ValidationError as exception: logging.error(exception.msg) value = self.convert_value(value=value) return value
class CliModel(Model): name = _('Python binary') @property def template_roots(self): result = super(CliModel, self).template_roots result += [('starterpyth', 'templates/common'), ('starterpyth', 'templates/package/package'), ('starterpyth', 'templates/cli'), ] if self.global_context.get('use_i18n'): result += [('starterpyth', 'templates/package'), ('starterpyth', 'templates/package/translation'), ] return result def get_extracontext(self): self.global_context['entry_points'].setdefault('console_scripts', []) module_name = self.global_context['module_name'] scripts = ['%s = %s.cli:main' % (module_name, module_name), ] self.global_context['entry_points']['console_scripts'] += scripts return {}
def read(self, initial=None): if initial is None: initial = self.initial raw_value = input_(self.widget(initial=initial)) if not raw_value and initial is not None: raw_value = self.to_str(initial) while True: try: valid_value = self.to_python(raw_value) break except InvalidValue as e: display(_('Invalid value: %(value)s (%(msg)s)') % {'value': raw_value, 'msg': str(e)}, color=RED, bold=True) raw_value = input_(self.widget()) if not raw_value and initial is not None: raw_value = self.to_str(initial) # noinspection PyUnboundLocalVariable return valid_value
class DjangofloorModel(Model): name = _('Djangofloor-based website') @property def template_roots(self): result = super(DjangofloorModel, self).template_roots result += [('starterpyth', 'templates/common'), ('starterpyth', 'templates/djangofloor')] return result class ExtraForm(BaseForm): use_djangorestframework = BooleanInput(label=_('Use Django REST framework'), initial=True) use_websockets = BooleanInput(label=_('Use Websockets'), initial=usewebsockets, show=usewebsockets) use_redis = BooleanInput(label=_('Use Redis database'), initial=True, show=lambda **kwargs: not kwargs['use_websockets']) def get_extracontext(self): requires = ['djangofloor', ] self.global_context['entry_points'].setdefault('console_scripts', []) module_name = self.global_context['module_name'] scripts = ['%s-manage = djangofloor.scripts:manage' % module_name, '%s-celery = djangofloor.scripts:celery' % module_name, '%s-uswgi = djangofloor.scripts:uswgi' % module_name, '%s-gunicorn = djangofloor.scripts:gunicorn' % module_name, ] self.global_context['entry_points']['console_scripts'] += scripts if self.global_context['use_websockets']: requires += ['django-websocket-redis', 'gevent', 'uwsgi', ] if self.global_context['use_djangorestframework']: requires += ['djangorestframework', 'markdown', 'django-filter', 'pygments', ] self.global_context['install_requires'] += requires self.global_context['secret_key'] = self.__get_random_string() return {} def process_directory_or_file(self, src_path, dst_path, name, is_directory): if name == 'api.py' and not self.file_context['use_tastypie']: return False return super(DjangofloorModel, self).process_directory_or_file(src_path, dst_path, name, is_directory) @staticmethod def __get_random_string(length=50, allowed_chars='abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'): # noinspection PyUnusedLocal return ''.join([random.choice(allowed_chars) for i in range(length)])
def run(self): module_names = find_packages() dst_rel_path = 'locale' if self.dest is None else self.dest # group by top-level packages and compute their directories: top_levels_modules = {} for module_name in module_names: tl, sep, bl = module_name.partition('.') if tl not in top_levels_modules: locale_dir = os.path.join(os.path.dirname(load_module(tl).__file__), dst_rel_path) top_levels_modules[tl] = locale_dir for module_name, locale_dir in top_levels_modules.items(): mo_file = os.path.join(locale_dir, self.language, 'LC_MESSAGES', '%s.mo' % module_name) po_file = os.path.join(locale_dir, self.language, 'LC_MESSAGES', '%s.po' % module_name) if not os.path.isdir(os.path.dirname(mo_file)): os.makedirs(os.path.dirname(mo_file)) if os.path.isfile(po_file): cmd = 'msgfmt --output-file %s %s' % (mo_file, po_file) display(_('Processing file %(filename)s.') % {'filename': po_file}, color=GREEN) subprocess.check_call(cmd, shell=True, stderr=subprocess.PIPE)
class DjangoModel(Model): name = _('Django-based website') @property def template_roots(self): result = super(DjangoModel, self).template_roots result += [('starterpyth', 'templates/common'), ('starterpyth', 'templates/django')] return result class ExtraForm(BaseForm): use_tastypie = BooleanInput(label=_('Use tastypie'), initial=True) def get_extracontext(self): requires = ['django', 'south', 'gunicorn', 'django-bootstrap3', 'django-pipeline', 'django-grappelli', 'django-debug-toolbar', ] if self.global_context['use_tastypie']: requires += ['django-tastypie', 'django-tastypie-swagger'] self.global_context['entry_points'].setdefault('console_scripts', []) module_name = self.global_context['module_name'] scripts = ['%s-manage = %s.core.scripts:main' % (module_name, module_name), '%s-gunicorn = %s.core.scripts:gunicorn' % (module_name, module_name)] self.global_context['entry_points']['console_scripts'] += scripts self.global_context['install_requires'] += requires self.global_context['setup_requires'] += requires self.global_context['secret_key'] = self.__get_random_string() return {} def process_directory_or_file(self, src_path, dst_path, name, is_directory): if name == 'api.py' and not self.file_context['use_tastypie']: return False return super(DjangoModel, self).process_directory_or_file(src_path, dst_path, name, is_directory) @staticmethod def __get_random_string(length=50, allowed_chars='abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'): return ''.join([random.choice(allowed_chars) for i in range(length)])
def validator(self, value): if value not in self.choices: raise ValidationError( _('Error: Value must be one of %(choices)s') % {'choices': _(', ').join(self.choices.keys())})
def __init__(self, label, default=True): self.yes_str = _('yes') self.no_str = _('no') super(BooleanInput, self).__init__(label, default=self.yes_str if default else self.no_str)
def write_files(self, context, filters): """ Write template or raw files to the new project :param context: context (dict) to be used by jinja2 :param filters: extra filters for jinja2 :param excludes: files to exclude :return: """ from jinja2 import Environment, PackageLoader, Template from jinja2.loaders import ChoiceLoader local_context = copy.copy(context) local_context.update(self.get_local_context(local_context)) excludes = self.get_excluded_files(local_context) modname, dirname = self.get_resources() if modname is None or dirname is None: return loader = ChoiceLoader([ PackageLoader('starterpyth', 'templates'), PackageLoader(modname, dirname) ]) env = Environment(loader=loader) env.filters.update(filters) project_root = os.path.join(context['project_root'], context['project_name']) if not os.path.isdir(project_root): msg = _('Directory %(f)s created.') % {'f': project_root} logging.info(msg) os.makedirs(project_root) # noinspection PyTypeChecker prefix_len = len(dirname) + 1 def get_path(root, name): """return relative source path (to template dir) and destination path""" src_path = (root + '/' + name)[prefix_len:] dst_path = src_path if os.sep != '/': dst_path = dst_path.replace('/', os.sep) if dst_path.find('{') > -1: dst_path = Template(dst_path).render(**local_context) if dst_path[-4:] == '_tpl': dst_path = dst_path[:-4] return src_path, os.path.join(project_root, dst_path) for root, dirnames, filenames in starterpyth.utils.walk( modname, dirname): for dirname in dirnames: src_path, dst_path = get_path(root, dirname) if src_path in excludes: continue if not os.path.isdir(dst_path): msg = _('Directory %(f)s created.') % {'f': dst_path} logging.info(msg) os.makedirs(dst_path) for filename in filenames: if filename[-4:] == '_inc': continue src_path, dst_path = get_path(root, filename) if src_path in excludes: continue if not os.path.isdir(os.path.dirname(dst_path)): continue if filename[-4:] == '_tpl': template = env.get_template(src_path) f_out = open(dst_path, 'ab') f_out.write( template.render(**local_context).encode('utf-8')) f_out.close() msg = _('Template %(f)s written.') % {'f': dst_path} logging.info(msg) else: f_out = open(dst_path, 'wb') f_in = pkg_resources.resource_stream( modname, root + '/' + filename) data = f_in.read(10240) while data: f_out.write(data) data = f_in.read(10240) f_in.close() f_out.close() msg = _('File %(f)s written.') % {'f': dst_path} logging.info(msg)
def validator(self, value): if not self.regexp.match(value): raise ValidationError( _('Error: Value must match the regexp %(regexp)s') % {'regexp': self.regexp.pattern})
def l(i, x): if initial is not None and x[0] == initial: return _(' [%d] %s') % (i + 1, x[1]) return _(' %d %s') % (i + 1, x[1])
def update_global_context(self, context, filters): self.selected = BooleanInput(_('Create a shell application'), default=True).input() if not self.selected: return script = '%(module_name)s-bin = %(module_name)s.cli:main' % {'module_name': context['module_name']} context['entry_points'].setdefault('console_scripts', []).append(script)
def update_global_context(self, context, filters): project_name = RegexpInput(_('Project name'), regexp=r'[A-Za-z]\w*', default='Project').input() module_name = RegexpInput(_('Python module name'), regexp=r'[A-Za-z]\w*', default=project_name.lower()).input() company = CharInput(_('Company name'), max_length=255, blank=True, default=defaults.COMPANY).input() author = CharInput(_('Author name'), max_length=255, default=defaults.AUTHOR).input() email = RegexpInput(_('Author e-mail'), default='%s@%s' % (author, company), regexp=r'[\w_\-\.]+@[\w_\-\.]').input() license_ = ChoiceInput(_('License'), choices=licenses, blank=True, default='cecill b').input() pyversion = ChoiceInput(_('Minimum Python version'), choices=pyversions, default='2.7').input() use_2to3 = False use_six = False py3compat = 'source' if pyversion < 3.: use_six = BooleanInput( _('Use six tool for Python 3 compatibility'), default=True).input() if not use_six: use_2to3 = BooleanInput( _('Use 2to3 tool for Python 3 compatibility'), default=True).input() py3compat = '2to3' if use_2to3 else None else: py3compat = 'six' translation = BooleanInput(_('Include translation (i18n) stuff'), default=True).input() context['translation'] = translation if translation: filters['translate'] = py3k_unicode('_(\'{0}\')').format else: filters['translate'] = py3k_unicode('\'{0}\'').format module_version = RegexpInput(_('Initial version'), regexp=r'[\w\.\-]', default='0.1').input() context['project_name'] = project_name context['module_name'] = module_name context['pyversion'] = pyversion context['use_2to3'] = use_2to3 context['use_six'] = use_six context['py3compat'] = py3compat context['license'] = license_names[license_] if license_ != 'Other': licence_fd = pkg_resources.resource_stream( 'starterpyth.plugins.base', 'licenses/%s.txt' % license_) context['license_content'] = licence_fd.read().decode('utf-8') licence_fd.close() else: context['license_content'] = '' if py3compat == 'six': filters['unicode'] = lambda x: py3k_unicode('six.u("{0}")').format( x.replace("\"", "\\\"")) filters['binary'] = lambda x: py3k_unicode('six.b("{0}")').format( x.replace("\"", "\\\"")) context['unicode'] = 'six.text_type' context['binary'] = 'six.binary_type' context['install_requires'].append('six') context['setup_requires'].append('six') context['tests_requires'].append('six') elif py3compat == 'source': filters['unicode'] = lambda x: py3k_unicode('"{0}"').format( x.replace("\"", "\\\"")) filters['binary'] = lambda x: py3k_unicode('b"{0}"').format( x.replace("\"", "\\\"")) context['unicode'] = 'str' context['binary'] = 'bytes' else: # no compatibility or compatibility through 2to3 filters['unicode'] = lambda x: py3k_unicode('u"{0}"').format( x.replace("\"", "\\\"")) filters['binary'] = lambda x: py3k_unicode('"{0}"').format( x.replace("\"", "\\\"")) context['unicode'] = 'unicode' context['binary'] = 'str' context['copyright_full'] = _('Copyright %(year)d, %(comp)s') % { 'year': datetime.date.today().year, 'comp': company } context['company'] = company context['project_url'] = py3k_unicode('http://{0}/{1}.html').format( company, project_name) context['email'] = email context['author'] = author context['module_version'] = module_version context['install_requires'].append('distribute') context['setup_requires'].append('distribute') context['tests_requires'].append('distribute') context['classifiers'].append('Programming Language :: Python') if py3compat in ('source', 'six'): context['classifiers'].append( 'Programming Language :: Python :: 3') context['year'] = datetime.date.today().year context['doc_urls']['python'] = ('http://docs.python.org/%.1f/' % pyversion, 'externals/python_%.1f.inv' % pyversion) path = os.path.join(context['project_root'], project_name) if os.path.isdir(path): rm_choice = ChoiceInput( _('The folder %(f)s already exists. Remove it?') % { 'f': path }, default='yes', choices=(('yes', 'yes'), ('no', 'no'))).input() if rm_choice == 'yes': shutil.rmtree(path)
def main(): global INTERACTIVE parser = OptionParser( usage=_('%(bin)s [-extension my.extension.module:class]') % {'bin': sys.argv[0]}) parser.add_option('--extension', '-e', action='append', dest='extensions', default=[], help=_('extra extension module')) parser.add_option('-v', '--verbose', action='store_true', help='print more messages', default=False) parser.add_option('--nointeractive', '-n', action='store_true', dest='nointeractive', default=False, help=_('no interactive mode')) parser.add_option('--target', '-t', action='store', dest='target', default='.', help=_('base folder for the new project')) options, args = parser.parse_args(sys.argv[1:]) if options.nointeractive: INTERACTIVE = False log_config = starterpyth.log.CONSOLE if options.verbose: log_config['root']['level'] = 'DEBUG' else: log_config['root']['level'] = 'WARNING' starterpyth.log.dict_config(log_config) extensions = DEFAULT_EXTENSIONS + options.extensions context = { 'project_root': options.target, 'entry_points': {}, 'install_requires': [], 'setup_requires': [], 'tests_requires': [], 'doc_urls': {}, 'ext_modules': [], 'extra_setup': [], 'classifiers': [], } filters = {} classes = [] for extension in extensions: module_name, class_name = extension.split(':', 1) module = load_module(module_name) cls = getattr(module, class_name)() classes.append(cls) for cls in classes: cls.update_global_context(context, filters) for cls in classes: if cls.is_selected(): cls.write_files(context, filters)
def validator(self, value): if value not in self.choices: raise ValidationError(_('Error: Value must be one of %(choices)s') % {'choices': _(', ').join(self.choices.keys())})
def write_files(self, modname, dirname, env): """ Write all templated or raw files to the new project. All template are rendered twice. This behaviour allows to determine which functions must be imported at the beginning of Python files :param modname: module containing template files :param dirname: dirname containing template files in the module `modname` :param env: Jinja2 environment :return: """ from jinja2 import Template project_root = self.global_context['project_root'] # creation of the project directory if needed if not os.path.isdir(project_root): os.makedirs(project_root) display(_('Directory %(f)s created.') % {'f': project_root}, color=GREEN) # noinspection PyTypeChecker prefix_len = len(dirname) + 1 def get_path(root_, name): """return relative source path (to template dir) and absolute destination path""" src_path_ = (root_ + '/' + name)[prefix_len:] dst_path_ = src_path_ if os.sep != '/': dst_path_ = dst_path_.replace('/', os.sep) if dst_path_.find('{') > -1: # the name of the file is templated dst_path_ = Template(dst_path_).render(**self.global_context) if dst_path_[-len(self.template_suffix):] == self.template_suffix: dst_path_ = dst_path_[:-len(self.template_suffix)] return src_path_, os.path.join(project_root, dst_path_) # walk through all files (raw and templates) in modname/dirname and write them to destination for root, dirnames, filenames in walk(modname, dirname): for dirname in dirnames: src_path, dst_path = get_path(root, dirname) if not self.process_directory_or_file(src_path, dst_path, dirname, True): continue if not os.path.isdir(dst_path): os.makedirs(dst_path) display(_('Directory %(f)s created.') % {'f': dst_path}, color=GREEN) for filename in filenames: src_path, dst_path = get_path(root, filename) if not self.process_directory_or_file(src_path, dst_path, filename, False): continue if not os.path.isdir(os.path.dirname(dst_path)): continue if filename[-len(self.template_suffix):] == self.template_suffix: self.file_context = {'render_pass': 1} template = env.get_template(src_path) f_out = open(dst_path, 'wb') self.file_context.update(self.global_context) template.render(**self.file_context) self.file_context['render_pass'] = 2 template_content = template.render(**self.file_context).encode('utf-8') f_out.write(template_content) f_out.close() display(_('Template %(f)s written.') % {'f': dst_path}, color=GREEN) else: f_out = open(dst_path, 'wb') f_in = pkg_resources.resource_stream(modname, root + '/' + filename) data = f_in.read(10240) while data: f_out.write(data) data = f_in.read(10240) f_in.close() f_out.close() display(_('File %(f)s written.') % {'f': dst_path}, color=GREEN)
def validator(self, value): if not self.regexp.match(value): raise ValidationError(_('Error: Value must match the regexp %(regexp)s') % {'regexp': self.regexp.pattern})
def validator(self, value): if len(value) > self.max_length: raise ValidationError(_('Error: Maximum length for value is %(max_length)d') % {'max_length': self.max_length}) if not self.blank and not value: raise ValidationError(_('Error: Empty values are not allowed'))
def widget(self, initial=None): if initial: return _('%(label)s [%(init)s]: ') % {'label': self.label, 'init': self.to_str(initial)} else: return _('%(label)s: ') % {'label': self.label}
def validator(self, value): result = value.lower() if result not in (self.yes_str, self.no_str): raise ValidationError(_('Error: Value must be %(yes)s of %(no)s') % {'yes': self.yes_str, 'no': self.no_str})
def to_python(self, value): if self.min_length is not None and self.min_length > len(value): raise InvalidValue(_('Value must be at least %(l)d character long') % {'l': self.min_length}) if self.max_length is not None and self.max_length < len(value): raise InvalidValue(_('Value must be at most %(l)d character long') % {'l': self.max_length}) return value
def run(self): if Environment is None: display(_('package jinja2 is required.'), color=RED) return 1 module_names = find_packages() dst_rel_path = 'locale' if self.dest is None else self.dest # group by top-level packages and compute their directories: all_modules = {} top_levels_modules = {} for module_name in module_names: top_module = module_name.partition('.')[0] all_modules.setdefault(top_module, []).append(module_name) if top_module not in top_levels_modules: top_levels_modules[top_module] = os.path.dirname(load_module(top_module).__file__) env = Environment(loader=PackageLoader('starterpyth.commands.makemessages', 'templates')) template = env.get_template('lang.po') context = { 'description': self.distribution.get_description(), 'copyright': self.distribution.get_author(), 'package': None, 'author': self.distribution.get_author(), 'version': self.distribution.get_version(), 'email': self.distribution.get_author_email(), 'year': datetime.datetime.now().year, } for tl_name in top_levels_modules.keys(): dst_abs_path = os.path.join(top_levels_modules[tl_name], dst_rel_path) pot_filename = os.path.join(dst_abs_path, '%s.pot' % tl_name) po_filename = os.path.join(dst_abs_path, self.language, 'LC_MESSAGES', '%s.po' % tl_name) if not os.path.isdir(os.path.dirname(po_filename)): os.makedirs(os.path.dirname(po_filename)) for filename in (pot_filename, po_filename): if not os.path.isfile(filename): context['package'] = tl_name po_fd = codecs.open(filename, 'w', encoding='utf-8') po_fd.write(template.render(context)) po_fd.close() for tl_name, module_names in all_modules.items(): dst_abs_path = os.path.join(top_levels_modules[tl_name], dst_rel_path) root_path = os.path.dirname(top_levels_modules[tl_name]) print(root_path) pot_filename = os.path.join(dst_abs_path, '%s.pot' % tl_name) po_filename = os.path.join(dst_abs_path, self.language, 'LC_MESSAGES', '%s.po' % tl_name) # build the list of files to examine, for each top-level module filenames = [] for module_name in module_names: init_filename = load_module(module_name).__file__ local_root = os.path.dirname(init_filename) for filename in os.listdir(local_root): filename = os.path.join(local_root, filename) basename, sepa, ext = filename.rpartition('.') if ext not in ('py', 'pyx', 'c'): continue try: po_fd = codecs.open(filename, 'r', encoding='utf-8') po_fd.read() po_fd.close() filenames.append(os.path.relpath(filename, root_path)) msg = _('%(filename)s added.') % {'filename': filename} display(msg, color=GREEN) except UnicodeDecodeError: msg = _('Encoding of %(filename)s is not UTF-8.') % {'filename': filename} display(msg, color=GREEN) cmd = ['xgettext', '--language=Python', '--keyword=_', u('--output=%s') % pot_filename, '--from-code=UTF-8', '--add-comments=Translators', ] + filenames subprocess.check_call(cmd, stdout=subprocess.PIPE) if os.path.isfile(po_filename): cmd = ['msgmerge', '--update', '--backup=off', po_filename, pot_filename, ] else: cmd = ['msginit', '--no-translator', '-l', self.language, u('--input=%s') % pot_filename, u('--output=%s') % po_filename, ] subprocess.check_call(cmd, stderr=subprocess.PIPE) msg = _('Please translate strings in %(filename)s') % {'filename': po_filename} display(msg, color=YELLOW) msg = _('Then run setup.py compilemessages -l %(lang)s') % {'lang': self.language} display(msg, color=YELLOW)
def __init__(self, label, default=True): self.yes_str = _('yes') self.no_str = _('no') super(BooleanInput, self).__init__(label, default=self.yes_str if default else self.no_str)
def update_global_context(self, context, filters): project_name = RegexpInput(_('Project name'), regexp=r'[A-Za-z]\w*', default=u('Project')).input() module_name = RegexpInput(_('Python module name'), regexp=r'[A-Za-z]\w*', default=project_name.lower()).input() company = CharInput(_('Company name'), max_length=255, blank=True, default=defaults.COMPANY).input() author = CharInput(_('Author name'), max_length=255, default=defaults.AUTHOR).input() author_normalized = normalize_str(author) company_normalized = normalize_str(company) email = RegexpInput(_('Author e-mail'), default=u('%s@%s') % (author_normalized, company_normalized), regexp=r'[\w_\-\.]+@[\w_\-\.]').input() license_ = ChoiceInput(_('License'), choices=licenses, blank=True, default='cecill b').input() pyversion = ChoiceInput(_('Minimum Python version'), choices=pyversions, default='2.7').input() use_2to3 = False use_six = False py3compat = 'source' if pyversion < 3.: use_six = BooleanInput(_('Use six tool for Python 3 compatibility'), default=True).input() if not use_six: use_2to3 = BooleanInput(_('Use 2to3 tool for Python 3 compatibility'), default=True).input() py3compat = '2to3' if use_2to3 else None else: py3compat = 'six' translation = BooleanInput(_('Include translation (i18n) stuff'), default=True).input() context['translation'] = translation if translation: filters['translate'] = u('_(\'{0}\')').format else: filters['translate'] = u('\'{0}\'').format module_version = RegexpInput(_('Initial version'), regexp=r'[\w\.\-]', default='0.1').input() context['project_name'] = project_name context['module_name'] = module_name context['pyversion'] = pyversion context['use_2to3'] = use_2to3 context['use_six'] = use_six context['py3compat'] = py3compat context['license'] = license_names[license_] context['file_encoding'] = '' if author_normalized != author or company_normalized != company: context['file_encoding'] = "# -*- coding: utf-8 -*-\n" if license_ != 'Other': licence_fd = pkg_resources.resource_stream('starterpyth.plugins.base', 'licenses/%s.txt' % license_) context['license_content'] = licence_fd.read().decode('utf-8') licence_fd.close() else: context['license_content'] = '' if py3compat == 'six': filters['unicode'] = lambda x: u('six.u("{0}")').format(x.replace("\"", "\\\"")) filters['binary'] = lambda x: u('six.b("{0}")').format(x.replace("\"", "\\\"")) context['unicode'] = 'six.text_type' context['binary'] = 'six.binary_type' context['install_requires'].append('six') context['setup_requires'].append('six') context['tests_requires'].append('six') elif py3compat == 'source': filters['unicode'] = lambda x: u('"{0}"').format(x.replace("\"", "\\\"")) filters['binary'] = lambda x: u('b"{0}"').format(x.replace("\"", "\\\"")) context['unicode'] = 'str' context['binary'] = 'bytes' else: # no compatibility or compatibility through 2to3 filters['unicode'] = lambda x: u('u"{0}"').format(x.replace("\"", "\\\"")) filters['binary'] = lambda x: u('"{0}"').format(x.replace("\"", "\\\"")) context['unicode'] = 'unicode' context['binary'] = 'str' context['copyright_full'] = _('Copyright %(year)d, %(comp)s') % {'year': datetime.date.today().year, 'comp': company} context['company'] = company context['project_url'] = u('http://{0}/{1}.html').format(company, project_name) context['email'] = email context['author'] = author context['module_version'] = module_version context['install_requires'].append('setuptools>=0.7') context['setup_requires'].append('setuptools>=0.7') context['tests_requires'].append('setuptools>=0.7') context['classifiers'].append('Programming Language :: Python') if py3compat in ('source', 'six', '2to3'): context['classifiers'].append('Programming Language :: Python :: 3') context['year'] = datetime.date.today().year context['doc_urls']['python'] = ('http://docs.python.org/%.1f/' % pyversion, 'externals/python_%.1f.inv' % pyversion) path = os.path.join(context['project_root'], project_name) if os.path.isdir(path): rm_choice = ChoiceInput(_('The folder %(f)s already exists. Remove it?') % {'f': path}, default='yes', choices=(('yes', 'yes'), ('no', 'no'))).input() if rm_choice == 'yes': shutil.rmtree(path)
def to_python(self, value): if not self.regexp.match(value): raise InvalidValue(_('Value must match %(l)s regexp') % {'l': self.regexp.pattern}) return value