mirror of
https://github.com/copier-org/copier.git
synced 2025-05-05 15:32:54 +00:00
Run pre-commit for the 1st time
OK, now this diff is very big, but it's actually modifying only style, not code functions themselves. From now on, this won't have to happen again. Basically I just ran `pre-commit run --all-files` and committed that. I hope you like how it looks now!
This commit is contained in:
parent
2ff11656e2
commit
0a952b7680
@ -10,7 +10,7 @@ cache:
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- python: 'nightly'
|
||||
- python: "nightly"
|
||||
|
||||
before_install:
|
||||
- pip install -U pip coveralls
|
||||
|
70
CHANGELOG.md
70
CHANGELOG.md
@ -3,60 +3,60 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||
|
||||
|
||||
### Version 3.0 (2019-xx)
|
||||
- Dropped support for Python 3.5.
|
||||
- Dropped support for deprecated `voodoo.json`.
|
||||
- Type annotated entire code base.
|
||||
|
||||
- Dropped support for Python 3.5.
|
||||
- Dropped support for deprecated `voodoo.json`.
|
||||
- Type annotated entire code base.
|
||||
|
||||
### Version 2.5 (2019-06)
|
||||
- Expanduser on all paths (so "~/foo/bar" is expanded to "<YOUR_HOME_FOLDER>/foo/bar").
|
||||
- Improve the output when running tasks.
|
||||
- Remove the destination folder if the copy process or one of the tasks fail.
|
||||
- Add a `cleanup_on_error` flag to optionally disable the cleanup feature.
|
||||
- Add the `skip_if_exists` option to skip files, without asking, if they already exists in the destination folder.
|
||||
|
||||
- Expanduser on all paths (so "~/foo/bar" is expanded to "<YOUR_HOME_FOLDER>/foo/bar").
|
||||
- Improve the output when running tasks.
|
||||
- Remove the destination folder if the copy process or one of the tasks fail.
|
||||
- Add a `cleanup_on_error` flag to optionally disable the cleanup feature.
|
||||
- Add the `skip_if_exists` option to skip files, without asking, if they already exists in the destination folder.
|
||||
|
||||
### Version 2.4.2 (2019-06)
|
||||
- Fix MAJOR bug that was preventing the `_exclude`, `_include` and `_tasks` keys from
|
||||
`copier.yml` (or alternatives) to be used at all. It also interpreted `_tasks` as
|
||||
a user-provided variable.
|
||||
|
||||
- Fix MAJOR bug that was preventing the `_exclude`, `_include` and `_tasks` keys from
|
||||
`copier.yml` (or alternatives) to be used at all. It also interpreted `_tasks` as
|
||||
a user-provided variable.
|
||||
|
||||
### Version 2.4 (2019-06)
|
||||
- Empty folders are now copied. The folders are also displayed in the console output
|
||||
instead of just the files.
|
||||
- `prompt_bool` can now have an undefined default (ans answer is mandatory in that case).
|
||||
- Reactivates the `copier.yml` and `copier.yaml` as configuration files.
|
||||
- The new `extra_paths` argument specifies additional paths to find templates to inherit from.
|
||||
|
||||
- Empty folders are now copied. The folders are also displayed in the console output
|
||||
instead of just the files.
|
||||
- `prompt_bool` can now have an undefined default (ans answer is mandatory in that case).
|
||||
- Reactivates the `copier.yml` and `copier.yaml` as configuration files.
|
||||
- The new `extra_paths` argument specifies additional paths to find templates to inherit from.
|
||||
|
||||
### Version 2.3 (2019-04)
|
||||
- Back to using a setup.py intead of a pyproject.toml.
|
||||
- The recommended configuration file is now `copier.toml`.
|
||||
|
||||
- Back to using a setup.py intead of a pyproject.toml.
|
||||
- The recommended configuration file is now `copier.toml`.
|
||||
|
||||
### Version 2.2 (2019-04)
|
||||
- The `copier` command-line script now accepts "help" and "version" as commands.
|
||||
|
||||
- The `copier` command-line script now accepts "help" and "version" as commands.
|
||||
|
||||
### Version 2.1 (2019-02)
|
||||
- Task runner 🎉.
|
||||
- Use `_exclude`, `_include`, and `_tasks` keys in `copier.yml` as the default
|
||||
values for the `.copy()` arguments `exclude`, `include`, and `tasks`.
|
||||
|
||||
- Task runner 🎉.
|
||||
- Use `_exclude`, `_include`, and `_tasks` keys in `copier.yml` as the default
|
||||
values for the `.copy()` arguments `exclude`, `include`, and `tasks`.
|
||||
|
||||
### Version 2.0 (2019-02)
|
||||
- Rebranded from `Voodoo` to `Copier`!
|
||||
- Dropped support for Python 2.x, the minimal version is now Python 3.5.
|
||||
- Cleanup and 100% test coverage.
|
||||
- The recommended configuration file is now `copier.yaml`, but a `copier.json`
|
||||
can be used as well. The old `voodoo.json` is also supported *for now* but is
|
||||
deprecated and will be removed in version 2.2.
|
||||
- Python package format updated to the latest standard (no `setup.py` 😵).
|
||||
- Renamed the `render_skeleton()` function to `copy()`. The function signature remains
|
||||
almost the same, the only changes are:
|
||||
- `filter_this` parameter is now called `exclude`.
|
||||
- `ignore_this` parameter is now called just `ignore`.
|
||||
- Dropped the idea of storing the templates in a hidden `$HOME` folder.
|
||||
|
||||
- Rebranded from `Voodoo` to `Copier`!
|
||||
- Dropped support for Python 2.x, the minimal version is now Python 3.5.
|
||||
- Cleanup and 100% test coverage.
|
||||
- The recommended configuration file is now `copier.yaml`, but a `copier.json`
|
||||
can be used as well. The old `voodoo.json` is also supported _for now_ but is
|
||||
deprecated and will be removed in version 2.2.
|
||||
- Python package format updated to the latest standard (no `setup.py` 😵).
|
||||
- Renamed the `render_skeleton()` function to `copy()`. The function signature remains
|
||||
almost the same, the only changes are:
|
||||
- `filter_this` parameter is now called `exclude`.
|
||||
- `ignore_this` parameter is now called just `ignore`.
|
||||
- Dropped the idea of storing the templates in a hidden `$HOME` folder.
|
||||
|
@ -55,7 +55,7 @@ Ready to contribute? Here's how to set up the project for local development.
|
||||
git clone git@github.com:jpscaletti/copier.git
|
||||
```
|
||||
|
||||
3. Install your local copy into a virtualenv.
|
||||
3. Install your local copy into a virtualenv.
|
||||
|
||||
```bash
|
||||
python -m virtualenv .venv
|
||||
@ -79,7 +79,7 @@ flake8 .
|
||||
```
|
||||
|
||||
To have multiple Python versions on the same machine for running `tox`, I recommend
|
||||
using [pyenv](https://github.com/pyenv/pyenv) (*do not* confuse it with `pipenv`,).
|
||||
using [pyenv](https://github.com/pyenv/pyenv) (_do not_ confuse it with `pipenv`,).
|
||||
|
||||
7. Commit your changes and push your branch to GitHub:
|
||||
|
||||
@ -91,7 +91,6 @@ git push origin name-of-your-bugfix-or-feature
|
||||
|
||||
8. Submit a pull request through the GitHub website.
|
||||
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
Before you submit a pull request, check that it meets these guidelines:
|
||||
|
174
README.md
174
README.md
@ -6,15 +6,15 @@
|
||||
|
||||
A library for rendering projects templates.
|
||||
|
||||
- Works with **local** paths and **git URLs**.
|
||||
- Your project can include any file and `Copier` can dynamically replace values in any kind of text files.
|
||||
- It generates a beautiful output and takes care of not overwrite existing files unless instructed to do so.
|
||||
- Works with **local** paths and **git URLs**.
|
||||
- Your project can include any file and `Copier` can dynamically replace values in any kind of text files.
|
||||
- It generates a beautiful output and takes care of not overwrite existing files unless instructed to do so.
|
||||
|
||||

|
||||
|
||||
## How to use
|
||||
|
||||
- Use it in your Python code:
|
||||
- Use it in your Python code:
|
||||
|
||||
```python
|
||||
from copier import copy
|
||||
@ -32,7 +32,7 @@ copy("gh:pykong/copier.git", "path/to/destination")
|
||||
copy("gl:pykong/copier.git", "path/to/destination")
|
||||
```
|
||||
|
||||
- Or as a command-line tool:
|
||||
- Or as a command-line tool:
|
||||
|
||||
```bash
|
||||
copier path/to/project/template path/to/destination
|
||||
@ -110,14 +110,14 @@ overwrite them.
|
||||
```yaml
|
||||
# Shell-style patterns files/folders that must not be copied.
|
||||
_exclude:
|
||||
- "*.bar"
|
||||
- ".git"
|
||||
- ".git/*"
|
||||
- "*.bar"
|
||||
- ".git"
|
||||
- ".git/*"
|
||||
|
||||
# Shell-style patterns files/folders that *must be* copied, even if
|
||||
# they are in the exclude list
|
||||
_include:
|
||||
- "foo.bar"
|
||||
- "foo.bar"
|
||||
|
||||
# Shell-style patterns files to skip, without asking, if they already exists
|
||||
# in the destination folder
|
||||
@ -125,12 +125,12 @@ _skip_if_exists:
|
||||
|
||||
# Commands to be executed after the copy
|
||||
_tasks:
|
||||
- "git init"
|
||||
- "rm [[ name_of_the_project ]]/README.md"
|
||||
- "git init"
|
||||
- "rm [[ name_of_the_project ]]/README.md"
|
||||
|
||||
# Additional paths, from where to search for templates
|
||||
_extra_paths:
|
||||
- ~/Projects/templates
|
||||
- ~/Projects/templates
|
||||
```
|
||||
|
||||
**Warning:** Use only trusted project templates as these tasks run with the
|
||||
@ -151,7 +151,7 @@ this content:
|
||||
|
||||
```yml
|
||||
# Changes here will be overwritten by Copier
|
||||
[[ _log|to_nice_yaml ]]
|
||||
[[_log|to_nice_yaml]]
|
||||
```
|
||||
|
||||
The builtin `_log` variable includes all data needed to smooth future updates
|
||||
@ -169,16 +169,16 @@ Copier includes:
|
||||
|
||||
### Builtin variables/functions
|
||||
|
||||
- `now()` to get current UTC time.
|
||||
- `make_secret()` to get a random string.
|
||||
- `now()` to get current UTC time.
|
||||
- `make_secret()` to get a random string.
|
||||
|
||||
### Builtin filters
|
||||
|
||||
- `anything|to_nice_yaml` to print as pretty-formatted YAML.
|
||||
- `anything|to_nice_yaml` to print as pretty-formatted YAML.
|
||||
|
||||
Without arguments it defaults to:
|
||||
`anything|to_nice_yaml(indent=2, width=80, allow_unicode=True)`,
|
||||
but you can modify those.
|
||||
Without arguments it defaults to:
|
||||
`anything|to_nice_yaml(indent=2, width=80, allow_unicode=True)`,
|
||||
but you can modify those.
|
||||
|
||||
---
|
||||
|
||||
@ -213,89 +213,89 @@ Uses the template in _src_path_ to generate a new project at _dst_path_.
|
||||
|
||||
**Arguments**:
|
||||
|
||||
- **src_path** (str):<br>
|
||||
Absolute path to the project skeleton. Can be a version control system URL.
|
||||
- **src_path** (str):<br>
|
||||
Absolute path to the project skeleton. Can be a version control system URL.
|
||||
|
||||
- **dst_path** (str):<br>
|
||||
Absolute path to where to render the skeleton.
|
||||
- **dst_path** (str):<br>
|
||||
Absolute path to where to render the skeleton.
|
||||
|
||||
- **data** (dict):<br>
|
||||
Data to be passed to the templates in addition to the user data from
|
||||
a `copier.yml`.
|
||||
- **data** (dict):<br>
|
||||
Data to be passed to the templates in addition to the user data from
|
||||
a `copier.yml`.
|
||||
|
||||
- **exclude** (list):<br>
|
||||
A list of names or shell-style patterns matching files or folders
|
||||
that must not be copied.
|
||||
- **exclude** (list):<br>
|
||||
A list of names or shell-style patterns matching files or folders
|
||||
that must not be copied.
|
||||
|
||||
To exclude a folder you should use **two** entries, one for the folder and
|
||||
the other for its content: `[".git", ".git/*"]`.
|
||||
To exclude a folder you should use **two** entries, one for the folder and
|
||||
the other for its content: `[".git", ".git/*"]`.
|
||||
|
||||
- **include** (list):<br>
|
||||
A list of names or shell-style patterns matching files or folders that
|
||||
must be included, even if its name is a match for the `exclude` list.
|
||||
Eg: `['.gitignore']`. The default is an empty list.
|
||||
- **include** (list):<br>
|
||||
A list of names or shell-style patterns matching files or folders that
|
||||
must be included, even if its name is a match for the `exclude` list.
|
||||
Eg: `['.gitignore']`. The default is an empty list.
|
||||
|
||||
- **skip_if_exists** (list):<br>
|
||||
Skip any of these files, without asking, if another with the same name already
|
||||
exists in the destination folder. (it only makes sense if you are copying to a
|
||||
folder that already exists).
|
||||
- **skip_if_exists** (list):<br>
|
||||
Skip any of these files, without asking, if another with the same name already
|
||||
exists in the destination folder. (it only makes sense if you are copying to a
|
||||
folder that already exists).
|
||||
|
||||
- **tasks** (list):<br>
|
||||
Optional lists of commands to run in order after finishing the copy. Like in
|
||||
the templates files, you can use variables on the commands that will be
|
||||
replaced by the real values before running the command. If one of the commands
|
||||
fails, the rest of them will not run.
|
||||
- **tasks** (list):<br>
|
||||
Optional lists of commands to run in order after finishing the copy. Like in
|
||||
the templates files, you can use variables on the commands that will be
|
||||
replaced by the real values before running the command. If one of the commands
|
||||
fails, the rest of them will not run.
|
||||
|
||||
- **envops** (dict):<br>
|
||||
Extra options for the Jinja template environment.
|
||||
See available options in
|
||||
[Jinja's docs](https://jinja.palletsprojects.com/en/2.10.x/api/#jinja2.Environment).
|
||||
- **envops** (dict):<br>
|
||||
Extra options for the Jinja template environment.
|
||||
See available options in
|
||||
[Jinja's docs](https://jinja.palletsprojects.com/en/2.10.x/api/#jinja2.Environment).
|
||||
|
||||
Copier uses these defaults that are different from Jinja's:
|
||||
Copier uses these defaults that are different from Jinja's:
|
||||
|
||||
```yml
|
||||
# copier.yml
|
||||
_envops:
|
||||
block_start_string: "[%"
|
||||
block_end_string: "%]"
|
||||
comment_start_string: "[#"
|
||||
comment_end_string: "#]"
|
||||
variable_start_string: "[["
|
||||
variable_end_string: "]]"
|
||||
keep_trailing_newline: true
|
||||
```
|
||||
```yml
|
||||
# copier.yml
|
||||
_envops:
|
||||
block_start_string: "[%"
|
||||
block_end_string: "%]"
|
||||
comment_start_string: "[#"
|
||||
comment_end_string: "#]"
|
||||
variable_start_string: "[["
|
||||
variable_end_string: "]]"
|
||||
keep_trailing_newline: true
|
||||
```
|
||||
|
||||
You can use default Jinja syntax with:
|
||||
You can use default Jinja syntax with:
|
||||
|
||||
```yml
|
||||
# copier.yml
|
||||
_envops:
|
||||
block_start_string: "{%"
|
||||
block_end_string: "%}"
|
||||
comment_start_string: "{#"
|
||||
comment_end_string: "#}"
|
||||
variable_start_string: "{{"
|
||||
variable_end_string: "}}"
|
||||
keep_trailing_newline: false
|
||||
```
|
||||
```yml
|
||||
# copier.yml
|
||||
_envops:
|
||||
block_start_string: "{%"
|
||||
block_end_string: "%}"
|
||||
comment_start_string: "{#"
|
||||
comment_end_string: "#}"
|
||||
variable_start_string: "{{"
|
||||
variable_end_string: "}}"
|
||||
keep_trailing_newline: false
|
||||
```
|
||||
|
||||
- **extra_paths** (list):<br>
|
||||
Additional paths, from where to search for templates. This is intended to be
|
||||
used with shared parent templates, files with macros, etc. outside the copied
|
||||
project skeleton.
|
||||
- **extra_paths** (list):<br>
|
||||
Additional paths, from where to search for templates. This is intended to be
|
||||
used with shared parent templates, files with macros, etc. outside the copied
|
||||
project skeleton.
|
||||
|
||||
- **pretend** (bool):<br>
|
||||
Run but do not make any changes.
|
||||
- **pretend** (bool):<br>
|
||||
Run but do not make any changes.
|
||||
|
||||
- **force** (bool):<br>
|
||||
Overwrite files that already exist, without asking.
|
||||
- **force** (bool):<br>
|
||||
Overwrite files that already exist, without asking.
|
||||
|
||||
- **skip** (bool):<br>
|
||||
Skip files that already exist, without asking.
|
||||
- **skip** (bool):<br>
|
||||
Skip files that already exist, without asking.
|
||||
|
||||
- **quiet** (bool):<br>
|
||||
Suppress the status output.
|
||||
- **quiet** (bool):<br>
|
||||
Suppress the status output.
|
||||
|
||||
- **cleanup_on_error** (bool):<br>
|
||||
Remove the destination folder if the copy process or one of the tasks fail.
|
||||
True by default.
|
||||
- **cleanup_on_error** (bool):<br>
|
||||
Remove the destination folder if the copy process or one of the tasks fail.
|
||||
True by default.
|
||||
|
@ -1,18 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
from plumbum import cli, colors
|
||||
import sys
|
||||
|
||||
from .types import OptStr, AnyByStrDict
|
||||
from plumbum import cli, colors
|
||||
|
||||
from .main import copy
|
||||
from .types import AnyByStrDict, OptStr
|
||||
from .version import __version__
|
||||
|
||||
|
||||
class CopierApp(cli.Application):
|
||||
DESCRIPTION = "Create a new project from a template."
|
||||
DESCRIPTION_MORE = colors.yellow | """
|
||||
DESCRIPTION_MORE = (
|
||||
colors.yellow
|
||||
| """
|
||||
WARNING! Use only trusted project templates, as they might
|
||||
execute code with the same level of access as your user.
|
||||
"""
|
||||
)
|
||||
USAGE = """
|
||||
copier [SWITCHES] [copy] template_src destination_path
|
||||
copier [SWITCHES] [update] [destination_path]
|
||||
@ -22,43 +26,46 @@ class CopierApp(cli.Application):
|
||||
data: AnyByStrDict = {}
|
||||
|
||||
extra_paths = cli.SwitchAttr(
|
||||
["-p", "--extra-paths"], str, list=True,
|
||||
help="Additional directories to find parent templates in")
|
||||
["-p", "--extra-paths"],
|
||||
str,
|
||||
list=True,
|
||||
help="Additional directories to find parent templates in",
|
||||
)
|
||||
exclude = cli.SwitchAttr(
|
||||
["-x", "--exclude"], str, list=True,
|
||||
["-x", "--exclude"],
|
||||
str,
|
||||
list=True,
|
||||
help=(
|
||||
"A name or shell-style pattern matching files or folders "
|
||||
"that must not be copied"
|
||||
),
|
||||
)
|
||||
include = cli.SwitchAttr(
|
||||
["-i", "--include"], str, list=True,
|
||||
["-i", "--include"],
|
||||
str,
|
||||
list=True,
|
||||
help=(
|
||||
"A name or shell-style pattern matching files or folders "
|
||||
"that must be included, even if their names are in the `exclude` list"
|
||||
),
|
||||
)
|
||||
|
||||
pretend = cli.Flag(
|
||||
["-n", "--pretend"],
|
||||
help="Run but do not make any changes",
|
||||
)
|
||||
pretend = cli.Flag(["-n", "--pretend"], help="Run but do not make any changes")
|
||||
force = cli.Flag(
|
||||
["-f", "--force"],
|
||||
help="Overwrite files that already exist, without asking",
|
||||
["-f", "--force"], help="Overwrite files that already exist, without asking"
|
||||
)
|
||||
skip = cli.Flag(
|
||||
["-s", "--skip"],
|
||||
help="Skip files that already exist, without asking",
|
||||
)
|
||||
quiet = cli.Flag(
|
||||
["-q", "--quiet"],
|
||||
help="Suppress status output",
|
||||
["-s", "--skip"], help="Skip files that already exist, without asking"
|
||||
)
|
||||
quiet = cli.Flag(["-q", "--quiet"], help="Suppress status output")
|
||||
|
||||
@cli.switch(
|
||||
["-d", "--data"], str, "VARIABLE=VALUE", list=True,
|
||||
help="Make VARIABLE available as VALUE when rendering the template")
|
||||
["-d", "--data"],
|
||||
str,
|
||||
"VARIABLE=VALUE",
|
||||
list=True,
|
||||
help="Make VARIABLE available as VALUE when rendering the template",
|
||||
)
|
||||
def data_switch(self, values):
|
||||
self.data.update(value.split("=", 1) for value in values)
|
||||
|
||||
@ -83,12 +90,14 @@ class CopierApp(cli.Application):
|
||||
if len(args) in {0, 1}:
|
||||
self.nested_command = (
|
||||
self._subcommands["update"].subapplication,
|
||||
["copier update"] + list(args))
|
||||
["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))
|
||||
["copier copy"] + list(args),
|
||||
)
|
||||
# If using more args, you're wrong
|
||||
else:
|
||||
self.help()
|
||||
|
@ -52,14 +52,11 @@ def make_config(
|
||||
raise NoSrcPathError(
|
||||
"No .copier-answers.yml file found, or it didn't include "
|
||||
"original template information (_src_path). "
|
||||
"Run `copier copy` instead.",
|
||||
"Run `copier copy` instead."
|
||||
)
|
||||
file_data = load_config_data(src_path, quiet=True)
|
||||
config_data, query_data = filter_config(file_data)
|
||||
query_data.update(filter(
|
||||
lambda item: item[0] in query_data,
|
||||
answers_data.items(),
|
||||
))
|
||||
query_data.update(filter(lambda item: item[0] in query_data, answers_data.items()))
|
||||
|
||||
if not force:
|
||||
query_data = query_user_data(query_data)
|
||||
|
@ -6,7 +6,7 @@ from typing import Any, Tuple
|
||||
|
||||
from pydantic import BaseModel, Extra, StrictBool, validator
|
||||
|
||||
from ..types import AnyByStrDict, PathSeq, StrOrPathSeq, StrSeq, OptStr
|
||||
from ..types import AnyByStrDict, OptStr, PathSeq, StrOrPathSeq, StrSeq
|
||||
|
||||
# Default list of files in the template to exclude from the rendered project
|
||||
DEFAULT_EXCLUDE: Tuple[str, ...] = (
|
||||
@ -39,7 +39,7 @@ class Flags(BaseModel):
|
||||
skip: StrictBool = False
|
||||
cleanup_on_error: StrictBool = True
|
||||
|
||||
@validator('skip', always=True)
|
||||
@validator("skip", always=True)
|
||||
def mutually_exclusive(cls, v, values):
|
||||
if v and values["force"]:
|
||||
raise ValueError(f"Flags `force` and `skip` are mutually exclusive.")
|
||||
|
@ -1,11 +1,11 @@
|
||||
from pathlib import Path
|
||||
from os.path import isfile
|
||||
import re
|
||||
from os.path import isfile
|
||||
from pathlib import Path
|
||||
|
||||
from ..tools import HLINE, INDENT, printf_exception, prompt
|
||||
from ..types import AnyByStrDict, StrOrPath, PathSeq
|
||||
from ..types import AnyByStrDict, PathSeq, StrOrPath
|
||||
|
||||
__all__ = ("load_config_data", "query_user_data",)
|
||||
__all__ = ("load_config_data", "query_user_data")
|
||||
|
||||
|
||||
class ConfigFileError(ValueError):
|
||||
@ -56,7 +56,8 @@ def load_config_data(
|
||||
"""Try to load the content from a `copier.yml` or a `copier.yaml` file.
|
||||
"""
|
||||
conf_paths = [
|
||||
p for p in Path(src_path).glob("copier.*")
|
||||
p
|
||||
for p in Path(src_path).glob("copier.*")
|
||||
if p.is_file() and re.match(r"\.ya?ml", p.suffix, re.I)
|
||||
]
|
||||
|
||||
@ -69,19 +70,21 @@ def load_config_data(
|
||||
|
||||
|
||||
def load_logfile_data(
|
||||
dst_path: StrOrPath,
|
||||
*,
|
||||
quiet: bool = False,
|
||||
_warning: bool = True
|
||||
dst_path: StrOrPath, *, quiet: bool = False, _warning: bool = True
|
||||
) -> AnyByStrDict:
|
||||
"""Load answers data from a `$dst_path/.copier-answers.yml` file if it exists.
|
||||
|
||||
`.yaml` suffix is also supported.
|
||||
"""
|
||||
answer_paths = list(filter(
|
||||
isfile,
|
||||
map(lambda suffix: Path(dst_path) / f".copier-answers.{suffix}", ("yml", "yaml")),
|
||||
))
|
||||
answer_paths = list(
|
||||
filter(
|
||||
isfile,
|
||||
map(
|
||||
lambda suffix: Path(dst_path) / f".copier-answers.{suffix}",
|
||||
("yml", "yaml"),
|
||||
),
|
||||
)
|
||||
)
|
||||
answers_data: AnyByStrDict = {}
|
||||
if len(answer_paths) > 1:
|
||||
raise MultipleAnswerFilesError(answer_paths, quiet=quiet)
|
||||
@ -90,7 +93,9 @@ def load_logfile_data(
|
||||
return answers_data
|
||||
|
||||
|
||||
def query_user_data(default_user_data: AnyByStrDict) -> AnyByStrDict: # pragma: no cover
|
||||
def query_user_data(
|
||||
default_user_data: AnyByStrDict
|
||||
) -> AnyByStrDict: # pragma: no cover
|
||||
"""Query to user about the data of the config file.
|
||||
"""
|
||||
if not default_user_data:
|
||||
|
@ -10,8 +10,8 @@ from . import vcs
|
||||
from .config import make_config
|
||||
from .config.objects import Flags
|
||||
from .tools import (
|
||||
Style,
|
||||
Renderer,
|
||||
Style,
|
||||
copy_file,
|
||||
get_jinja_renderer,
|
||||
get_name_filters,
|
||||
|
@ -18,7 +18,6 @@ from .types import (
|
||||
CheckPathFunc,
|
||||
IntSeq,
|
||||
JSONSerializable,
|
||||
OptStrOrPathSeq,
|
||||
OptBool,
|
||||
OptStr,
|
||||
StrOrPath,
|
||||
@ -27,12 +26,7 @@ from .types import (
|
||||
)
|
||||
from .version import __version__
|
||||
|
||||
__all__ = (
|
||||
"Style",
|
||||
"printf",
|
||||
"prompt",
|
||||
"prompt_bool",
|
||||
)
|
||||
__all__ = ("Style", "printf", "prompt", "prompt_bool")
|
||||
|
||||
colorama.init()
|
||||
|
||||
@ -71,11 +65,7 @@ def printf(
|
||||
|
||||
|
||||
def printf_exception(
|
||||
e: Exception,
|
||||
action: str,
|
||||
msg: str = "",
|
||||
indent: int = 0,
|
||||
quiet: bool = False,
|
||||
e: Exception, action: str, msg: str = "", indent: int = 0, quiet: bool = False
|
||||
) -> None:
|
||||
if not quiet:
|
||||
print("")
|
||||
@ -129,10 +119,7 @@ def prompt(
|
||||
|
||||
|
||||
def prompt_bool(
|
||||
question: str,
|
||||
default: Optional[Any] = False,
|
||||
yes: str = "y",
|
||||
no: str = "n",
|
||||
question: str, default: Optional[Any] = False, yes: str = "y", no: str = "n"
|
||||
) -> OptBool:
|
||||
please_answer = f' Please answer "{yes}" or "{no}"'
|
||||
|
||||
@ -187,7 +174,10 @@ def to_nice_yaml(data: Any, **kwargs) -> Optional[str]:
|
||||
|
||||
class Renderer:
|
||||
def __init__(
|
||||
self, env: SandboxedEnvironment, src_path: Path, data: AnyByStrDict,
|
||||
self,
|
||||
env: SandboxedEnvironment,
|
||||
src_path: Path,
|
||||
data: AnyByStrDict,
|
||||
original_src_path: OptStr,
|
||||
) -> None:
|
||||
self.env = env
|
||||
@ -198,7 +188,8 @@ class Renderer:
|
||||
log["_src_path"] = original_src_path
|
||||
# Other data goes next
|
||||
log.update(
|
||||
(k, v) for (k, v) in data.items()
|
||||
(k, v)
|
||||
for (k, v) in data.items()
|
||||
if isinstance(k, JSONSerializable) and isinstance(v, JSONSerializable)
|
||||
)
|
||||
self.data = dict(data, _log=log)
|
||||
@ -233,8 +224,9 @@ def get_jinja_renderer(
|
||||
# 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(**envops)
|
||||
return Renderer(env=env, src_path=src_path, data=data,
|
||||
original_src_path=original_src_path)
|
||||
return Renderer(
|
||||
env=env, src_path=src_path, data=data, original_src_path=original_src_path
|
||||
)
|
||||
|
||||
|
||||
def normalize_str(text: StrOrPath, form: str = "NFD") -> str:
|
||||
|
14
mm.py
14
mm.py
@ -27,18 +27,8 @@ data = {
|
||||
"ruamel.yaml ~= 0.15",
|
||||
"pydantic ~= 0.30",
|
||||
],
|
||||
"testing_requires": [
|
||||
"pytest",
|
||||
"pytest-mock",
|
||||
"pytest-mypy"
|
||||
"pytest-cov",
|
||||
],
|
||||
"development_requires": [
|
||||
"pytest-flake8",
|
||||
"flake8",
|
||||
"ipdb",
|
||||
"tox",
|
||||
],
|
||||
"testing_requires": ["pytest", "pytest-mock", "pytest-mypy" "pytest-cov"],
|
||||
"development_requires": ["pytest-flake8", "flake8", "ipdb", "tox"],
|
||||
"entry_points": "copier = copier.cli:run",
|
||||
"coverage_omit": [],
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||
combine_as_imports = true
|
||||
force_grid_wrap = 0
|
||||
include_trailing_comma = true
|
||||
known_third_party = ["colorama", "jinja2", "pkg_resources", "pydantic", "pytest", "setuptools", "six"]
|
||||
known_third_party = ["colorama", "jinja2", "pkg_resources", "plumbum", "pydantic", "pytest", "ruamel", "setuptools", "six"]
|
||||
line_length = 88
|
||||
multi_line_output = 3
|
||||
use_parentheses = true
|
||||
|
@ -1 +1 @@
|
||||
This file should be normally ignored
|
||||
This file should be normally ignored
|
||||
|
@ -1 +1 @@
|
||||
This is a triumph
|
||||
This is a triumph
|
||||
|
@ -1 +1 @@
|
||||
world
|
||||
world
|
||||
|
@ -1 +1 @@
|
||||
lorem ipsum
|
||||
lorem ipsum
|
||||
|
@ -1,2 +1,2 @@
|
||||
UTF-8(ユーティーエフはち、ユーティーエフエイト)はISO/IEC 10646(UCS)とUnicodeで使える8ビット符号単位の文字符号化形式及び文字符号化スキーム。
|
||||
正式名称は、ISO/IEC 10646では‘UCS Transformation Format 8’、Unicodeでは‘Unicode Transformation Format-8’という。両者はISO/IEC 10646とUnicodeのコード重複範囲で互換性がある。RFCにも仕様がある[1]。
|
||||
正式名称は、ISO/IEC 10646では‘UCS Transformation Format 8’、Unicodeでは‘Unicode Transformation Format-8’という。両者はISO/IEC 10646とUnicodeのコード重複範囲で互換性がある。RFCにも仕様がある[1]。
|
||||
|
@ -1,2 +1,2 @@
|
||||
UTF-8(ユーティーエフはち、ユーティーエフエイト)はISO/IEC 10646(UCS)とUnicodeで使える8ビット符号単位の文字符号化形式及び文字符号化スキーム。
|
||||
正式名称は、ISO/IEC 10646では‘UCS Transformation Format 8’、Unicodeでは‘Unicode Transformation Format-8’という。両者はISO/IEC 10646とUnicodeのコード重複範囲で互換性がある。RFCにも仕様がある[1]。
|
||||
正式名称は、ISO/IEC 10646では‘UCS Transformation Format 8’、Unicodeでは‘Unicode Transformation Format-8’という。両者はISO/IEC 10646とUnicodeのコード重複範囲で互換性がある。RFCにも仕様がある[1]。
|
||||
|
@ -1,2 +1,2 @@
|
||||
_tasks:
|
||||
- exit 1
|
||||
- exit 1
|
||||
|
@ -1,2 +1 @@
|
||||
[% extends "parent.txt" %]
|
||||
|
||||
|
@ -1,2 +1 @@
|
||||
[% extends "parent.txt" %]
|
||||
|
||||
|
@ -1 +1 @@
|
||||
%343
|
||||
%343
|
||||
|
@ -2,7 +2,7 @@
|
||||
a_string: lorem ipsum
|
||||
a_number: 12345
|
||||
a_boolean: true
|
||||
a_list:
|
||||
a_list:
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
|
@ -1,4 +1,4 @@
|
||||
A string: [[ a_string ]]
|
||||
A number: [[ a_number ]]
|
||||
A boolean: [[ a_boolean ]]
|
||||
A list: [[ ", ".join(a_list) ]]
|
||||
A list: [[ ", ".join(a_list) ]]
|
||||
|
@ -2,7 +2,7 @@
|
||||
a_string: lorem ipsum
|
||||
a_number: 12345
|
||||
a_boolean: true
|
||||
a_list:
|
||||
a_list:
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
|
@ -1,4 +1,4 @@
|
||||
A string: [[ a_string ]]
|
||||
A number: [[ a_number ]]
|
||||
A boolean: [[ a_boolean ]]
|
||||
A list: [[ ", ".join(a_list) ]]
|
||||
A list: [[ ", ".join(a_list) ]]
|
||||
|
@ -1,4 +1,4 @@
|
||||
A string: lorem ipsum
|
||||
A number: 12345
|
||||
A boolean: True
|
||||
A list: one, two, three
|
||||
A list: one, two, three
|
||||
|
@ -36,8 +36,8 @@ GOOD_ENV_OPS = {
|
||||
"variable_end_string": ">>",
|
||||
"keep_trailing_newline": False,
|
||||
"i_am_not_a_member": None,
|
||||
'comment_end_string': '#>',
|
||||
'comment_start_string': '<#',
|
||||
"comment_end_string": "#>",
|
||||
"comment_start_string": "<#",
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
import copier
|
||||
|
||||
from .helpers import DATA, PROJECT_TEMPLATE, assert_file, filecmp, render
|
||||
|
||||
|
||||
@ -40,7 +41,9 @@ def test_copy(dst):
|
||||
assert not os.path.exists(dst / "[% if py3 %]py3_only.py[% endif %]")
|
||||
assert not os.path.exists(dst / "py2_only.py")
|
||||
assert os.path.exists(dst / "py3_only.py")
|
||||
assert not os.path.exists(dst / "[% if not py3 %]py2_folder[% endif %]" / "thing.py")
|
||||
assert not os.path.exists(
|
||||
dst / "[% if not py3 %]py2_folder[% endif %]" / "thing.py"
|
||||
)
|
||||
assert not os.path.exists(dst / "[% if py3 %]py3_folder[% endif %]" / "thing.py")
|
||||
assert not os.path.exists(dst / "py2_folder" / "thing.py")
|
||||
assert os.path.exists(dst / "py3_folder" / "thing.py")
|
||||
@ -108,7 +111,6 @@ def test_config_exclude(dst, monkeypatch):
|
||||
assert not (dst / "aaaa.txt").exists()
|
||||
|
||||
|
||||
|
||||
def test_config_exclude_overridden(dst):
|
||||
def fake_data(*_args, **_kwargs):
|
||||
return {"_exclude": ["*.txt"]}
|
||||
@ -126,7 +128,6 @@ def test_config_include(dst, monkeypatch):
|
||||
assert (dst / ".svn").exists()
|
||||
|
||||
|
||||
|
||||
def test_skip_option(dst):
|
||||
render(dst)
|
||||
path = dst / "pyproject.toml"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import copier
|
||||
from copier.config.user_data import load_logfile_data
|
||||
|
||||
from .helpers import PROJECT_TEMPLATE
|
||||
|
||||
SRC = f"{PROJECT_TEMPLATE}_logfile"
|
||||
|
@ -6,5 +6,5 @@ from .helpers import PROJECT_TEMPLATE
|
||||
def test_normal_jinja2(dst):
|
||||
copier.copy(f"{PROJECT_TEMPLATE}_normal_jinja2", dst, force=True)
|
||||
todo = (dst / "TODO.txt").read_text()
|
||||
expected = '[[ Guybrush TODO LIST]]\n[# GROG #]\n - Become a pirate\n'
|
||||
expected = "[[ Guybrush TODO LIST]]\n[# GROG #]\n - Become a pirate\n"
|
||||
assert todo == expected
|
||||
|
Loading…
x
Reference in New Issue
Block a user