Skip to content

factory.py

Functions used to generate configuration data.

filter_config(data)

Separates config and questions data.

Source code in copier/config/factory.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def filter_config(data: AnyByStrDict) -> Tuple[AnyByStrDict, AnyByStrDict]:
    """Separates config and questions data."""
    conf_data: AnyByStrDict = {"secret_questions": set()}
    questions_data = {}
    for k, v in data.items():
        if k == "_secret_questions":
            conf_data["secret_questions"].update(v)
        elif k.startswith("_"):
            conf_data[k[1:]] = v
        else:
            # Transform simplified questions format into complex
            if not isinstance(v, dict):
                v = {"default": v}
            questions_data[k] = v
            if v.get("secret"):
                conf_data["secret_questions"].add(k)
    return conf_data, questions_data

make_config(src_path=None, dst_path='.', *, answers_file=None, exclude=None, skip_if_exists=None, tasks=None, envops=None, extra_paths=None, pretend=None, force=None, skip=None, quiet=None, cleanup_on_error=None, vcs_ref=None, subdirectory=None, use_prereleases=False, **kwargs)

Provides the configuration object, merged from the different sources.

The order of precedence for the merger of configuration objects is: function_args > user_data > defaults.

Source code in copier/config/factory.py
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def make_config(
    src_path: OptStr = None,
    dst_path: str = ".",
    *,
    answers_file: OptStr = None,
    exclude: OptStrSeq = None,
    skip_if_exists: OptStrSeq = None,
    tasks: OptStrSeq = None,
    envops: OptAnyByStrDict = None,
    extra_paths: OptStrSeq = None,
    pretend: OptBool = None,
    force: OptBool = None,
    skip: OptBool = None,
    quiet: OptBool = None,
    cleanup_on_error: OptBool = None,
    vcs_ref: OptStr = None,
    subdirectory: OptStr = None,
    use_prereleases: OptBool = False,
    **kwargs,
) -> ConfigData:
    """Provides the configuration object, merged from the different sources.

    The order of precedence for the merger of configuration objects is:
    function_args > user_data > defaults.
    """
    # These args are provided by API or CLI call
    init_args = {k: v for k, v in locals().items() if v is not None and v != []}
    # Store different answer sources
    init_args["data_from_answers_file"] = load_answersfile_data(dst_path, answers_file)
    if "_commit" in init_args["data_from_answers_file"]:
        init_args["old_commit"] = init_args["data_from_answers_file"]["_commit"]
    # Detect original source if running in update mode
    if src_path is None:
        try:
            src_path = init_args["data_from_answers_file"]["_src_path"]
        except KeyError:
            raise NoSrcPathError(
                "No copier answers file found, or it didn't include "
                "original template information (_src_path). "
                "Run `copier copy` instead."
            )
    init_args["original_src_path"] = src_path
    if src_path:
        repo = vcs.get_repo(src_path)
        if repo:
            src_path = vcs.clone(repo, vcs_ref or "HEAD")
            vcs_ref = vcs_ref or vcs.checkout_latest_tag(src_path, use_prereleases)
            with local.cwd(src_path):
                init_args["commit"] = git("describe", "--tags", "--always").strip()
        init_args["src_path"] = src_path
    # Obtain config and query data, asking the user if needed
    file_data = load_config_data(src_path, quiet=True)

    try:
        verify_minimum_version(file_data["_min_copier_version"])
    except KeyError:
        pass

    template_config_data, questions_data = filter_config(file_data)
    init_args["data_from_template_defaults"] = {
        k: v.get("default") for k, v in questions_data.items()
    }
    init_args["envops"] = EnvOps(**template_config_data.get("envops", {}))
    data = kwargs.get("data") or {}
    init_args["data_from_init"] = ChainMap(
        query_user_data(
            {k: v for k, v in questions_data.items() if k in data},
            {},
            data,
            False,
            init_args["envops"],
        ),
        data,
    )
    init_args["data_from_asking_user"] = query_user_data(
        questions_data,
        init_args["data_from_answers_file"],
        init_args["data_from_init"],
        not force,
        init_args["envops"],
    )
    return ConfigData(**ChainMap(init_args, template_config_data))

verify_minimum_version(version_str)

Raise an error if the current Copier version is less than the given version.

Source code in copier/config/factory.py
37
38
39
40
41
42
43
44
45
46
47
48
def verify_minimum_version(version_str: str) -> None:
    """Raise an error if the current Copier version is less than the given version."""
    # Importing __version__ at the top of the module creates a circular import
    # ("cannot import name '__version__' from partially initialized module 'copier'"),
    # so instead we do a lazy import here
    from .. import __version__

    if version.parse(__version__) < version.parse(version_str):
        raise UserMessageError(
            f"This template requires Copier version >= {version_str}, "
            f"while your version of Copier is {__version__}."
        )