mirror of
https://github.com/copier-org/copier.git
synced 2025-05-05 15:32:54 +00:00
Autodoc
This commit is contained in:
parent
45c5f0d90b
commit
3ad2c67a20
1
.gitignore
vendored
1
.gitignore
vendored
@ -33,6 +33,7 @@ pip-wheel-metadata
|
||||
# Documentation
|
||||
*/_build/*
|
||||
*/site/*
|
||||
/site
|
||||
.mypy_cache/*
|
||||
.cache
|
||||
*.log
|
||||
|
@ -70,14 +70,3 @@ repos:
|
||||
- id: mixed-line-ending
|
||||
args: ["--fix=lf"]
|
||||
- id: trailing-whitespace
|
||||
|
||||
# regenerate the README table of contents
|
||||
- repo: https://github.com/thlorenz/doctoc
|
||||
rev: v1.4.0
|
||||
hooks:
|
||||
- id: doctoc
|
||||
args:
|
||||
- --github
|
||||
- --maxlevel=6
|
||||
- --title=<summary>Table of contents</summary>
|
||||
exclude: ^tests/
|
||||
|
31
CHANGELOG.md
31
CHANGELOG.md
@ -3,29 +3,18 @@
|
||||
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)
|
||||
|
||||
<details>
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
<summary>Table of contents</summary>
|
||||
### Version 4.0.2 (2020-07-21)
|
||||
|
||||
- [Version 4.0.0 (2020-06)](#version-400-2020-06)
|
||||
- [Version 3.2.0 (2020-06)](#version-320-2020-06)
|
||||
- [Version 3.1.0 (2020-05)](#version-310-2020-05)
|
||||
- [Version 3.0.0 (2020-03)](#version-300-2020-03)
|
||||
- [Features](#features)
|
||||
- [Other](#other)
|
||||
- [Version 2.5 (2019-06)](#version-25-2019-06)
|
||||
- [Version 2.4.2 (2019-06)](#version-242-2019-06)
|
||||
- [Version 2.4 (2019-06)](#version-24-2019-06)
|
||||
- [Version 2.3 (2019-04)](#version-23-2019-04)
|
||||
- [Version 2.2 (2019-04)](#version-22-2019-04)
|
||||
- [Version 2.1 (2019-02)](#version-21-2019-02)
|
||||
- [Version 2.0 (2019-02)](#version-20-2019-02)
|
||||
[All changes here](https://github.com/copier-org/copier/milestone/11?closed=1). Summary:
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- prettier-ignore-end -->
|
||||
</details>
|
||||
- Fix wrong templated default answers classification, which produced some questions being ignored.
|
||||
|
||||
### Version 4.0.1 (2020-06-23)
|
||||
|
||||
[All changes here](https://github.com/copier-org/copier/milestone/10?closed=1). Summary:
|
||||
|
||||
- Fix wrong prompt regression when updating.
|
||||
- Remove redundant `dst` fixture in tests.
|
||||
|
||||
### Version 4.0.0 (2020-06)
|
||||
|
||||
|
@ -3,25 +3,6 @@
|
||||
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and
|
||||
credit will always be given.
|
||||
|
||||
<details>
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
<summary>Table of contents</summary>
|
||||
|
||||
- [Report Bugs](#report-bugs)
|
||||
- [Fix Bugs](#fix-bugs)
|
||||
- [Implement Features](#implement-features)
|
||||
- [Write Documentation](#write-documentation)
|
||||
- [Submit Feedback](#submit-feedback)
|
||||
- [Get Started!](#get-started)
|
||||
- [Pull Request Guidelines](#pull-request-guidelines)
|
||||
- [Tips](#tips)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- prettier-ignore-end -->
|
||||
</details>
|
||||
|
||||
## Report Bugs
|
||||
|
||||
Report bugs at <https://github.com/jpscaletti/copier/issues>.
|
||||
|
4
Makefile
4
Makefile
@ -23,6 +23,10 @@ clean-pyc:
|
||||
find . -name '__pycache__' -exec rm -rf {} +
|
||||
find . -name '.pytest_cache' -exec rm -rf {} +
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
mkdocs serve
|
||||
|
||||
test:
|
||||
pytest -x copier tests
|
||||
|
||||
|
577
README.md
577
README.md
@ -1,4 +1,5 @@
|
||||
## Think this library is awesome? Vote with a 👍 to include it in the awesome-python list: https://github.com/vinta/awesome-python/pull/1350
|
||||
**Think this library is awesome? Vote with a 👍 to include it in the awesome-python
|
||||
list: https://github.com/DoronCohen/awesome-python/pull/1**
|
||||
|
||||
# 
|
||||
|
||||
@ -19,40 +20,6 @@ A library for rendering project templates.
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
<summary>Table of contents</summary>
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Quick usage](#quick-usage)
|
||||
- [Creating a template](#creating-a-template)
|
||||
- [The `copier.yml` file](#the-copieryml-file)
|
||||
- [Prompt the user for information](#prompt-the-user-for-information)
|
||||
- [Advanced prompt formatting](#advanced-prompt-formatting)
|
||||
- [Prompt templating](#prompt-templating)
|
||||
- [Special options](#special-options)
|
||||
- [Patterns syntax](#patterns-syntax)
|
||||
- [Examples for pattern matching](#examples-for-pattern-matching)
|
||||
- [Include other yaml files](#include-other-yaml-files)
|
||||
- [The `.copier-answers.yml` file](#the-copier-answersyml-file)
|
||||
- [Template helpers](#template-helpers)
|
||||
- [Builtin variables/functions](#builtin-variablesfunctions)
|
||||
- [Builtin filters](#builtin-filters)
|
||||
- [Generating a project](#generating-a-project)
|
||||
- [Updating a project](#updating-a-project)
|
||||
- [Browse or tag public templates](#browse-or-tag-public-templates)
|
||||
- [API](#api)
|
||||
- [copier.copy()](#copiercopy)
|
||||
- [Comparison with other project generators](#comparison-with-other-project-generators)
|
||||
- [Cookiecutter](#cookiecutter)
|
||||
- [Credits](#credits)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- prettier-ignore-end -->
|
||||
</details>
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install Git 2.24 or newer.
|
||||
@ -85,444 +52,6 @@ copy("gl:pykong/copier.git", "path/to/destination")
|
||||
copier path/to/project/template path/to/destination
|
||||
```
|
||||
|
||||
## Creating a template
|
||||
|
||||
A template is a directory: usually the root folder of a git repository.
|
||||
|
||||
The content of the files inside the project template is copied to the destination
|
||||
without changes, **unless they end with `.tmpl`** (or your chosen `templates_suffix`).
|
||||
In that case, the templating engine will be used to render them.
|
||||
|
||||
A slightly customized Jinja2 templating is used. The main difference is those variables
|
||||
are referenced with `[[ name ]]` instead of `{{ name }}` and blocks are `[% if name %]`
|
||||
instead of `{% if name %}`. To read more about templating see the
|
||||
[Jinja2 documentation](https://jinja.palletsprojects.com/).
|
||||
|
||||
If a **YAML** file named `copier.yml` is found in the root of the project
|
||||
(alternatively, a YAML file named `copier.yaml`), the user will be prompted to fill in
|
||||
or confirm the default values.
|
||||
|
||||
Since version 3.0, only Python 3.6 or later are supported. Please use the 2.5.1 version
|
||||
if your project runs on a previous Python version.
|
||||
|
||||
### The `copier.yml` file
|
||||
|
||||
If a `copier.yml`, or `copier.yaml` is found in the root of the template, it will be
|
||||
read and used for two purposes:
|
||||
|
||||
- prompting the user for information
|
||||
- configuring project generation (excluding files, setting arguments defaults, etc.)
|
||||
|
||||
#### Prompt the user for information
|
||||
|
||||
For each key found, Copier will prompt the user to fill or confirm the values before
|
||||
they become available to the project template. So content like this:
|
||||
|
||||
```yaml
|
||||
name_of_the_project: My awesome project
|
||||
number_of_eels: 1234
|
||||
your_email: ""
|
||||
```
|
||||
|
||||
will result in this series of questions:
|
||||
|
||||
<pre>
|
||||
<b>name_of_the_project</b>? Format: yaml
|
||||
🎤 [My awesome project]:
|
||||
|
||||
<b>number_of_eels</b>? Format: yaml
|
||||
🎤 [1234]:
|
||||
|
||||
<b>your_email</b>? Format: yaml
|
||||
🎤 []:
|
||||
</pre>
|
||||
|
||||
##### Advanced prompt formatting
|
||||
|
||||
Apart from the simplified format, as seen above, Copier supports a more advanced format
|
||||
to ask users for data. To use it, the value must be a dict.
|
||||
|
||||
Supported keys:
|
||||
|
||||
- **type**: User input must match this type. Options are: bool, float, int, json, str,
|
||||
yaml.
|
||||
- **help**: Additional text to help the user know what's this question for.
|
||||
- **default**: Leave empty to force the user to answer. Provide a default to save him
|
||||
from typing it if it's quite common. When using **choices**, the default must be the
|
||||
choice _value_, not its _key_. If values are quite long, you can use
|
||||
[YAML anchors](https://confluence.atlassian.com/bitbucket/yaml-anchors-960154027.html).
|
||||
|
||||
```yaml
|
||||
love_copier:
|
||||
type: bool # This makes Copier ask for y/n
|
||||
help: Do you love Copier?
|
||||
default: yes # Without a default, you force the user to answer
|
||||
|
||||
project_name:
|
||||
type: str # Any value will be treated raw as a string
|
||||
help: An awesome project needs an awesome name. Tell me yours.
|
||||
default: paradox-specifier
|
||||
|
||||
rocket_launch_password:
|
||||
type: str
|
||||
secret: true # This value will not be logged into .copier-answers.yml
|
||||
default: my top secret password
|
||||
|
||||
# I'll avoid default and help here, but you can use them too
|
||||
age:
|
||||
type: int
|
||||
|
||||
height:
|
||||
type: float
|
||||
|
||||
any_json:
|
||||
help: Tell me anything, but format it as a one-line JSON string
|
||||
type: json
|
||||
|
||||
any_yaml:
|
||||
help: Tell me anything, but format it as a one-line YAML string
|
||||
type: yaml # This is the default type, also for short syntax questions
|
||||
|
||||
your_favorite_book:
|
||||
# User will type 1 or 2, but your template will get the value
|
||||
choices:
|
||||
- The Bible
|
||||
- The Hitchhiker's Guide to the Galaxy
|
||||
|
||||
project_license:
|
||||
# User will type 1 or 2 and will see only the dict key, but you will
|
||||
# get the dict value in your template
|
||||
choices:
|
||||
MIT: &mit_text |
|
||||
Here I can write the full text of the MIT license.
|
||||
This will be a long text, shortened here for example purposes.
|
||||
Apache2: |
|
||||
Full text of Apache2 license.
|
||||
# When using choices, the default value is the value, **not** the key;
|
||||
# that's why I'm using the YAML anchor declared above to avoid retyping the
|
||||
# whole license
|
||||
default: *mit_text
|
||||
# You can still define the type, to make sure answers that come from --data
|
||||
# CLI argument match the type that your template expects
|
||||
type: str
|
||||
|
||||
close_to_work:
|
||||
help: Do you live close to your work?
|
||||
# This format works just like the dict one
|
||||
choices:
|
||||
- [at home, I work at home]
|
||||
- [less than 10km, quite close]
|
||||
- [more than 10km, not so close]
|
||||
- [more than 100km, quite far away]
|
||||
```
|
||||
|
||||
##### Prompt templating
|
||||
|
||||
Values of prompted keys can use Jinja templates.
|
||||
|
||||
Keep in mind that the configuration is loaded as **YAML**, so the contents must be
|
||||
**valid YAML** and respect **Copier's structure**. That is why we explicitly wrap some
|
||||
strings in double-quotes in the following examples.
|
||||
|
||||
Answers provided through interactive prompting will not be rendered with Jinja, so you
|
||||
cannot use Jinja templating in your answers.
|
||||
|
||||
```yaml
|
||||
# default
|
||||
username:
|
||||
type: str
|
||||
|
||||
organization:
|
||||
type: str
|
||||
|
||||
email:
|
||||
type: str
|
||||
default: "[[ username ]]@[[ organization ]].com"
|
||||
|
||||
# help
|
||||
copyright_holder:
|
||||
type: str
|
||||
help: The person or entity within [[ organization ]] that holds copyrights.
|
||||
|
||||
# type
|
||||
target:
|
||||
type: str
|
||||
choices:
|
||||
- humans
|
||||
- machines
|
||||
|
||||
user_config:
|
||||
type: "[% if target == 'humans' %]yaml[% else %]json[% endif %]"
|
||||
|
||||
# choices
|
||||
title:
|
||||
type: str
|
||||
help: Your title within [[ organization ]]
|
||||
|
||||
contact:
|
||||
choices:
|
||||
Copyright holder: "[[ copyright_holder ]]"
|
||||
CEO: Alice Bob
|
||||
CTO: Carl Dave
|
||||
"[[ title ]]": "[[ username ]]"
|
||||
```
|
||||
|
||||
#### Special options
|
||||
|
||||
Copier will also read special configuration options from the `copier.yml` file. They all
|
||||
start with an underscore.
|
||||
|
||||
```yaml
|
||||
# Specify the minimum required version of Copier to generate a project from this template.
|
||||
# The version must be follow the PEP 440 syntax.
|
||||
# Upon generating or updating a project, if the installed version of Copier is less than the required one,
|
||||
# the generation will be aborted and an error will be shown to the user.
|
||||
_min_copier_version: "4.1.0"
|
||||
|
||||
# File where answers will be recorded. Defaults to `.copier-answers.yml`.
|
||||
# Remember to add that file to your template if you want to support updates.
|
||||
_answers_file: .my-custom-answers.yml
|
||||
|
||||
# Suffix that instructs which files are to be processed by Jinja as templates
|
||||
_templates_suffix: .tmpl
|
||||
|
||||
# gitignore-style patterns files/folders that must not be copied.
|
||||
# Can be overridden with the `exclude` CLI/API option.
|
||||
_exclude:
|
||||
- "*.bar"
|
||||
- ".git"
|
||||
|
||||
# gitignore-style patterns files to skip, without asking, if they already exists
|
||||
# in the destination folder
|
||||
# Can be overridden with the `skip_if_exist` API option.
|
||||
_skip_if_exists:
|
||||
|
||||
# Subdirectory to use as the template root when generating a project.
|
||||
# If not specified, the root of the git repository is used.
|
||||
# Can be overridden with the `subdirectory` CLI/API option.
|
||||
_subdirectory: "project"
|
||||
|
||||
# Commands to execute after generating or updating a project from your template.
|
||||
# They run ordered, and with the $STAGE=task variable in their environment.
|
||||
# Can be overridden with the `tasks` API option.
|
||||
_tasks:
|
||||
# Strings get executed under system's default shell
|
||||
- "git init"
|
||||
- "rm [[ name_of_the_project / 'README.md' ]]"
|
||||
# Arrays are executed without shell, saving you the work of escaping arguments
|
||||
- [invoke, "--search-root=[[ _copier_conf.src_path ]]", after-copy]
|
||||
# You are able to output the full conf to JSON, to be parsed by your script,
|
||||
# but you cannot use the normal `|tojson` filter; instead, use `.json()`
|
||||
- [invoke, end-process, "--full-conf=[[ _copier_conf.json() ]]"]
|
||||
|
||||
# Migrations are like tasks, but they are executed:
|
||||
# - Evaluated using PEP 440
|
||||
# - In the same order as declared here (so you could even run a migration for a higher
|
||||
# version before running a migration for a lower version if the higher one is declared
|
||||
# before and the update passes through both)
|
||||
# - Only when new version >= declared version > old version
|
||||
# - Only when updating
|
||||
# - After being rendered with Jinja, with the same context as the rest of the template
|
||||
# - With $VERSION_FROM, $VERSION_TO, $VERSION_CURRENT and $STAGE (before/after)
|
||||
# environment variables
|
||||
# - The answers file is reloaded after running migrations in the "before" stage.
|
||||
_migrations:
|
||||
- version: v1.0.0
|
||||
before:
|
||||
- rm ./old-folder
|
||||
after:
|
||||
# [[ _copier_conf.src_path ]] points to the path where the template was
|
||||
# cloned, so it can be helpful to run migration scripts stored there.
|
||||
- invoke -r [[ _copier_conf.src_path ]] -c migrations migrate $VERSION_CURRENT
|
||||
|
||||
# Additional paths, from where to search for templates
|
||||
# Can be overridden with the `extra_paths` API option.
|
||||
_extra_paths:
|
||||
- ~/Projects/templates
|
||||
```
|
||||
|
||||
##### Patterns syntax
|
||||
|
||||
Copier supports matching names against patterns in a gitignore style fashion. This works
|
||||
for the options `exclude` and `skip`. This means you can write patterns as you would for
|
||||
any `.gitignore` file. The full range of the gitignore syntax ist supported via
|
||||
[pathspec](https://github.com/cpburnz/python-path-specification).
|
||||
|
||||
###### Examples for pattern matching
|
||||
|
||||
Putting the following settings in your `copier.yaml` file would exclude all files ending
|
||||
with "txt" from being copied to the destination folder, except the file `a.txt`.
|
||||
|
||||
```yaml
|
||||
_exclude:
|
||||
# match all text files...
|
||||
- "*.txt"
|
||||
# .. but not this one:
|
||||
- "!a.txt"
|
||||
```
|
||||
|
||||
#### Include other yaml files
|
||||
|
||||
To reuse configurations across templates you can reference other yaml files. You just
|
||||
need to state the `!include` together with the absolute or relative path to the file to
|
||||
be included. Multiple files can be included per `copier.yml`. For more detailed
|
||||
instructions, see [pyyaml-include](https://github.com/tanbro/pyyaml-include#usage).
|
||||
|
||||
```yaml
|
||||
# other_place/include_me.yml
|
||||
common_setting: "1"
|
||||
|
||||
# copier.yml
|
||||
!include other_place/include_me.yml
|
||||
```
|
||||
|
||||
### The `.copier-answers.yml` file
|
||||
|
||||
If the destination path exists and a `.copier-answers.yml` file is present there, it
|
||||
will be used to load the last user's answers to the questions made in
|
||||
[the `copier.yml` file](#the-copieryml-file).
|
||||
|
||||
This makes projects easier to update because when the user is asked, the default answers
|
||||
will be the last ones he used.
|
||||
|
||||
To make sure projects based on your templates can make use of this nice feature, **add a
|
||||
file called `[[ _copier_conf.answers_file ]].tmpl`** (or your chosen `templates_suffix`)
|
||||
in your template's root folder, with this content:
|
||||
|
||||
```yml
|
||||
# Changes here will be overwritten by Copier
|
||||
[[_copier_answers|to_nice_yaml]]
|
||||
```
|
||||
|
||||
If this file is called different than `[[ _copier_conf.answers_file ]].tmpl` your users
|
||||
will not be able to choose a custom answers file name, and thus they will not be able to
|
||||
integrate several updatable templates into one destination directory.
|
||||
|
||||
The builtin `_copier_answers` variable includes all data needed to smooth future updates
|
||||
of this project. This includes (but is not limited to) all JSON-serializable values
|
||||
declared as user questions in [the `copier.yml` file](#the-copieryml-file).
|
||||
|
||||
As you can see, you also have the power to customize what will be logged here. Keys that
|
||||
start with an underscore (`_`) are specific to Copier. Other keys should match questions
|
||||
in `copier.yml`.
|
||||
|
||||
If you plan to integrate several templates into one single downstream project, you can
|
||||
use a different path for this file:
|
||||
|
||||
```yaml
|
||||
# In your `copier.yml`:
|
||||
_answers_file: .my-custom-answers.yml
|
||||
```
|
||||
|
||||
### Template helpers
|
||||
|
||||
In addition to
|
||||
[all the features Jinja supports](https://jinja.palletsprojects.com/en/2.11.x/templates/),
|
||||
Copier includes:
|
||||
|
||||
#### Builtin variables/functions
|
||||
|
||||
- `now()` to get current UTC time.
|
||||
- `make_secret()` to get a random string.
|
||||
- `_copier_answers` includes the current answers dict, but slightly modified to make it
|
||||
suitable to [autoupdate your project safely](#the-answers-file):
|
||||
- It doesn't contain secret answers.
|
||||
- It doesn't contain any data that is not easy to render to JSON or YAML.
|
||||
- It contains special keys like `_commit` and `_src_path`, indicating how the last
|
||||
template update was done.
|
||||
- `_copier_conf` includes the current copier `ConfigData` object, also slightly
|
||||
modified:
|
||||
- It only contains JSON-serializable data.
|
||||
- But you have to serialize it with `[[ _copier_conf.json() ]]` instead of
|
||||
`[[ _copier_conf|tojson ]]`.
|
||||
- ⚠️ It contains secret answers inside its `.data` key.
|
||||
- Modifying it doesn't alter the current rendering configuration.
|
||||
|
||||
#### Builtin filters
|
||||
|
||||
- `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.
|
||||
|
||||
## Generating a project
|
||||
|
||||
**Warning:** Generate projects only from trusted templates as their tasks run with the
|
||||
same level of access as your user.
|
||||
|
||||
As seen in the quick usage section, you can generate a project from a template using the
|
||||
`copier` command-line tool:
|
||||
|
||||
```bash
|
||||
copier path/to/project/template path/to/destination
|
||||
```
|
||||
|
||||
Or within Python code:
|
||||
|
||||
```python
|
||||
copier.copy("path/to/project/template", "path/to/destination")
|
||||
```
|
||||
|
||||
The "template" parameter can be a local path, an URL, or a shortcut URL:
|
||||
|
||||
- GitHub: `gh:namespace/project`
|
||||
- GitLab: `gl:namespace/project`
|
||||
|
||||
Use the `--data` command-line argument or the `data` parameter of the `copier.copy()`
|
||||
function to pass whatever extra context you want to be available in the templates. The
|
||||
arguments can be any valid Python value, even a function.
|
||||
|
||||
Use the `--vcs-ref` command-line argument to checkout a particular git ref before
|
||||
generating the project.
|
||||
|
||||
All the available options are described with the `--help-all` option.
|
||||
|
||||
## Updating a project
|
||||
|
||||
The best way to update a project from its template is when all of these conditions are
|
||||
true:
|
||||
|
||||
1. The template includes a valid `.copier-answers.yml` file.
|
||||
2. The template is versioned with git (with tags).
|
||||
3. The destination folder is versioned with git.
|
||||
|
||||
If that's your case, then just enter the destination folder, make sure `git status`
|
||||
shows it clean, and run:
|
||||
|
||||
```bash
|
||||
copier update
|
||||
```
|
||||
|
||||
This will read all available git tags, will compare them using
|
||||
[PEP 440](https://www.python.org/dev/peps/pep-0440/), and will check out the latest one
|
||||
before updating. To update to the latest commit, add `--vcs-ref=HEAD`. You can use any
|
||||
other git ref you want.
|
||||
|
||||
When updating, Copier will do its best to respect your project evolution by using the
|
||||
answers you provided when copied last time. However, sometimes it's impossible for
|
||||
Copier to know what to do with a diff code hunk. In those cases, you will find `*.rej`
|
||||
files that contain the unresolved diffs. _You should review those manually_ before
|
||||
committing.
|
||||
|
||||
You probably don't want `*.rej` files in your git history, but if you add them to
|
||||
`.gitignore`, some important changes could pass unnoticed to you. That's why the
|
||||
recommended way to deal with them is to _not_ add them to add a
|
||||
[pre-commit](https://pre-commit.com/) (or equivalent) hook that forbids them, just like
|
||||
this:
|
||||
|
||||
```yaml
|
||||
# .pre-commit-config.yaml
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: forbidden-files
|
||||
name: forbidden files
|
||||
entry: found copier update rejection files; review them and remove them
|
||||
language: fail
|
||||
files: "\\.rej$"
|
||||
```
|
||||
|
||||
## Browse or tag public templates
|
||||
|
||||
You can browse public copier templates in GitHub using
|
||||
@ -531,108 +60,6 @@ inspiration!
|
||||
|
||||
If you want your template to appear in that list, just add the topic to it! 🏷
|
||||
|
||||
## API
|
||||
|
||||
### copier.copy()
|
||||
|
||||
```python
|
||||
copier.copy(
|
||||
src_path,
|
||||
dst_path,
|
||||
|
||||
data=DEFAULT_DATA,
|
||||
*,
|
||||
exclude=DEFAULT_FILTER,
|
||||
skip_if_exists=[],
|
||||
tasks=[],
|
||||
|
||||
envops={},
|
||||
extra_paths=[],
|
||||
|
||||
pretend=False,
|
||||
force=False,
|
||||
skip=False,
|
||||
quiet=False,
|
||||
cleanup_on_error=True,
|
||||
subdirectory=None,
|
||||
)
|
||||
```
|
||||
|
||||
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, which can also be a
|
||||
version control system URL.
|
||||
|
||||
- **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`.
|
||||
|
||||
- **exclude** (list):<br> A list of names or gitignore-style patterns matching files or
|
||||
folders that must not be copied.
|
||||
|
||||
- **skip_if_exists** (list):<br> A list of names or gitignore-style patterns matching
|
||||
files or folders, that are skipped 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.
|
||||
|
||||
- **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:
|
||||
|
||||
```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:
|
||||
|
||||
```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.
|
||||
|
||||
- **pretend** (bool):<br> Run but do not make any changes.
|
||||
|
||||
- **force** (bool):<br> Overwrite files that already exist, without asking.
|
||||
|
||||
- **skip** (bool):<br> Skip files that already exist, without asking.
|
||||
|
||||
- **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 fails. True by default.
|
||||
|
||||
- **subdirectory** (str):<br> Path to a sub-folder to use as the root of the template
|
||||
when generating the project. If not specified, the root of the git repository is used.
|
||||
|
||||
## Comparison with other project generators
|
||||
|
||||
### Cookiecutter
|
||||
|
@ -1,4 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Command line entrypoint.
|
||||
|
||||
This module declares the Plumbum CLI application, its subcommand and options.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
|
||||
@ -7,10 +13,12 @@ from plumbum import cli, colors
|
||||
from . import __version__
|
||||
from .config.objects import UserMessageError
|
||||
from .main import copy
|
||||
from .types import AnyByStrDict, OptStr
|
||||
from .types import AnyByStrDict, List, OptStr
|
||||
|
||||
|
||||
def handle_exceptions(method):
|
||||
"""Handle keyboard interruption while running a method."""
|
||||
|
||||
def _wrapper(*args, **kwargs):
|
||||
try:
|
||||
try:
|
||||
@ -25,6 +33,21 @@ def handle_exceptions(method):
|
||||
|
||||
|
||||
class CopierApp(cli.Application):
|
||||
"""The Plumbum CLI application.
|
||||
|
||||
Attributes:
|
||||
answers_file: The switch for the answers file option.
|
||||
extra_paths: The switch for the extra paths option.
|
||||
exclude: The switch for the exclude option.
|
||||
vcs_ref: The switch for the VCS ref option.
|
||||
subdirectory: The switch for the subdirectory option.
|
||||
pretend: The flag for the pretend option.
|
||||
force: The flag for the force option.
|
||||
skip: The flag for the skip option.
|
||||
quiet: The flag for the quiet option.
|
||||
only_diff: The flag for the only diff option.
|
||||
"""
|
||||
|
||||
DESCRIPTION = "Create a new project from a template."
|
||||
DESCRIPTION_MORE = colors.yellow | dedent(
|
||||
"""
|
||||
@ -42,7 +65,7 @@ class CopierApp(cli.Application):
|
||||
CALL_MAIN_IF_NESTED_COMMAND = False
|
||||
data: AnyByStrDict = {}
|
||||
|
||||
answers_file = cli.SwitchAttr(
|
||||
answers_file: cli.SwitchAttr = cli.SwitchAttr(
|
||||
["-a", "--answers-file"],
|
||||
default=None,
|
||||
help=(
|
||||
@ -50,13 +73,13 @@ class CopierApp(cli.Application):
|
||||
"to find the answers file"
|
||||
),
|
||||
)
|
||||
extra_paths = cli.SwitchAttr(
|
||||
extra_paths: cli.SwitchAttr = cli.SwitchAttr(
|
||||
["-p", "--extra-paths"],
|
||||
str,
|
||||
list=True,
|
||||
help="Additional directories to find parent templates in",
|
||||
)
|
||||
exclude = cli.SwitchAttr(
|
||||
exclude: cli.SwitchAttr = cli.SwitchAttr(
|
||||
["-x", "--exclude"],
|
||||
str,
|
||||
list=True,
|
||||
@ -65,7 +88,7 @@ class CopierApp(cli.Application):
|
||||
"that must not be copied"
|
||||
),
|
||||
)
|
||||
vcs_ref = cli.SwitchAttr(
|
||||
vcs_ref: cli.SwitchAttr = cli.SwitchAttr(
|
||||
["-r", "--vcs-ref"],
|
||||
str,
|
||||
help=(
|
||||
@ -75,7 +98,7 @@ class CopierApp(cli.Application):
|
||||
"the latest version, use `--vcs-ref=HEAD`."
|
||||
),
|
||||
)
|
||||
subdirectory = cli.SwitchAttr(
|
||||
subdirectory: cli.SwitchAttr = cli.SwitchAttr(
|
||||
["-b", "--subdirectory"],
|
||||
str,
|
||||
help=(
|
||||
@ -84,14 +107,16 @@ class CopierApp(cli.Application):
|
||||
),
|
||||
)
|
||||
|
||||
pretend = cli.Flag(["-n", "--pretend"], help="Run but do not make any changes")
|
||||
force = cli.Flag(
|
||||
pretend: cli.Flag = cli.Flag(
|
||||
["-n", "--pretend"], help="Run but do not make any changes"
|
||||
)
|
||||
force: cli.Flag = cli.Flag(
|
||||
["-f", "--force"], help="Overwrite files that already exist, without asking"
|
||||
)
|
||||
skip = cli.Flag(
|
||||
skip: cli.Flag = cli.Flag(
|
||||
["-s", "--skip"], help="Skip files that already exist, without asking"
|
||||
)
|
||||
quiet = cli.Flag(["-q", "--quiet"], help="Suppress status output")
|
||||
quiet: cli.Flag = cli.Flag(["-q", "--quiet"], help="Suppress status output")
|
||||
|
||||
@cli.switch(
|
||||
["-d", "--data"],
|
||||
@ -100,11 +125,25 @@ class CopierApp(cli.Application):
|
||||
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)
|
||||
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`.
|
||||
"""
|
||||
self.data.update(value.split("=", 1) for value in values) # type: ignore
|
||||
|
||||
def _copy(self, src_path: OptStr = None, dst_path: str = ".", **kwargs) -> None:
|
||||
"""Run Copier's internal API using CLI switches."""
|
||||
"""
|
||||
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 [`copy`][copier.main.copy].
|
||||
"""
|
||||
return copy(
|
||||
data=self.data,
|
||||
dst_path=dst_path,
|
||||
@ -151,16 +190,26 @@ class CopierApp(cli.Application):
|
||||
|
||||
@CopierApp.subcommand("copy")
|
||||
class CopierCopySubApp(cli.Application):
|
||||
"""The `copy` subcommand."""
|
||||
|
||||
DESCRIPTION = "Copy form a template source to a destination"
|
||||
|
||||
@handle_exceptions
|
||||
def main(self, template_src: str, destination_path: str) -> int:
|
||||
"""The code of the `copy` subcommand."""
|
||||
self.parent._copy(template_src, destination_path)
|
||||
return 0
|
||||
|
||||
|
||||
@CopierApp.subcommand("update")
|
||||
class CopierUpdateSubApp(cli.Application):
|
||||
"""
|
||||
The `update` subcommand.
|
||||
|
||||
Attributes:
|
||||
only_diff: The flag for the only diff option.
|
||||
"""
|
||||
|
||||
DESCRIPTION = "Update a copy from its original template"
|
||||
DESCRIPTION_MORE = """
|
||||
The copy must have a valid answers file which contains info
|
||||
@ -172,12 +221,13 @@ class CopierUpdateSubApp(cli.Application):
|
||||
generated since the last `copier` execution. To disable that, use `--no-diff`.
|
||||
"""
|
||||
|
||||
only_diff = cli.Flag(
|
||||
only_diff: cli.Flag = cli.Flag(
|
||||
["-D", "--no-diff"], default=True, help="Disable smart diff detection."
|
||||
)
|
||||
|
||||
@handle_exceptions
|
||||
def main(self, destination_path: cli.ExistingDirectory = ".") -> int:
|
||||
"""The code of the `update` subcommand."""
|
||||
self.parent._copy(
|
||||
dst_path=destination_path, only_diff=self.only_diff,
|
||||
)
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Functions used to generate configuration data."""
|
||||
|
||||
from collections import ChainMap
|
||||
from typing import Tuple
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Pydantic models, exceptions and default values."""
|
||||
|
||||
import datetime
|
||||
from collections import ChainMap
|
||||
from copy import deepcopy
|
||||
@ -41,6 +43,8 @@ class NoSrcPathError(UserMessageError):
|
||||
|
||||
|
||||
class EnvOps(BaseModel):
|
||||
"""Jinja2 environment options."""
|
||||
|
||||
autoescape: StrictBool = False
|
||||
block_start_string: str = "[%"
|
||||
block_end_string: str = "%]"
|
||||
@ -62,6 +66,8 @@ class Migrations(BaseModel):
|
||||
|
||||
|
||||
class ConfigData(BaseModel):
|
||||
"""A model holding configuration data."""
|
||||
|
||||
src_path: Path
|
||||
subdirectory: OptStr
|
||||
dst_path: Path
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Functions used to load user data."""
|
||||
|
||||
import json
|
||||
import re
|
||||
from collections import ChainMap
|
||||
|
167
copier/main.py
167
copier/main.py
@ -1,3 +1,5 @@
|
||||
"""The main functions, used to generate or update projects."""
|
||||
|
||||
import filecmp
|
||||
import os
|
||||
import shutil
|
||||
@ -58,72 +60,71 @@ def copy(
|
||||
subdirectory: OptStr = None,
|
||||
) -> None:
|
||||
"""
|
||||
Uses the template in src_path to generate a new project at dst_path.
|
||||
Uses the template in `src_path` to generate a new project at `dst_path`.
|
||||
|
||||
Arguments:
|
||||
src_path:
|
||||
Absolute path to the project skeleton. May be a version control system URL.
|
||||
If `None`, it will be taken from `dst_path/answers_file` or fail.
|
||||
|
||||
- src_path (str):
|
||||
Absolute path to the project skeleton. May be a version control system URL.
|
||||
If `None`, it will be taken from `dst_path/answers_file` or fail.
|
||||
dst_path:
|
||||
Absolute path to where to render the skeleton
|
||||
|
||||
- dst_path (str):
|
||||
Absolute path to where to render the skeleton
|
||||
data:
|
||||
Optional. Data to be passed to the templates in addtion to the user data
|
||||
from a `copier.json`.
|
||||
|
||||
- data (dict):
|
||||
Optional. Data to be passed to the templates in addtion to the user data
|
||||
from a `copier.json`.
|
||||
answers_file:
|
||||
Path where to obtain the answers recorded from the last update.
|
||||
The path must be relative to `dst_path`.
|
||||
|
||||
- answers_file (str):
|
||||
Path where to obtain the answers recorded from the last update. The path
|
||||
must be relative to `dst_path`.
|
||||
exclude:
|
||||
A list of names or gitignore-style patterns matching files or folders that
|
||||
must not be copied.
|
||||
|
||||
- exclude (list):
|
||||
A list of names or gitignore-style patterns matching files or folders that
|
||||
must not be copied.
|
||||
skip_if_exists:
|
||||
A list of names or gitignore-style patterns matching files or folders,
|
||||
that are skipped 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):
|
||||
A list of names or gitignore-style patterns matching files or folders,
|
||||
that are skipped 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:
|
||||
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 fail, the rest of them will not run.
|
||||
|
||||
- tasks (list):
|
||||
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 fail, the rest of them will not run.
|
||||
envops:
|
||||
Extra options for the Jinja template environment.
|
||||
|
||||
- envops (dict):
|
||||
Extra options for the Jinja template environment.
|
||||
extra_paths:
|
||||
Optional. Additional paths, outside the `src_path`, 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):
|
||||
Optional. Additional paths, outside the `src_path`, 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:
|
||||
Run but do not make any changes.
|
||||
|
||||
- pretend (bool):
|
||||
Run but do not make any changes
|
||||
force:
|
||||
Overwrite files that already exist, without asking.
|
||||
|
||||
- force (bool):
|
||||
Overwrite files that already exist, without asking
|
||||
skip:
|
||||
Skip files that already exist, without asking.
|
||||
|
||||
- skip (bool):
|
||||
Skip files that already exist, without asking
|
||||
quiet:
|
||||
Suppress the status output.
|
||||
|
||||
- quiet (bool):
|
||||
Suppress the status output
|
||||
cleanup_on_error:
|
||||
Remove the destination folder if the copy process or one of the tasks fail.
|
||||
|
||||
- cleanup_on_error (bool):
|
||||
Remove the destination folder if the copy process or one of the tasks fail.
|
||||
vcs_ref:
|
||||
VCS reference to checkout in the template.
|
||||
|
||||
- vcs_ref (str):
|
||||
VCS reference to checkout in the template.
|
||||
only_diff:
|
||||
Try to update only the template diff.
|
||||
|
||||
- only_diff (bool):
|
||||
Try to update only the template diff.
|
||||
|
||||
- subdirectory (str):
|
||||
Specify a subdirectory to use when generating the project.
|
||||
subdirectory:
|
||||
Specify a subdirectory to use when generating the project.
|
||||
"""
|
||||
conf = make_config(**locals())
|
||||
is_update = conf.original_src_path != conf.src_path and vcs.is_git_repo_root(
|
||||
@ -151,7 +152,11 @@ def copy(
|
||||
|
||||
|
||||
def copy_local(conf: ConfigData) -> None:
|
||||
"""Use the given configuration to generate a project.
|
||||
|
||||
Arguments:
|
||||
conf: Configuration obtained with [`make_config`][copier.config.factory.make_config].
|
||||
"""
|
||||
must_filter = create_path_filter(conf.exclude)
|
||||
|
||||
render = Renderer(conf)
|
||||
@ -199,7 +204,12 @@ def copy_local(conf: ConfigData) -> None:
|
||||
print("") # padding space
|
||||
|
||||
|
||||
def update_diff(conf: ConfigData):
|
||||
def update_diff(conf: ConfigData) -> None:
|
||||
"""Use the given configuration to update a project.
|
||||
|
||||
Arguments:
|
||||
conf: Configuration obtained with [`make_config`][copier.config.factory.make_config].
|
||||
"""
|
||||
# Ensure local repo is clean
|
||||
if vcs.is_git_repo_root(conf.dst_path):
|
||||
with local.cwd(conf.dst_path):
|
||||
@ -273,6 +283,19 @@ def get_source_paths(
|
||||
render: Renderer,
|
||||
must_filter: Callable[[StrOrPath], bool],
|
||||
) -> List[Tuple[Path, Path]]:
|
||||
"""Get the paths to all the files to render.
|
||||
|
||||
Arguments:
|
||||
conf: Configuration obtained with [`make_config`][copier.config.factory.make_config].
|
||||
folder:
|
||||
rel_folder: Relative path to the folder.
|
||||
files:
|
||||
render: The [template renderer][copier.tools.Renderer] instance.
|
||||
must_filter: A callable telling whether to skip rendering a file.
|
||||
|
||||
Returns:
|
||||
The list of files to render.
|
||||
"""
|
||||
source_paths = []
|
||||
files_set = set(files)
|
||||
for src_name in files:
|
||||
@ -294,6 +317,14 @@ def get_source_paths(
|
||||
|
||||
|
||||
def render_folder(rel_folder: Path, conf: ConfigData) -> None:
|
||||
"""Render a complete folder.
|
||||
|
||||
This function renders the folder's name as well as its contents.
|
||||
|
||||
Arguments:
|
||||
rel_folder: The relative path to the folder.
|
||||
conf: Configuration obtained with [`make_config`][copier.config.factory.make_config].
|
||||
"""
|
||||
dst_path = conf.dst_path / rel_folder
|
||||
rel_path = f"{rel_folder}{os.path.sep}"
|
||||
|
||||
@ -319,7 +350,15 @@ def render_file(
|
||||
render: Renderer,
|
||||
must_skip: CheckPathFunc,
|
||||
) -> None:
|
||||
"""Process or copy a file of the skeleton."""
|
||||
"""Process or copy a file of the skeleton.
|
||||
|
||||
Arguments:
|
||||
conf: Configuration obtained with [`make_config`][copier.config.factory.make_config].
|
||||
rel_path: The relative path to the file.
|
||||
src_path:
|
||||
render: The [template renderer][copier.tools.Renderer] instance.
|
||||
must_skip: A callable telling whether to skip a file.
|
||||
"""
|
||||
content: Optional[str] = None
|
||||
if str(src_path).endswith(conf.templates_suffix):
|
||||
content = render(src_path)
|
||||
@ -346,12 +385,33 @@ def render_file(
|
||||
|
||||
|
||||
def files_are_identical(src_path: Path, dst_path: Path, content: Optional[str]) -> bool:
|
||||
"""Tell whether two files are identical.
|
||||
|
||||
Arguments:
|
||||
src_path: Source file.
|
||||
dst_path: Destination file.
|
||||
content: File contents.
|
||||
|
||||
Returns:
|
||||
True if the files are identical, False otherwise.
|
||||
"""
|
||||
if content is None:
|
||||
return filecmp.cmp(str(src_path), str(dst_path), shallow=False)
|
||||
return dst_path.read_text() == content
|
||||
|
||||
|
||||
def overwrite_file(conf: ConfigData, dst_path: Path, rel_path: Path) -> bool:
|
||||
"""Handle the case when there's an update conflict.
|
||||
|
||||
Arguments:
|
||||
conf: Configuration obtained with [`make_config`][copier.config.factory.make_config].
|
||||
dst_path: The destination file.
|
||||
rel_path: The new file.
|
||||
|
||||
Returns:
|
||||
True if the overwrite was forced or the user answered yes,
|
||||
False if skipped by configuration or if the user answered no.
|
||||
"""
|
||||
printf("conflict", rel_path, style=Style.DANGER, quiet=conf.quiet)
|
||||
if conf.force:
|
||||
return True
|
||||
@ -361,6 +421,13 @@ def overwrite_file(conf: ConfigData, dst_path: Path, rel_path: Path) -> bool:
|
||||
|
||||
|
||||
def run_tasks(conf: ConfigData, render: Renderer, tasks: Sequence[Dict]) -> None:
|
||||
"""Run the given tasks.
|
||||
|
||||
Arguments:
|
||||
conf: Configuration obtained with [`make_config`][copier.config.factory.make_config].
|
||||
render: The [template renderer][copier.tools.Renderer] instance.
|
||||
tasks: The list of tasks to run.
|
||||
"""
|
||||
for i, task in enumerate(tasks):
|
||||
task_cmd = task["task"]
|
||||
use_shell = isinstance(task_cmd, str)
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Some utility functions."""
|
||||
|
||||
import errno
|
||||
import os
|
||||
import shutil
|
||||
@ -125,6 +127,8 @@ def get_jinja_env(
|
||||
|
||||
|
||||
class Renderer:
|
||||
"""The Jinja template renderer."""
|
||||
|
||||
def __init__(self, conf: ConfigData) -> None:
|
||||
envops: EnvOps = conf.envops
|
||||
paths = [str(conf.src_path)] + list(map(str, conf.extra_paths or []))
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""All complex types and annotations are declared here."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
Any,
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Utilities related to VCS."""
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
|
101
docs/api/index.md
Normal file
101
docs/api/index.md
Normal file
@ -0,0 +1,101 @@
|
||||
## API
|
||||
|
||||
### copier.copy()
|
||||
|
||||
```python
|
||||
copier.copy(
|
||||
src_path,
|
||||
dst_path,
|
||||
|
||||
data=DEFAULT_DATA,
|
||||
*,
|
||||
exclude=DEFAULT_FILTER,
|
||||
skip_if_exists=[],
|
||||
tasks=[],
|
||||
|
||||
envops={},
|
||||
extra_paths=[],
|
||||
|
||||
pretend=False,
|
||||
force=False,
|
||||
skip=False,
|
||||
quiet=False,
|
||||
cleanup_on_error=True,
|
||||
subdirectory=None,
|
||||
)
|
||||
```
|
||||
|
||||
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, which can also be a
|
||||
version control system URL.
|
||||
|
||||
- **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`.
|
||||
|
||||
- **exclude** (list):<br> A list of names or gitignore-style patterns matching files or
|
||||
folders that must not be copied.
|
||||
|
||||
- **skip_if_exists** (list):<br> A list of names or gitignore-style patterns matching
|
||||
files or folders, that are skipped 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.
|
||||
|
||||
- **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:
|
||||
|
||||
```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:
|
||||
|
||||
```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.
|
||||
|
||||
- **pretend** (bool):<br> Run but do not make any changes.
|
||||
|
||||
- **force** (bool):<br> Overwrite files that already exist, without asking.
|
||||
|
||||
- **skip** (bool):<br> Skip files that already exist, without asking.
|
||||
|
||||
- **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 fails. True by default.
|
||||
|
||||
- **subdirectory** (str):<br> Path to a sub-folder to use as the root of the template
|
||||
when generating the project. If not specified, the root of the git repository is used.
|
1
docs/api/reference/cli.md
Normal file
1
docs/api/reference/cli.md
Normal file
@ -0,0 +1 @@
|
||||
::: copier.cli
|
1
docs/api/reference/config/factory.md
Normal file
1
docs/api/reference/config/factory.md
Normal file
@ -0,0 +1 @@
|
||||
::: copier.config.factory
|
1
docs/api/reference/config/objects.md
Normal file
1
docs/api/reference/config/objects.md
Normal file
@ -0,0 +1 @@
|
||||
::: copier.config.objects
|
1
docs/api/reference/config/user_data.md
Normal file
1
docs/api/reference/config/user_data.md
Normal file
@ -0,0 +1 @@
|
||||
::: copier.config.user_data
|
1
docs/api/reference/main.md
Normal file
1
docs/api/reference/main.md
Normal file
@ -0,0 +1 @@
|
||||
::: copier.main
|
1
docs/api/reference/tools.md
Normal file
1
docs/api/reference/tools.md
Normal file
@ -0,0 +1 @@
|
||||
::: copier.tools
|
1
docs/api/reference/types.md
Normal file
1
docs/api/reference/types.md
Normal file
@ -0,0 +1 @@
|
||||
::: copier.types
|
1
docs/api/reference/vcs.md
Normal file
1
docs/api/reference/vcs.md
Normal file
@ -0,0 +1 @@
|
||||
::: copier.vcs
|
1
docs/changelog.md
Symbolic link
1
docs/changelog.md
Symbolic link
@ -0,0 +1 @@
|
||||
../CHANGELOG.md
|
1
docs/contributing.md
Symbolic link
1
docs/contributing.md
Symbolic link
@ -0,0 +1 @@
|
||||
../CONTRIBUTING.md
|
360
docs/creating.md
Normal file
360
docs/creating.md
Normal file
@ -0,0 +1,360 @@
|
||||
# Creating a template
|
||||
|
||||
A template is a directory: usually the root folder of a git repository.
|
||||
|
||||
The content of the files inside the project template is copied to the destination
|
||||
without changes, **unless they end with `.tmpl`** (or your chosen `templates_suffix`).
|
||||
In that case, the templating engine will be used to render them.
|
||||
|
||||
A slightly customized Jinja2 templating is used. The main difference is those variables
|
||||
are referenced with `[[ name ]]` instead of `{{ name }}` and blocks are `[% if name %]`
|
||||
instead of `{% if name %}`. To read more about templating see the
|
||||
[Jinja2 documentation](https://jinja.palletsprojects.com/).
|
||||
|
||||
If a **YAML** file named `copier.yml` is found in the root of the project
|
||||
(alternatively, a YAML file named `copier.yaml`), the user will be prompted to fill in
|
||||
or confirm the default values.
|
||||
|
||||
Since version 3.0, only Python 3.6 or later are supported. Please use the 2.5.1 version
|
||||
if your project runs on a previous Python version.
|
||||
|
||||
## The `copier.yml` file
|
||||
|
||||
If a `copier.yml`, or `copier.yaml` is found in the root of the template, it will be
|
||||
read and used for two purposes:
|
||||
|
||||
- prompting the user for information
|
||||
- configuring project generation (excluding files, setting arguments defaults, etc.)
|
||||
|
||||
### Prompt the user for information
|
||||
|
||||
For each key found, Copier will prompt the user to fill or confirm the values before
|
||||
they become available to the project template. So content like this:
|
||||
|
||||
```yaml
|
||||
name_of_the_project: My awesome project
|
||||
number_of_eels: 1234
|
||||
your_email: ""
|
||||
```
|
||||
|
||||
will result in this series of questions:
|
||||
|
||||
<pre>
|
||||
<b>name_of_the_project</b>? Format: yaml
|
||||
🎤 [My awesome project]:
|
||||
|
||||
<b>number_of_eels</b>? Format: yaml
|
||||
🎤 [1234]:
|
||||
|
||||
<b>your_email</b>? Format: yaml
|
||||
🎤 []:
|
||||
</pre>
|
||||
|
||||
#### Advanced prompt formatting
|
||||
|
||||
Apart from the simplified format, as seen above, Copier supports a more advanced format
|
||||
to ask users for data. To use it, the value must be a dict.
|
||||
|
||||
Supported keys:
|
||||
|
||||
- **type**: User input must match this type. Options are: bool, float, int, json, str,
|
||||
yaml.
|
||||
- **help**: Additional text to help the user know what's this question for.
|
||||
- **default**: Leave empty to force the user to answer. Provide a default to save him
|
||||
from typing it if it's quite common. When using **choices**, the default must be the
|
||||
choice _value_, not its _key_. If values are quite long, you can use
|
||||
[YAML anchors](https://confluence.atlassian.com/bitbucket/yaml-anchors-960154027.html).
|
||||
|
||||
```yaml
|
||||
love_copier:
|
||||
type: bool # This makes Copier ask for y/n
|
||||
help: Do you love Copier?
|
||||
default: yes # Without a default, you force the user to answer
|
||||
|
||||
project_name:
|
||||
type: str # Any value will be treated raw as a string
|
||||
help: An awesome project needs an awesome name. Tell me yours.
|
||||
default: paradox-specifier
|
||||
|
||||
rocket_launch_password:
|
||||
type: str
|
||||
secret: true # This value will not be logged into .copier-answers.yml
|
||||
default: my top secret password
|
||||
|
||||
# I'll avoid default and help here, but you can use them too
|
||||
age:
|
||||
type: int
|
||||
|
||||
height:
|
||||
type: float
|
||||
|
||||
any_json:
|
||||
help: Tell me anything, but format it as a one-line JSON string
|
||||
type: json
|
||||
|
||||
any_yaml:
|
||||
help: Tell me anything, but format it as a one-line YAML string
|
||||
type: yaml # This is the default type, also for short syntax questions
|
||||
|
||||
your_favorite_book:
|
||||
# User will type 1 or 2, but your template will get the value
|
||||
choices:
|
||||
- The Bible
|
||||
- The Hitchhiker's Guide to the Galaxy
|
||||
|
||||
project_license:
|
||||
# User will type 1 or 2 and will see only the dict key, but you will
|
||||
# get the dict value in your template
|
||||
choices:
|
||||
MIT: &mit_text |
|
||||
Here I can write the full text of the MIT license.
|
||||
This will be a long text, shortened here for example purposes.
|
||||
Apache2: |
|
||||
Full text of Apache2 license.
|
||||
# When using choices, the default value is the value, **not** the key;
|
||||
# that's why I'm using the YAML anchor declared above to avoid retyping the
|
||||
# whole license
|
||||
default: *mit_text
|
||||
# You can still define the type, to make sure answers that come from --data
|
||||
# CLI argument match the type that your template expects
|
||||
type: str
|
||||
|
||||
close_to_work:
|
||||
help: Do you live close to your work?
|
||||
# This format works just like the dict one
|
||||
choices:
|
||||
- [at home, I work at home]
|
||||
- [less than 10km, quite close]
|
||||
- [more than 10km, not so close]
|
||||
- [more than 100km, quite far away]
|
||||
```
|
||||
|
||||
#### Prompt templating
|
||||
|
||||
Values of prompted keys can use Jinja templates.
|
||||
|
||||
Keep in mind that the configuration is loaded as **YAML**, so the contents must be
|
||||
**valid YAML** and respect **Copier's structure**. That is why we explicitly wrap some
|
||||
strings in double-quotes in the following examples.
|
||||
|
||||
Answers provided through interactive prompting will not be rendered with Jinja, so you
|
||||
cannot use Jinja templating in your answers.
|
||||
|
||||
```yaml
|
||||
# default
|
||||
username:
|
||||
type: str
|
||||
|
||||
organization:
|
||||
type: str
|
||||
|
||||
email:
|
||||
type: str
|
||||
default: "[[ username ]]@[[ organization ]].com"
|
||||
|
||||
# help
|
||||
copyright_holder:
|
||||
type: str
|
||||
help: The person or entity within [[ organization ]] that holds copyrights.
|
||||
|
||||
# type
|
||||
target:
|
||||
type: str
|
||||
choices:
|
||||
- humans
|
||||
- machines
|
||||
|
||||
user_config:
|
||||
type: "[% if target == 'humans' %]yaml[% else %]json[% endif %]"
|
||||
|
||||
# choices
|
||||
title:
|
||||
type: str
|
||||
help: Your title within [[ organization ]]
|
||||
|
||||
contact:
|
||||
choices:
|
||||
Copyright holder: "[[ copyright_holder ]]"
|
||||
CEO: Alice Bob
|
||||
CTO: Carl Dave
|
||||
"[[ title ]]": "[[ username ]]"
|
||||
```
|
||||
|
||||
### Special options
|
||||
|
||||
Copier will also read special configuration options from the `copier.yml` file. They all
|
||||
start with an underscore.
|
||||
|
||||
```yaml
|
||||
# Specify the minimum required version of Copier to generate a project from this template.
|
||||
# The version must be follow the PEP 440 syntax.
|
||||
# Upon generating or updating a project, if the installed version of Copier is less than the required one,
|
||||
# the generation will be aborted and an error will be shown to the user.
|
||||
_min_copier_version: "4.1.0"
|
||||
|
||||
# File where answers will be recorded. Defaults to `.copier-answers.yml`.
|
||||
# Remember to add that file to your template if you want to support updates.
|
||||
_answers_file: .my-custom-answers.yml
|
||||
|
||||
# Suffix that instructs which files are to be processed by Jinja as templates
|
||||
_templates_suffix: .tmpl
|
||||
|
||||
# gitignore-style patterns files/folders that must not be copied.
|
||||
# Can be overridden with the `exclude` CLI/API option.
|
||||
_exclude:
|
||||
- "*.bar"
|
||||
- ".git"
|
||||
|
||||
# gitignore-style patterns files to skip, without asking, if they already exists
|
||||
# in the destination folder
|
||||
# Can be overridden with the `skip_if_exist` API option.
|
||||
_skip_if_exists:
|
||||
|
||||
# Subdirectory to use as the template root when generating a project.
|
||||
# If not specified, the root of the git repository is used.
|
||||
# Can be overridden with the `subdirectory` CLI/API option.
|
||||
_subdirectory: "project"
|
||||
|
||||
# Commands to execute after generating or updating a project from your template.
|
||||
# They run ordered, and with the $STAGE=task variable in their environment.
|
||||
# Can be overridden with the `tasks` API option.
|
||||
_tasks:
|
||||
# Strings get executed under system's default shell
|
||||
- "git init"
|
||||
- "rm [[ name_of_the_project / 'README.md' ]]"
|
||||
# Arrays are executed without shell, saving you the work of escaping arguments
|
||||
- [invoke, "--search-root=[[ _copier_conf.src_path ]]", after-copy]
|
||||
# You are able to output the full conf to JSON, to be parsed by your script,
|
||||
# but you cannot use the normal `|tojson` filter; instead, use `.json()`
|
||||
- [invoke, end-process, "--full-conf=[[ _copier_conf.json() ]]"]
|
||||
|
||||
# Migrations are like tasks, but they are executed:
|
||||
# - Evaluated using PEP 440
|
||||
# - In the same order as declared here (so you could even run a migration for a higher
|
||||
# version before running a migration for a lower version if the higher one is declared
|
||||
# before and the update passes through both)
|
||||
# - Only when new version >= declared version > old version
|
||||
# - Only when updating
|
||||
# - After being rendered with Jinja, with the same context as the rest of the template
|
||||
# - With $VERSION_FROM, $VERSION_TO, $VERSION_CURRENT and $STAGE (before/after)
|
||||
# environment variables
|
||||
# - The answers file is reloaded after running migrations in the "before" stage.
|
||||
_migrations:
|
||||
- version: v1.0.0
|
||||
before:
|
||||
- rm ./old-folder
|
||||
after:
|
||||
# [[ _copier_conf.src_path ]] points to the path where the template was
|
||||
# cloned, so it can be helpful to run migration scripts stored there.
|
||||
- invoke -r [[ _copier_conf.src_path ]] -c migrations migrate $VERSION_CURRENT
|
||||
|
||||
# Additional paths, from where to search for templates
|
||||
# Can be overridden with the `extra_paths` API option.
|
||||
_extra_paths:
|
||||
- ~/Projects/templates
|
||||
```
|
||||
|
||||
#### Patterns syntax
|
||||
|
||||
Copier supports matching names against patterns in a gitignore style fashion. This works
|
||||
for the options `exclude` and `skip`. This means you can write patterns as you would for
|
||||
any `.gitignore` file. The full range of the gitignore syntax ist supported via
|
||||
[pathspec](https://github.com/cpburnz/python-path-specification).
|
||||
|
||||
##### Examples for pattern matching
|
||||
|
||||
Putting the following settings in your `copier.yaml` file would exclude all files ending
|
||||
with "txt" from being copied to the destination folder, except the file `a.txt`.
|
||||
|
||||
```yaml
|
||||
_exclude:
|
||||
# match all text files...
|
||||
- "*.txt"
|
||||
# .. but not this one:
|
||||
- "!a.txt"
|
||||
```
|
||||
|
||||
### Include other yaml files
|
||||
|
||||
To reuse configurations across templates you can reference other yaml files. You just
|
||||
need to state the `!include` together with the absolute or relative path to the file to
|
||||
be included. Multiple files can be included per `copier.yml`. For more detailed
|
||||
instructions, see [pyyaml-include](https://github.com/tanbro/pyyaml-include#usage).
|
||||
|
||||
```yaml
|
||||
# other_place/include_me.yml
|
||||
common_setting: "1"
|
||||
|
||||
# copier.yml
|
||||
!include other_place/include_me.yml
|
||||
```
|
||||
|
||||
## The `.copier-answers.yml` file
|
||||
|
||||
If the destination path exists and a `.copier-answers.yml` file is present there, it
|
||||
will be used to load the last user's answers to the questions made in
|
||||
[the `copier.yml` file](#the-copieryml-file).
|
||||
|
||||
This makes projects easier to update because when the user is asked, the default answers
|
||||
will be the last ones he used.
|
||||
|
||||
To make sure projects based on your templates can make use of this nice feature, **add a
|
||||
file called `[[ _copier_conf.answers_file ]].tmpl`** (or your chosen `templates_suffix`)
|
||||
in your template's root folder, with this content:
|
||||
|
||||
```yml
|
||||
# Changes here will be overwritten by Copier
|
||||
[[_copier_answers|to_nice_yaml]]
|
||||
```
|
||||
|
||||
If this file is called different than `[[ _copier_conf.answers_file ]].tmpl` your users
|
||||
will not be able to choose a custom answers file name, and thus they will not be able to
|
||||
integrate several updatable templates into one destination directory.
|
||||
|
||||
The builtin `_copier_answers` variable includes all data needed to smooth future updates
|
||||
of this project. This includes (but is not limited to) all JSON-serializable values
|
||||
declared as user questions in [the `copier.yml` file](#the-copieryml-file).
|
||||
|
||||
As you can see, you also have the power to customize what will be logged here. Keys that
|
||||
start with an underscore (`_`) are specific to Copier. Other keys should match questions
|
||||
in `copier.yml`.
|
||||
|
||||
If you plan to integrate several templates into one single downstream project, you can
|
||||
use a different path for this file:
|
||||
|
||||
```yaml
|
||||
# In your `copier.yml`:
|
||||
_answers_file: .my-custom-answers.yml
|
||||
```
|
||||
|
||||
## Template helpers
|
||||
|
||||
In addition to
|
||||
[all the features Jinja supports](https://jinja.palletsprojects.com/en/2.11.x/templates/),
|
||||
Copier includes:
|
||||
|
||||
### Builtin variables/functions
|
||||
|
||||
- `now()` to get current UTC time.
|
||||
- `make_secret()` to get a random string.
|
||||
- `_copier_answers` includes the current answers dict, but slightly modified to make it
|
||||
suitable to [autoupdate your project safely](#the-answers-file):
|
||||
- It doesn't contain secret answers.
|
||||
- It doesn't contain any data that is not easy to render to JSON or YAML.
|
||||
- It contains special keys like `_commit` and `_src_path`, indicating how the last
|
||||
template update was done.
|
||||
- `_copier_conf` includes the current copier `ConfigData` object, also slightly
|
||||
modified:
|
||||
- It only contains JSON-serializable data.
|
||||
- But you have to serialize it with `[[ _copier_conf.json() ]]` instead of
|
||||
`[[ _copier_conf|tojson ]]`.
|
||||
- ⚠️ It contains secret answers inside its `.data` key.
|
||||
- Modifying it doesn't alter the current rendering configuration.
|
||||
|
||||
### Builtin filters
|
||||
|
||||
- `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.
|
33
docs/css/mkdocstrings.css
Normal file
33
docs/css/mkdocstrings.css
Normal file
@ -0,0 +1,33 @@
|
||||
div.doc-contents:not(.first) {
|
||||
padding-left: 25px;
|
||||
border-left: 4px solid rgba(230, 230, 230);
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
|
||||
h5.doc-heading {
|
||||
text-transform: none !important;
|
||||
}
|
||||
|
||||
h6.hidden-toc {
|
||||
margin: 0 !important;
|
||||
position: relative;
|
||||
top: -70px;
|
||||
}
|
||||
|
||||
h6.hidden-toc::before {
|
||||
margin-top: 0 !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
h6.hidden-toc a.headerlink {
|
||||
display: none;
|
||||
}
|
||||
|
||||
td code {
|
||||
word-break: normal !important;
|
||||
}
|
||||
|
||||
td p {
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
31
docs/generating.md
Normal file
31
docs/generating.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Generating a project
|
||||
|
||||
**Warning:** Generate projects only from trusted templates as their tasks run with the
|
||||
same level of access as your user.
|
||||
|
||||
As seen in the quick usage section, you can generate a project from a template using the
|
||||
`copier` command-line tool:
|
||||
|
||||
```bash
|
||||
copier path/to/project/template path/to/destination
|
||||
```
|
||||
|
||||
Or within Python code:
|
||||
|
||||
```python
|
||||
copier.copy("path/to/project/template", "path/to/destination")
|
||||
```
|
||||
|
||||
The "template" parameter can be a local path, an URL, or a shortcut URL:
|
||||
|
||||
- GitHub: `gh:namespace/project`
|
||||
- GitLab: `gl:namespace/project`
|
||||
|
||||
Use the `--data` command-line argument or the `data` parameter of the `copier.copy()`
|
||||
function to pass whatever extra context you want to be available in the templates. The
|
||||
arguments can be any valid Python value, even a function.
|
||||
|
||||
Use the `--vcs-ref` command-line argument to checkout a particular git ref before
|
||||
generating the project.
|
||||
|
||||
All the available options are described with the `--help-all` option.
|
1
docs/index.md
Symbolic link
1
docs/index.md
Symbolic link
@ -0,0 +1 @@
|
||||
../README.md
|
44
docs/updating.md
Normal file
44
docs/updating.md
Normal file
@ -0,0 +1,44 @@
|
||||
# Updating a project
|
||||
|
||||
The best way to update a project from its template is when all of these conditions are
|
||||
true:
|
||||
|
||||
1. The template includes a valid `.copier-answers.yml` file.
|
||||
2. The template is versioned with git (with tags).
|
||||
3. The destination folder is versioned with git.
|
||||
|
||||
If that's your case, then just enter the destination folder, make sure `git status`
|
||||
shows it clean, and run:
|
||||
|
||||
```bash
|
||||
copier update
|
||||
```
|
||||
|
||||
This will read all available git tags, will compare them using
|
||||
[PEP 440](https://www.python.org/dev/peps/pep-0440/), and will check out the latest one
|
||||
before updating. To update to the latest commit, add `--vcs-ref=HEAD`. You can use any
|
||||
other git ref you want.
|
||||
|
||||
When updating, Copier will do its best to respect your project evolution by using the
|
||||
answers you provided when copied last time. However, sometimes it's impossible for
|
||||
Copier to know what to do with a diff code hunk. In those cases, you will find `*.rej`
|
||||
files that contain the unresolved diffs. _You should review those manually_ before
|
||||
committing.
|
||||
|
||||
You probably don't want `*.rej` files in your git history, but if you add them to
|
||||
`.gitignore`, some important changes could pass unnoticed to you. That's why the
|
||||
recommended way to deal with them is to _not_ add them to add a
|
||||
[pre-commit](https://pre-commit.com/) (or equivalent) hook that forbids them, just like
|
||||
this:
|
||||
|
||||
```yaml
|
||||
# .pre-commit-config.yaml
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: forbidden-files
|
||||
name: forbidden files
|
||||
entry: found copier update rejection files; review them and remove them
|
||||
language: fail
|
||||
files: "\\.rej$"
|
||||
```
|
47
mkdocs.yml
Normal file
47
mkdocs.yml
Normal file
@ -0,0 +1,47 @@
|
||||
site_name: copier
|
||||
site_description: Library and command-line utility for rendering projects templates.
|
||||
site_url: https://copier.readthedocs.io/
|
||||
repo_url: https://github.com/copier-org/copier
|
||||
repo_name: copier-org/copier
|
||||
|
||||
nav:
|
||||
- Overview: "index.md"
|
||||
- Creating a template: "creating.md"
|
||||
- Generating a project: "generating.md"
|
||||
- Updating a project: "updating.md"
|
||||
- API:
|
||||
- Main usage: "api/index.md"
|
||||
- Reference:
|
||||
- config:
|
||||
- factory.py: "api/reference/config/factory.md"
|
||||
- objects.py: "api/reference/config/objects.md"
|
||||
- user_data.py: "api/reference/config/user_data.md"
|
||||
- cli.py: "api/reference/cli.md"
|
||||
- main.py: "api/reference/main.md"
|
||||
- tools.py: "api/reference/tools.md"
|
||||
- types.py: "api/reference/types.md"
|
||||
- vcs.py: "api/reference/vcs.md"
|
||||
- Contributing: "contributing.md"
|
||||
- Changelog: "changelog.md"
|
||||
|
||||
theme:
|
||||
name: "material"
|
||||
|
||||
extra_css:
|
||||
- css/mkdocstrings.css
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- codehilite:
|
||||
guess_lang: false
|
||||
- pymdownx.superfences
|
||||
- pymdownx.emoji
|
||||
- pymdownx.magiclink
|
||||
- toc:
|
||||
permalink: true
|
||||
|
||||
plugins:
|
||||
- search
|
||||
- mkdocstrings:
|
||||
watch:
|
||||
- copier
|
285
poetry.lock
generated
285
poetry.lock
generated
@ -55,6 +55,21 @@ optional = false
|
||||
python-versions = "*"
|
||||
version = "0.2.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Screen-scraping library"
|
||||
name = "beautifulsoup4"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "4.9.1"
|
||||
|
||||
[package.dependencies]
|
||||
soupsieve = [">1.2", "<2.0"]
|
||||
|
||||
[package.extras]
|
||||
html5lib = ["html5lib"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "The uncompromising code formatter."
|
||||
@ -225,6 +240,14 @@ version = "3.2.1"
|
||||
flake8 = ">=1.5"
|
||||
pycodestyle = "*"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Clean single-source support for Python 3 and 2"
|
||||
name = "future"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
version = "0.18.2"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "File identification library for Python"
|
||||
@ -360,6 +383,66 @@ MarkupSafe = ">=0.23"
|
||||
[package.extras]
|
||||
i18n = ["Babel (>=0.8)"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Lightweight pipelining: using Python functions as pipeline jobs."
|
||||
marker = "python_version > \"2.7\""
|
||||
name = "joblib"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "0.16.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Python LiveReload is an awesome tool for web developers"
|
||||
name = "livereload"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "2.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
six = "*"
|
||||
|
||||
[package.dependencies.tornado]
|
||||
python = ">=2.8"
|
||||
version = "*"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A Python implementation of Lunr.js"
|
||||
name = "lunr"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.5.8"
|
||||
|
||||
[package.dependencies]
|
||||
future = ">=0.16.0"
|
||||
six = ">=1.11.0"
|
||||
|
||||
[package.dependencies.nltk]
|
||||
optional = true
|
||||
python = ">=2.8"
|
||||
version = ">=3.2.5"
|
||||
|
||||
[package.extras]
|
||||
languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Python implementation of Markdown."
|
||||
name = "markdown"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "3.2.2"
|
||||
|
||||
[package.dependencies]
|
||||
[package.dependencies.importlib-metadata]
|
||||
python = "<3.8"
|
||||
version = "*"
|
||||
|
||||
[package.extras]
|
||||
testing = ["coverage", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
@ -376,6 +459,66 @@ optional = false
|
||||
python-versions = "*"
|
||||
version = "0.6.1"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Project documentation with Markdown."
|
||||
name = "mkdocs"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "1.1.2"
|
||||
|
||||
[package.dependencies]
|
||||
Jinja2 = ">=2.10.1"
|
||||
Markdown = ">=3.2.1"
|
||||
PyYAML = ">=3.10"
|
||||
click = ">=3.3"
|
||||
livereload = ">=2.5.1"
|
||||
tornado = ">=5.0"
|
||||
|
||||
[package.dependencies.lunr]
|
||||
extras = ["languages"]
|
||||
version = "0.5.8"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A Material Design theme for MkDocs"
|
||||
name = "mkdocs-material"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "5.4.0"
|
||||
|
||||
[package.dependencies]
|
||||
Pygments = ">=2.4"
|
||||
markdown = ">=3.2"
|
||||
mkdocs = ">=1.1"
|
||||
mkdocs-material-extensions = ">=1.0"
|
||||
pymdown-extensions = ">=7.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Extension pack for Python Markdown."
|
||||
name = "mkdocs-material-extensions"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "1.0"
|
||||
|
||||
[package.dependencies]
|
||||
mkdocs-material = ">=5.0.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Automatic documentation from sources, for MkDocs."
|
||||
name = "mkdocstrings"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4.0"
|
||||
version = "0.12.1"
|
||||
|
||||
[package.dependencies]
|
||||
beautifulsoup4 = ">=4.8.2,<5.0.0"
|
||||
mkdocs = ">=1.1,<2.0"
|
||||
pymdown-extensions = ">=6.3,<8.0"
|
||||
pytkdocs = ">=0.2.0,<0.7.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "More routines for operating on iterables, beyond itertools"
|
||||
@ -408,6 +551,29 @@ optional = false
|
||||
python-versions = "*"
|
||||
version = "0.4.3"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Natural Language Toolkit"
|
||||
marker = "python_version > \"2.7\""
|
||||
name = "nltk"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "3.5"
|
||||
|
||||
[package.dependencies]
|
||||
click = "*"
|
||||
joblib = "*"
|
||||
regex = "*"
|
||||
tqdm = "*"
|
||||
|
||||
[package.extras]
|
||||
all = ["requests", "numpy", "python-crfsuite", "scikit-learn", "twython", "pyparsing", "scipy", "matplotlib", "gensim"]
|
||||
corenlp = ["requests"]
|
||||
machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"]
|
||||
plot = ["matplotlib"]
|
||||
tgrep = ["pyparsing"]
|
||||
twitter = ["twython"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Node.js virtual environment builder"
|
||||
@ -583,12 +749,22 @@ version = "2.2.0"
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
marker = "python_version >= \"3.4\""
|
||||
name = "pygments"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "2.6.1"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Extension pack for Python Markdown."
|
||||
name = "pymdown-extensions"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
|
||||
version = "7.1"
|
||||
|
||||
[package.dependencies]
|
||||
Markdown = ">=3.2"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Python parsing module"
|
||||
@ -677,6 +853,14 @@ six = "*"
|
||||
[package.extras]
|
||||
testing = ["filelock"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Load Python objects documentation."
|
||||
name = "pytkdocs"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4.0"
|
||||
version = "0.6.0"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "YAML parser and emitter for Python"
|
||||
@ -716,6 +900,14 @@ optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
version = "1.15.0"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "A modern CSS selector implementation for Beautiful Soup."
|
||||
name = "soupsieve"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "1.9.6"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
@ -724,6 +916,26 @@ optional = false
|
||||
python-versions = "*"
|
||||
version = "0.10.1"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
|
||||
name = "tornado"
|
||||
optional = false
|
||||
python-versions = ">= 3.5"
|
||||
version = "6.0.4"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Fast, Extensible Progress Meter"
|
||||
marker = "python_version > \"2.7\""
|
||||
name = "tqdm"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
|
||||
version = "4.48.0"
|
||||
|
||||
[package.extras]
|
||||
dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"]
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Traitlets Python config system"
|
||||
@ -805,7 +1017,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["jaraco.itertools", "func-timeout"]
|
||||
|
||||
[metadata]
|
||||
content-hash = "b3a1fc5823ae3bb22b89ef9a0034a9759afdbd94fc9de416392c4050dda95cb2"
|
||||
content-hash = "7d7386c290b8b92a9a9a35c6e00862c60a4834deb36f9284be8524a556ee163a"
|
||||
python-versions = "^3.6"
|
||||
|
||||
[metadata.files]
|
||||
@ -833,6 +1045,11 @@ backcall = [
|
||||
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
|
||||
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
|
||||
]
|
||||
beautifulsoup4 = [
|
||||
{file = "beautifulsoup4-4.9.1-py2-none-any.whl", hash = "sha256:e718f2342e2e099b640a34ab782407b7b676f47ee272d6739e60b8ea23829f2c"},
|
||||
{file = "beautifulsoup4-4.9.1-py3-none-any.whl", hash = "sha256:a6237df3c32ccfaee4fd201c8f5f9d9df619b93121d01353a64a73ce8c6ef9a8"},
|
||||
{file = "beautifulsoup4-4.9.1.tar.gz", hash = "sha256:73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7"},
|
||||
]
|
||||
black = [
|
||||
{file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"},
|
||||
{file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"},
|
||||
@ -920,6 +1137,9 @@ flake8-comprehensions = [
|
||||
flake8-debugger = [
|
||||
{file = "flake8-debugger-3.2.1.tar.gz", hash = "sha256:712d7c1ff69ddf3f0130e94cc88c2519e720760bce45e8c330bfdcb61ab4090d"},
|
||||
]
|
||||
future = [
|
||||
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
||||
]
|
||||
identify = [
|
||||
{file = "identify-1.4.19-py2.py3-none-any.whl", hash = "sha256:781fd3401f5d2b17b22a8b18b493a48d5d948e3330634e82742e23f9c20234ef"},
|
||||
{file = "identify-1.4.19.tar.gz", hash = "sha256:249ebc7e2066d6393d27c1b1be3b70433f824a120b1d8274d362f1eb419e3b52"},
|
||||
@ -951,6 +1171,21 @@ jinja2 = [
|
||||
{file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
|
||||
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
|
||||
]
|
||||
joblib = [
|
||||
{file = "joblib-0.16.0-py3-none-any.whl", hash = "sha256:d348c5d4ae31496b2aa060d6d9b787864dd204f9480baaa52d18850cb43e9f49"},
|
||||
{file = "joblib-0.16.0.tar.gz", hash = "sha256:8f52bf24c64b608bf0b2563e0e47d6fcf516abc8cfafe10cfd98ad66d94f92d6"},
|
||||
]
|
||||
livereload = [
|
||||
{file = "livereload-2.6.2.tar.gz", hash = "sha256:d1eddcb5c5eb8d2ca1fa1f750e580da624c0f7fcb734aa5780dc81b7dcbd89be"},
|
||||
]
|
||||
lunr = [
|
||||
{file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"},
|
||||
{file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"},
|
||||
]
|
||||
markdown = [
|
||||
{file = "Markdown-3.2.2-py3-none-any.whl", hash = "sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59"},
|
||||
{file = "Markdown-3.2.2.tar.gz", hash = "sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17"},
|
||||
]
|
||||
markupsafe = [
|
||||
{file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
|
||||
{file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
|
||||
@ -990,6 +1225,22 @@ mccabe = [
|
||||
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
|
||||
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
|
||||
]
|
||||
mkdocs = [
|
||||
{file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"},
|
||||
{file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"},
|
||||
]
|
||||
mkdocs-material = [
|
||||
{file = "mkdocs-material-5.4.0.tar.gz", hash = "sha256:7cd0fabc336ef93c78693134a0f9ac62900a76f905489aa935bab0b50910c47b"},
|
||||
{file = "mkdocs_material-5.4.0-py2.py3-none-any.whl", hash = "sha256:bf34d5cfbb2a085187adfcb82e8fb3bfc3014860f8416c2656dc8e9666b82eb0"},
|
||||
]
|
||||
mkdocs-material-extensions = [
|
||||
{file = "mkdocs-material-extensions-1.0.tar.gz", hash = "sha256:17d7491e189af75700310b7ec33c6c48a22060b8b445001deca040cb60471cde"},
|
||||
{file = "mkdocs_material_extensions-1.0-py3-none-any.whl", hash = "sha256:09569c3694b5acc1e8334c9730e52b4bcde65fc9d613cc20e49af131ef1a9ca0"},
|
||||
]
|
||||
mkdocstrings = [
|
||||
{file = "mkdocstrings-0.12.1-py3-none-any.whl", hash = "sha256:346a881267c4fdc581ba910038a7e5690ed7d99c34420a393da847d79b9bfab3"},
|
||||
{file = "mkdocstrings-0.12.1.tar.gz", hash = "sha256:10632762d0f4f7d243912f42e370782f4a06edd49372f5729001fbbfcecffa83"},
|
||||
]
|
||||
more-itertools = [
|
||||
{file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"},
|
||||
{file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"},
|
||||
@ -1014,6 +1265,9 @@ mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
]
|
||||
nltk = [
|
||||
{file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"},
|
||||
]
|
||||
nodeenv = [
|
||||
{file = "nodeenv-1.4.0-py2.py3-none-any.whl", hash = "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"},
|
||||
]
|
||||
@ -1092,6 +1346,10 @@ pygments = [
|
||||
{file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"},
|
||||
{file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
|
||||
]
|
||||
pymdown-extensions = [
|
||||
{file = "pymdown-extensions-7.1.tar.gz", hash = "sha256:5bf93d1ccd8281948cd7c559eb363e59b179b5373478e8a7195cf4b78e3c11b6"},
|
||||
{file = "pymdown_extensions-7.1-py2.py3-none-any.whl", hash = "sha256:8f415b21ee86d80bb2c3676f4478b274d0a8ccb13af672a4c86b9ffd22bd005c"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
||||
@ -1116,6 +1374,10 @@ pytest-xdist = [
|
||||
{file = "pytest-xdist-1.32.0.tar.gz", hash = "sha256:1d4166dcac69adb38eeaedb88c8fada8588348258a3492ab49ba9161f2971129"},
|
||||
{file = "pytest_xdist-1.32.0-py2.py3-none-any.whl", hash = "sha256:ba5ec9fde3410bd9a116ff7e4f26c92e02fa3d27975ef3ad03f330b3d4b54e91"},
|
||||
]
|
||||
pytkdocs = [
|
||||
{file = "pytkdocs-0.6.0-py3-none-any.whl", hash = "sha256:e5675088061ab1950688e52e581d21a98f5d4945b92fe55ad26ba4a0670359ef"},
|
||||
{file = "pytkdocs-0.6.0.tar.gz", hash = "sha256:4dec18d0465aa84e38629ea3b32c2215e2bc4714c611b0b64c35b786aae1f180"},
|
||||
]
|
||||
pyyaml = [
|
||||
{file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"},
|
||||
{file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"},
|
||||
@ -1160,10 +1422,29 @@ six = [
|
||||
{file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
|
||||
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
|
||||
]
|
||||
soupsieve = [
|
||||
{file = "soupsieve-1.9.6-py2.py3-none-any.whl", hash = "sha256:feb1e937fa26a69e08436aad4a9037cd7e1d4c7212909502ba30701247ff8abd"},
|
||||
{file = "soupsieve-1.9.6.tar.gz", hash = "sha256:7985bacc98c34923a439967c1a602dc4f1e15f923b6fcf02344184f86cc7efaa"},
|
||||
]
|
||||
toml = [
|
||||
{file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"},
|
||||
{file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
|
||||
]
|
||||
tornado = [
|
||||
{file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"},
|
||||
{file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"},
|
||||
{file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"},
|
||||
{file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"},
|
||||
{file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"},
|
||||
{file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"},
|
||||
{file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"},
|
||||
{file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"},
|
||||
{file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"},
|
||||
]
|
||||
tqdm = [
|
||||
{file = "tqdm-4.48.0-py2.py3-none-any.whl", hash = "sha256:fcb7cb5b729b60a27f300b15c1ffd4744f080fb483b88f31dc8654b082cc8ea5"},
|
||||
{file = "tqdm-4.48.0.tar.gz", hash = "sha256:6baa75a88582b1db6d34ce4690da5501d2a1cb65c34664840a456b2c9f794d29"},
|
||||
]
|
||||
traitlets = [
|
||||
{file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"},
|
||||
{file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
|
||||
|
@ -53,6 +53,9 @@ pytest = "*"
|
||||
pytest-cov = "*"
|
||||
pytest-xdist = "*"
|
||||
pytest-timeout = "^1.4.1"
|
||||
mkdocs = "^1.1.2"
|
||||
mkdocstrings = "^0.12.1"
|
||||
mkdocs-material = "^5.4.0"
|
||||
|
||||
[tool.poetry-dynamic-versioning]
|
||||
enable = true
|
||||
|
Loading…
x
Reference in New Issue
Block a user