cli.py
Command line entrypoint. This module declares the Copier CLI applications.
Basically, there are 3 different commands you can run:
-
copier
, the main app, which is a shortcut for thecopy
andupdate
subapps.If the destination project is found and has an answers file with enough information, it will become a shortcut for
copier update
.Otherwise it will be a shortcut for
copier copy
.Example
# Copy a new project copier gh:copier-org/autopretty my-project # Update it cd my-project copier
-
copier copy
, used to bootstrap a new project from a template.Example
copier copy gh:copier-org/autopretty my-project
-
copier update
to update a preexisting project to a newer version of its template.Example
copier update
Below are the docs of each one of those.
CLI help generated from copier --help-all
:
copier 6.0.0
Create a new project from a template.
Docs in https://copier.readthedocs.io/
WARNING! Use only trusted project templates, as they might execute code with the
same level of access as your user.
Usage:
copier [MAIN_SWITCHES] [copy] [SUB_SWITCHES] template_src destination_path
copier [MAIN_SWITCHES] [update] [SUB_SWITCHES] [destination_path]
Meta-switches:
-h, --help Prints this help message and quits
--help-all Prints help messages of all sub-commands and
quits
-v, --version Prints the program's version and quits
Switches:
-a, --answers-file VALUE:str Update using this path (relative to
`destination_path`) to find the answers file
-d, --data VARIABLE=VALUE:str Make VARIABLE available as VALUE when
rendering the template; may be given
multiple times
-f, --force Same as `--defaults --overwrite`.
-g, --prereleases Use prereleases to compare template VCS
tags.
-l, --defaults Use default answers to questions, which
might be null if not specified.
-n, --pretend Run but do not make any changes
-q, --quiet Suppress status output
-r, --vcs-ref VALUE:str Git reference to checkout in `template_src`.
If you do not specify it, it will try to
checkout the latest git tag, as sorted using
the PEP 440 algorithm. If you want to
checkout always the latest version, use
`--vcs-ref=HEAD`.
-s, --skip VALUE:str Skip specified files if they exist already;
may be given multiple times
-w, --overwrite Overwrite files that already exist, without
asking.
-x, --exclude VALUE:str A name or shell-style pattern matching files
or folders that must not be copied; may be
given multiple times
Sub-commands:
copy Copy from a template source to a
destination.; see 'copier copy --help' for
more info
update Update a copy from its original template;
see 'copier update --help' for more info
copier copy 6.0.0
Copy from a template source to a destination.
Usage:
copier copy [SWITCHES] args...
Hidden-switches:
-h, --help Prints this help message and quits
--help-all Prints help messages of all sub-commands and quits
-v, --version Prints the program's version and quits
Switches:
-C, --no-cleanup On error, do not delete destination if it was created
by Copier.
copier update 6.0.0
Update a copy from its original template
The copy must have a valid answers file which contains info from the last Copier
execution, including the source template (it must be a key called `_src_path`).
If that file contains also `_commit` and `destination_path` is a git repository,
this command will do its best to respect the diff that you have generated since
the last `copier` execution. To avoid that, use `copier copy` instead.
Usage:
copier update [SWITCHES] args...
Hidden-switches:
-h, --help Prints this help message and quits
--help-all Prints help messages of all sub-commands and quits
-v, --version Prints the program's version and quits
CopierApp (Application)
¶
The Copier CLI application.
Attributes:
Name | Type | Description |
---|---|---|
answers_file |
SwitchAttr |
Set answers_file option. |
exclude |
SwitchAttr |
Set exclude option. |
vcs_ref |
SwitchAttr |
Set vcs_ref option. |
pretend |
Flag |
Set pretend option. |
force |
Flag |
Set force option. |
defaults |
Flag |
Set defaults option. |
overwrite |
Flag |
Set overwrite option. |
skip |
Flag |
Set skip_if_exists option. |
prereleases |
Flag |
Set use_prereleases option. |
quiet |
Flag |
Set quiet option. |
Source code in copier/cli.py
class CopierApp(cli.Application):
"""The Copier CLI application.
Attributes:
answers_file: Set [answers_file][] option.
exclude: Set [exclude][] option.
vcs_ref: Set [vcs_ref][] option.
pretend: Set [pretend][] option.
force: Set [force][] option.
defaults: Set [defaults][] option.
overwrite: Set [overwrite][] option.
skip: Set [skip_if_exists][] option.
prereleases: Set [use_prereleases][] option.
quiet: Set [quiet][] option.
"""
DESCRIPTION = "Create a new project from a template."
DESCRIPTION_MORE = (
dedent(
"""\
Docs in https://copier.readthedocs.io/
"""
)
+ (
colors.yellow
| dedent(
"""\
WARNING! Use only trusted project templates, as they might
execute code with the same level of access as your user.\n
"""
)
)
)
USAGE = dedent(
"""\
copier [MAIN_SWITCHES] [copy] [SUB_SWITCHES] template_src destination_path
copier [MAIN_SWITCHES] [update] [SUB_SWITCHES] [destination_path]
"""
)
VERSION = copier_version()
CALL_MAIN_IF_NESTED_COMMAND = False
data: AnyByStrDict = {}
answers_file: cli.SwitchAttr = cli.SwitchAttr(
["-a", "--answers-file"],
default=None,
help=(
"Update using this path (relative to `destination_path`) "
"to find the answers file"
),
)
exclude: cli.SwitchAttr = cli.SwitchAttr(
["-x", "--exclude"],
str,
list=True,
help=(
"A name or shell-style pattern matching files or folders "
"that must not be copied"
),
)
vcs_ref: cli.SwitchAttr = cli.SwitchAttr(
["-r", "--vcs-ref"],
str,
help=(
"Git reference to checkout in `template_src`. "
"If you do not specify it, it will try to checkout the latest git tag, "
"as sorted using the PEP 440 algorithm. If you want to checkout always "
"the latest version, use `--vcs-ref=HEAD`."
),
)
pretend: cli.Flag = cli.Flag(
["-n", "--pretend"], help="Run but do not make any changes"
)
force: cli.Flag = cli.Flag(
["-f", "--force"],
help="Same as `--defaults --overwrite`.",
)
defaults: cli.Flag = cli.Flag(
["-l", "--defaults"],
help="Use default answers to questions, which might be null if not specified.",
)
overwrite: cli.Flag = cli.Flag(
["-w", "--overwrite"],
help="Overwrite files that already exist, without asking.",
)
skip: cli.Flag = cli.SwitchAttr(
["-s", "--skip"],
str,
list=True,
help="Skip specified files if they exist already",
)
quiet: cli.Flag = cli.Flag(["-q", "--quiet"], help="Suppress status output")
prereleases: cli.Flag = cli.Flag(
["-g", "--prereleases"],
help="Use prereleases to compare template VCS tags.",
)
@cli.switch(
["-d", "--data"],
str,
"VARIABLE=VALUE",
list=True,
help="Make VARIABLE available as VALUE when rendering the template",
)
def data_switch(self, values: List[str]) -> None:
"""Update [data][] with provided values.
Arguments:
values: The list of values to apply.
Each value in the list is of the following form: `NAME=VALUE`.
"""
for arg in values:
key, value = arg.split("=", 1)
value = yaml.safe_load(value)
self.data[key] = value
def _worker(self, src_path: OptStr = None, dst_path: str = ".", **kwargs) -> Worker:
"""
Run Copier's internal API using CLI switches.
Arguments:
src_path: The source path of the template to generate the project from.
dst_path: The path to generate the project to.
**kwargs: Arguments passed to [Worker][copier.main.Worker].
"""
return Worker(
data=self.data,
dst_path=Path(dst_path),
answers_file=self.answers_file,
exclude=self.exclude,
defaults=self.force or self.defaults,
overwrite=self.force or self.overwrite,
pretend=self.pretend,
quiet=self.quiet,
src_path=src_path,
vcs_ref=self.vcs_ref,
use_prereleases=self.prereleases,
**kwargs,
)
@handle_exceptions
def main(self, *args: str) -> int:
"""Copier CLI app shortcuts.
This method redirects abstract CLI calls
(those that don't specify `copy` or `update`)
to [CopierCopySubApp.main][copier.cli.CopierCopySubApp.main]
or [CopierUpdateSubApp.main][copier.cli.CopierUpdateSubApp.main]
automatically.
Examples:
- `copier from to` → `copier copy from to`
- `copier from` → `copier update from`
- `copier` → `copier update .`
"""
# Redirect to subcommand if supplied
if args and args[0] in self._subcommands:
self.nested_command = (
self._subcommands[args[0]].subapplication,
["copier %s" % args[0]] + list(args[1:]),
)
# If using 0 or 1 args, you want to update
elif len(args) in {0, 1}:
self.nested_command = (
self._subcommands["update"].subapplication,
["copier update"] + list(args),
)
# If using 2 args, you want to copy
elif len(args) == 2:
self.nested_command = (
self._subcommands["copy"].subapplication,
["copier copy"] + list(args),
)
# If using more args, you're wrong
else:
self.help()
raise UserMessageError("Unsupported arguments")
return 0
data_switch(self, values)
¶
Update data with provided values.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
values |
List[str] |
The list of values to apply.
Each value in the list is of the following form: |
required |
Source code in copier/cli.py
@cli.switch(
["-d", "--data"],
str,
"VARIABLE=VALUE",
list=True,
help="Make VARIABLE available as VALUE when rendering the template",
)
def data_switch(self, values: List[str]) -> None:
"""Update [data][] with provided values.
Arguments:
values: The list of values to apply.
Each value in the list is of the following form: `NAME=VALUE`.
"""
for arg in values:
key, value = arg.split("=", 1)
value = yaml.safe_load(value)
self.data[key] = value
main(self, *args)
¶
Copier CLI app shortcuts.
This method redirects abstract CLI calls
(those that don't specify copy
or update
)
to CopierCopySubApp.main
or CopierUpdateSubApp.main
automatically.
Examples:
copier from to
→copier copy from to
copier from
→copier update from
copier
→copier update .
Source code in copier/cli.py
@handle_exceptions
def main(self, *args: str) -> int:
"""Copier CLI app shortcuts.
This method redirects abstract CLI calls
(those that don't specify `copy` or `update`)
to [CopierCopySubApp.main][copier.cli.CopierCopySubApp.main]
or [CopierUpdateSubApp.main][copier.cli.CopierUpdateSubApp.main]
automatically.
Examples:
- `copier from to` → `copier copy from to`
- `copier from` → `copier update from`
- `copier` → `copier update .`
"""
# Redirect to subcommand if supplied
if args and args[0] in self._subcommands:
self.nested_command = (
self._subcommands[args[0]].subapplication,
["copier %s" % args[0]] + list(args[1:]),
)
# If using 0 or 1 args, you want to update
elif len(args) in {0, 1}:
self.nested_command = (
self._subcommands["update"].subapplication,
["copier update"] + list(args),
)
# If using 2 args, you want to copy
elif len(args) == 2:
self.nested_command = (
self._subcommands["copy"].subapplication,
["copier copy"] + list(args),
)
# If using more args, you're wrong
else:
self.help()
raise UserMessageError("Unsupported arguments")
return 0
CopierCopySubApp (Application)
¶
The copier copy
subcommand.
Use this subcommand to bootstrap a new subproject from a template, or to override a preexisting subproject ignoring its history diff.
Attributes:
Name | Type | Description |
---|---|---|
cleanup_on_error |
Flag |
Set cleanup_on_error option. |
Source code in copier/cli.py
class CopierCopySubApp(cli.Application):
"""The `copier copy` subcommand.
Use this subcommand to bootstrap a new subproject from a template, or to override
a preexisting subproject ignoring its history diff.
Attributes:
cleanup_on_error: Set [cleanup_on_error][] option.
"""
DESCRIPTION = "Copy from a template source to a destination."
cleanup_on_error: cli.Flag = cli.Flag(
["-C", "--no-cleanup"],
default=True,
help="On error, do not delete destination if it was created by Copier.",
)
@handle_exceptions
def main(self, template_src: str, destination_path: str) -> int:
"""Call [run_copy][copier.main.Worker.run_copy].
Params:
template_src:
Indicate where to get the template from.
This can be a git URL or a local path.
destination_path:
Where to generate the new subproject. It must not exist or be empty.
"""
self.parent._worker(
template_src,
destination_path,
cleanup_on_error=self.cleanup_on_error,
).run_copy()
return 0
main(self, template_src, destination_path)
¶
Call run_copy.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
template_src |
str |
Indicate where to get the template from. This can be a git URL or a local path. |
required |
destination_path |
str |
Where to generate the new subproject. It must not exist or be empty. |
required |
Source code in copier/cli.py
@handle_exceptions
def main(self, template_src: str, destination_path: str) -> int:
"""Call [run_copy][copier.main.Worker.run_copy].
Params:
template_src:
Indicate where to get the template from.
This can be a git URL or a local path.
destination_path:
Where to generate the new subproject. It must not exist or be empty.
"""
self.parent._worker(
template_src,
destination_path,
cleanup_on_error=self.cleanup_on_error,
).run_copy()
return 0
CopierUpdateSubApp (Application)
¶
The copier update
subcommand.
Use this subcommand to update an existing subproject from a template that supports updates.
Source code in copier/cli.py
class CopierUpdateSubApp(cli.Application):
"""The `copier update` subcommand.
Use this subcommand to update an existing subproject from a template
that supports updates.
"""
DESCRIPTION = "Update a copy from its original template"
DESCRIPTION_MORE = dedent(
"""\
The copy must have a valid answers file which contains info
from the last Copier execution, including the source template
(it must be a key called `_src_path`).
If that file contains also `_commit` and `destination_path` is a git
repository, this command will do its best to respect the diff that you have
generated since the last `copier` execution. To avoid that, use `copier copy`
instead.
"""
)
@handle_exceptions
def main(self, destination_path: cli.ExistingDirectory = ".") -> int:
"""Call [run_update][copier.main.Worker.run_update].
Parameters:
destination_path:
Only the destination path is needed to update, because the
`src_path` comes from [the answers file][the-copier-answersyml-file].
The subproject must exist. If not specified, the currently
working directory is used.
"""
self.parent._worker(
dst_path=destination_path,
).run_update()
return 0
main(self, destination_path='.')
¶
Call run_update.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
destination_path |
ExistingDirectory |
Only the destination path is needed to update, because the
The subproject must exist. If not specified, the currently working directory is used. |
'.' |
Source code in copier/cli.py
@handle_exceptions
def main(self, destination_path: cli.ExistingDirectory = ".") -> int:
"""Call [run_update][copier.main.Worker.run_update].
Parameters:
destination_path:
Only the destination path is needed to update, because the
`src_path` comes from [the answers file][the-copier-answersyml-file].
The subproject must exist. If not specified, the currently
working directory is used.
"""
self.parent._worker(
dst_path=destination_path,
).run_update()
return 0
handle_exceptions(method)
¶
Handle keyboard interruption while running a method.
Source code in copier/cli.py
def handle_exceptions(method):
"""Handle keyboard interruption while running a method."""
@wraps(method)
def _wrapper(*args, **kwargs):
try:
try:
return method(*args, **kwargs)
except KeyboardInterrupt:
raise UserMessageError("Execution stopped by user")
except UserMessageError as error:
print(colors.red | "\n".join(error.args), file=sys.stderr)
return 1
return _wrapper