예제 #1
0
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
예제 #2
0
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)
예제 #3
0
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
            )
예제 #4
0
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
예제 #5
0
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)