def get_name_filter(filter_this, include_this): """Returns a function that evaluates if a file or folder name must be filtered out. The compared paths are first converted to unicode and decomposed. This is neccesary because the way PY2.* `os.walk` read unicode paths in different filesystems. For instance, in OSX, it returns a decomposed unicode string. In those systems, u'ñ' is read as `\u0303` instead of `\xf1`. """ filter_this = [normalize(to_unicode(f)) for f in filter_this] include_this = [normalize(to_unicode(f)) for f in include_this] def fullmatch(path, pattern): path = normalize(path) name = os.path.basename(path) return fnmatch(name, pattern) or fnmatch(path, pattern) def must_be_filtered(name): return reduce(lambda r, pattern: r or fullmatch(name, pattern), filter_this, False) def must_be_included(name): return reduce(lambda r, pattern: r or fullmatch(name, pattern), include_this, False) def must_filter(path): return must_be_filtered(path) and not must_be_included(path) return must_filter
def create_file(path, content, encoding='utf8'): """Create a file at path the content. Content is assumed to be a utf-8 encoded string. """ content = to_unicode(content, encoding) with io.open(path, 'wt', encoding=encoding) as f: f.write(content)
def render_local_skeleton( src_path, dst_path, data=None, filter_this=None, include_this=None, pretend=False, force=False, skip=False, quiet=False, envops=None): """Uses the template in ``src_path`` to generate the scaffold/skeleton at ``dst_path``. ``src_path`` **must be** an absolute local path. Called by ``render_skeleton``. """ src_path = to_unicode(src_path) if not os.path.exists(src_path): raise ValueError('Project skeleton not found') if not os.path.isdir(src_path): raise ValueError('Project skeleton must be a folder') data = clean_data(data) data.setdefault('folder_name', os.path.basename(dst_path)) must_filter = get_name_filter(filter_this, include_this) render_tmpl = get_jinja_renderer(src_path, data, envops) for folder, subs, files in os.walk(src_path): rel_folder = folder.replace(src_path, '').lstrip(os.path.sep) rel_folder = parametrize_path(rel_folder, data) if must_filter(rel_folder): continue for src_name in files: dst_name = re.sub(r'\.tmpl$', '', src_name) dst_name = parametrize_path(dst_name, data) rel_path = os.path.join(rel_folder, dst_name) if must_filter(rel_path): continue render_file( dst_path, rel_folder, folder, src_name, dst_name, render_tmpl, pretend=pretend, force=force, skip=skip, quiet=quiet )
def get_user_data(src_path, force, quiet): """Query to user for information needed as per the template's ``vooodoo.json``. """ json_path = os.path.join(src_path, VOODOO_JSON_FILE) if not os.path.exists(json_path): return {} json_src = read_file(json_path) try: # Load the default user data in order def_user_data = json.loads(json_src, object_pairs_hook=OrderedDict) except ValueError as e: if not quiet: print_format('Invalid `voodoo.json`', color=COLOR_WARNING) print(e) def_user_data = {} user_data = {} if force: return def_user_data print('\n' + '-' * 50 + '\n') for key, value in def_user_data.items(): resp = prompt('{0}?'.format(key), value) user_data[key] = to_unicode(resp).decode('utf8') print('\n' + '-' * 50 + '\n') try: os.remove(json_path) except OSError: pass return user_data
def render_skeleton( src_path, dst_path, data=None, filter_this=None, include_this=None, pretend=False, force=False, skip=False, quiet=False, envops=None): """ Uses the template in src_path to generate the scaffold/skeleton at dst_path src_path: Absolute path to the project skeleton. May be a version control system URL dst_path: Absolute path to where to render the skeleton data: Data to be passed to the templates, as context. filter_this: A list of names or shell-style patterns matching files or folders that musn't be copied. The default is: ``['.*', '~*', '*.py[co]']`` include_this: A list of names or shell-style patterns matching files or folders that must be included, even if its name are in the `filter_this` list. Eg: ``['.gitignore']``. The default is an empty list. pretend: Run but do not make any changes force: Overwrite files that already exist, without asking skip: Skip files that already exist, without asking quiet: Suppress the status output envops: Extra options for the Jinja template environment. """ src_path = to_unicode(src_path) vcs = get_vcs_from_url(src_path) if filter_this is None: filter_this = DEFAULT_FILTER if include_this is None: include_this = DEFAULT_INCLUDE try: if vcs: src_path = clone(vcs, quiet) if not src_path: return data = data or {} user_data = get_user_data(src_path, force, quiet) data.update(user_data) render_local_skeleton( src_path, dst_path, data=data, filter_this=filter_this, include_this=include_this, pretend=pretend, force=force, skip=skip, quiet=quiet, envops=envops) run_setup(dst_path) finally: if src_path and vcs: shutil.rmtree(src_path)