Skip to content

tools.py

Some utility functions.

Renderer

The Jinja template renderer.

create_path_filter(patterns)

Returns a function that matches a path against given patterns.

Source code in copier/tools.py
175
176
177
178
179
180
181
182
183
def create_path_filter(patterns: StrOrPathSeq) -> CheckPathFunc:
    """Returns a function that matches a path against given patterns."""
    patterns = [normalize_str(p) for p in patterns]
    spec = pathspec.PathSpec.from_lines("gitwildmatch", patterns)

    def match(path: StrOrPath) -> bool:
        return spec.match_file(str(path))

    return match

get_jinja_env(envops, filters=None, paths=None, **kwargs)

Return a pre-configured Jinja environment.

Source code in copier/tools.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def get_jinja_env(
    envops: EnvOps,
    filters: Optional[Filters] = None,
    paths: Optional[LoaderPaths] = None,
    **kwargs: Any,
) -> SandboxedEnvironment:
    """Return a pre-configured Jinja environment."""
    loader = FileSystemLoader(paths) if paths else None
    # We want to minimize the risk of hidden malware in the templates
    # so we use the SandboxedEnvironment instead of the regular one.
    # Of couse we still have the post-copy tasks to worry about, but at least
    # they are more visible to the final user.
    env = SandboxedEnvironment(loader=loader, **envops.dict(), **kwargs)
    default_filters = {"to_nice_yaml": to_nice_yaml}
    default_filters.update(filters or {})
    env.filters.update(default_filters)
    return env

get_migration_tasks(conf, stage)

Get migration objects that match current version spec.

Versions are compared using PEP 440.

Source code in copier/tools.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def get_migration_tasks(conf: ConfigData, stage: str) -> List[Dict]:
    """Get migration objects that match current version spec.

    Versions are compared using PEP 440.
    """
    result: List[Dict] = []
    if not conf.old_commit or not conf.commit:
        return result
    vfrom = version.parse(conf.old_commit)
    vto = version.parse(conf.commit)
    extra_env = {
        "STAGE": stage,
        "VERSION_FROM": conf.old_commit,
        "VERSION_TO": conf.commit,
    }
    for migration in conf.migrations:
        if vto >= version.parse(migration.version) > vfrom:
            extra_env = dict(extra_env, VERSION_CURRENT=str(migration.version))
            result += [
                {"task": task, "extra_env": extra_env}
                for task in migration.dict().get(stage, [])
            ]
    return result

normalize_str(text, form='NFD')

Normalize unicode text. Uses the NFD algorithm by default.

Source code in copier/tools.py
170
171
172
def normalize_str(text: StrOrPath, form: str = "NFD") -> str:
    """Normalize unicode text. Uses the NFD algorithm by default."""
    return unicodedata.normalize(form, str(text))

to_nice_yaml(data, **kwargs)

Dump a string to pretty YAML.

Source code in copier/tools.py
101
102
103
104
105
106
107
108
109
def to_nice_yaml(data: Any, **kwargs) -> str:
    """Dump a string to pretty YAML."""
    # Remove security-problematic kwargs
    kwargs.pop("stream", None)
    kwargs.pop("Dumper", None)
    result = safe_dump(data, **kwargs)
    if isinstance(result, str):
        result = result.rstrip()
    return result or ""