mirror of
https://github.com/copier-org/copier.git
synced 2025-05-05 23:42:55 +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
|
# Documentation
|
||||||
*/_build/*
|
*/_build/*
|
||||||
*/site/*
|
*/site/*
|
||||||
|
/site
|
||||||
.mypy_cache/*
|
.mypy_cache/*
|
||||||
.cache
|
.cache
|
||||||
*.log
|
*.log
|
||||||
|
@ -70,14 +70,3 @@ repos:
|
|||||||
- id: mixed-line-ending
|
- id: mixed-line-ending
|
||||||
args: ["--fix=lf"]
|
args: ["--fix=lf"]
|
||||||
- id: trailing-whitespace
|
- 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
|
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)
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||||
|
|
||||||
<details>
|
### Version 4.0.2 (2020-07-21)
|
||||||
<!-- 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.0 (2020-06)](#version-400-2020-06)
|
[All changes here](https://github.com/copier-org/copier/milestone/11?closed=1). Summary:
|
||||||
- [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)
|
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
- Fix wrong templated default answers classification, which produced some questions being ignored.
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
</details>
|
### 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)
|
### Version 4.0.0 (2020-06)
|
||||||
|
|
||||||
|
@ -3,25 +3,6 @@
|
|||||||
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and
|
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and
|
||||||
credit will always be given.
|
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
|
||||||
|
|
||||||
Report bugs at <https://github.com/jpscaletti/copier/issues>.
|
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 '__pycache__' -exec rm -rf {} +
|
||||||
find . -name '.pytest_cache' -exec rm -rf {} +
|
find . -name '.pytest_cache' -exec rm -rf {} +
|
||||||
|
|
||||||
|
.PHONY: docs
|
||||||
|
docs:
|
||||||
|
mkdocs serve
|
||||||
|
|
||||||
test:
|
test:
|
||||||
pytest -x copier tests
|
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
|
## Installation
|
||||||
|
|
||||||
1. Install Git 2.24 or newer.
|
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
|
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
|
## Browse or tag public templates
|
||||||
|
|
||||||
You can browse public copier templates in GitHub using
|
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! 🏷
|
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
|
## Comparison with other project generators
|
||||||
|
|
||||||
### Cookiecutter
|
### Cookiecutter
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
Command line entrypoint.
|
||||||
|
|
||||||
|
This module declares the Plumbum CLI application, its subcommand and options.
|
||||||
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
@ -7,10 +13,12 @@ from plumbum import cli, colors
|
|||||||
from . import __version__
|
from . import __version__
|
||||||
from .config.objects import UserMessageError
|
from .config.objects import UserMessageError
|
||||||
from .main import copy
|
from .main import copy
|
||||||
from .types import AnyByStrDict, OptStr
|
from .types import AnyByStrDict, List, OptStr
|
||||||
|
|
||||||
|
|
||||||
def handle_exceptions(method):
|
def handle_exceptions(method):
|
||||||
|
"""Handle keyboard interruption while running a method."""
|
||||||
|
|
||||||
def _wrapper(*args, **kwargs):
|
def _wrapper(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
@ -25,6 +33,21 @@ def handle_exceptions(method):
|
|||||||
|
|
||||||
|
|
||||||
class CopierApp(cli.Application):
|
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 = "Create a new project from a template."
|
||||||
DESCRIPTION_MORE = colors.yellow | dedent(
|
DESCRIPTION_MORE = colors.yellow | dedent(
|
||||||
"""
|
"""
|
||||||
@ -42,7 +65,7 @@ class CopierApp(cli.Application):
|
|||||||
CALL_MAIN_IF_NESTED_COMMAND = False
|
CALL_MAIN_IF_NESTED_COMMAND = False
|
||||||
data: AnyByStrDict = {}
|
data: AnyByStrDict = {}
|
||||||
|
|
||||||
answers_file = cli.SwitchAttr(
|
answers_file: cli.SwitchAttr = cli.SwitchAttr(
|
||||||
["-a", "--answers-file"],
|
["-a", "--answers-file"],
|
||||||
default=None,
|
default=None,
|
||||||
help=(
|
help=(
|
||||||
@ -50,13 +73,13 @@ class CopierApp(cli.Application):
|
|||||||
"to find the answers file"
|
"to find the answers file"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
extra_paths = cli.SwitchAttr(
|
extra_paths: cli.SwitchAttr = cli.SwitchAttr(
|
||||||
["-p", "--extra-paths"],
|
["-p", "--extra-paths"],
|
||||||
str,
|
str,
|
||||||
list=True,
|
list=True,
|
||||||
help="Additional directories to find parent templates in",
|
help="Additional directories to find parent templates in",
|
||||||
)
|
)
|
||||||
exclude = cli.SwitchAttr(
|
exclude: cli.SwitchAttr = cli.SwitchAttr(
|
||||||
["-x", "--exclude"],
|
["-x", "--exclude"],
|
||||||
str,
|
str,
|
||||||
list=True,
|
list=True,
|
||||||
@ -65,7 +88,7 @@ class CopierApp(cli.Application):
|
|||||||
"that must not be copied"
|
"that must not be copied"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
vcs_ref = cli.SwitchAttr(
|
vcs_ref: cli.SwitchAttr = cli.SwitchAttr(
|
||||||
["-r", "--vcs-ref"],
|
["-r", "--vcs-ref"],
|
||||||
str,
|
str,
|
||||||
help=(
|
help=(
|
||||||
@ -75,7 +98,7 @@ class CopierApp(cli.Application):
|
|||||||
"the latest version, use `--vcs-ref=HEAD`."
|
"the latest version, use `--vcs-ref=HEAD`."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
subdirectory = cli.SwitchAttr(
|
subdirectory: cli.SwitchAttr = cli.SwitchAttr(
|
||||||
["-b", "--subdirectory"],
|
["-b", "--subdirectory"],
|
||||||
str,
|
str,
|
||||||
help=(
|
help=(
|
||||||
@ -84,14 +107,16 @@ class CopierApp(cli.Application):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
pretend = cli.Flag(["-n", "--pretend"], help="Run but do not make any changes")
|
pretend: cli.Flag = cli.Flag(
|
||||||
force = 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"
|
["-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"
|
["-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(
|
@cli.switch(
|
||||||
["-d", "--data"],
|
["-d", "--data"],
|
||||||
@ -100,11 +125,25 @@ class CopierApp(cli.Application):
|
|||||||
list=True,
|
list=True,
|
||||||
help="Make VARIABLE available as VALUE when rendering the template",
|
help="Make VARIABLE available as VALUE when rendering the template",
|
||||||
)
|
)
|
||||||
def data_switch(self, values):
|
def data_switch(self, values: List[str]) -> None:
|
||||||
self.data.update(value.split("=", 1) for value in values)
|
"""
|
||||||
|
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:
|
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(
|
return copy(
|
||||||
data=self.data,
|
data=self.data,
|
||||||
dst_path=dst_path,
|
dst_path=dst_path,
|
||||||
@ -151,16 +190,26 @@ class CopierApp(cli.Application):
|
|||||||
|
|
||||||
@CopierApp.subcommand("copy")
|
@CopierApp.subcommand("copy")
|
||||||
class CopierCopySubApp(cli.Application):
|
class CopierCopySubApp(cli.Application):
|
||||||
|
"""The `copy` subcommand."""
|
||||||
|
|
||||||
DESCRIPTION = "Copy form a template source to a destination"
|
DESCRIPTION = "Copy form a template source to a destination"
|
||||||
|
|
||||||
@handle_exceptions
|
@handle_exceptions
|
||||||
def main(self, template_src: str, destination_path: str) -> int:
|
def main(self, template_src: str, destination_path: str) -> int:
|
||||||
|
"""The code of the `copy` subcommand."""
|
||||||
self.parent._copy(template_src, destination_path)
|
self.parent._copy(template_src, destination_path)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@CopierApp.subcommand("update")
|
@CopierApp.subcommand("update")
|
||||||
class CopierUpdateSubApp(cli.Application):
|
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 = "Update a copy from its original template"
|
||||||
DESCRIPTION_MORE = """
|
DESCRIPTION_MORE = """
|
||||||
The copy must have a valid answers file which contains info
|
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`.
|
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."
|
["-D", "--no-diff"], default=True, help="Disable smart diff detection."
|
||||||
)
|
)
|
||||||
|
|
||||||
@handle_exceptions
|
@handle_exceptions
|
||||||
def main(self, destination_path: cli.ExistingDirectory = ".") -> int:
|
def main(self, destination_path: cli.ExistingDirectory = ".") -> int:
|
||||||
|
"""The code of the `update` subcommand."""
|
||||||
self.parent._copy(
|
self.parent._copy(
|
||||||
dst_path=destination_path, only_diff=self.only_diff,
|
dst_path=destination_path, only_diff=self.only_diff,
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
"""Functions used to generate configuration data."""
|
||||||
|
|
||||||
from collections import ChainMap
|
from collections import ChainMap
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
"""Pydantic models, exceptions and default values."""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from collections import ChainMap
|
from collections import ChainMap
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
@ -41,6 +43,8 @@ class NoSrcPathError(UserMessageError):
|
|||||||
|
|
||||||
|
|
||||||
class EnvOps(BaseModel):
|
class EnvOps(BaseModel):
|
||||||
|
"""Jinja2 environment options."""
|
||||||
|
|
||||||
autoescape: StrictBool = False
|
autoescape: StrictBool = False
|
||||||
block_start_string: str = "[%"
|
block_start_string: str = "[%"
|
||||||
block_end_string: str = "%]"
|
block_end_string: str = "%]"
|
||||||
@ -62,6 +66,8 @@ class Migrations(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ConfigData(BaseModel):
|
class ConfigData(BaseModel):
|
||||||
|
"""A model holding configuration data."""
|
||||||
|
|
||||||
src_path: Path
|
src_path: Path
|
||||||
subdirectory: OptStr
|
subdirectory: OptStr
|
||||||
dst_path: Path
|
dst_path: Path
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
"""Functions used to load user data."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from collections import ChainMap
|
from collections import ChainMap
|
||||||
|
121
copier/main.py
121
copier/main.py
@ -1,3 +1,5 @@
|
|||||||
|
"""The main functions, used to generate or update projects."""
|
||||||
|
|
||||||
import filecmp
|
import filecmp
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@ -58,71 +60,70 @@ def copy(
|
|||||||
subdirectory: OptStr = None,
|
subdirectory: OptStr = None,
|
||||||
) -> 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:
|
Arguments:
|
||||||
|
src_path:
|
||||||
- src_path (str):
|
|
||||||
Absolute path to the project skeleton. May be a version control system URL.
|
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.
|
If `None`, it will be taken from `dst_path/answers_file` or fail.
|
||||||
|
|
||||||
- dst_path (str):
|
dst_path:
|
||||||
Absolute path to where to render the skeleton
|
Absolute path to where to render the skeleton
|
||||||
|
|
||||||
- data (dict):
|
data:
|
||||||
Optional. Data to be passed to the templates in addtion to the user data
|
Optional. Data to be passed to the templates in addtion to the user data
|
||||||
from a `copier.json`.
|
from a `copier.json`.
|
||||||
|
|
||||||
- answers_file (str):
|
answers_file:
|
||||||
Path where to obtain the answers recorded from the last update. The path
|
Path where to obtain the answers recorded from the last update.
|
||||||
must be relative to `dst_path`.
|
The path must be relative to `dst_path`.
|
||||||
|
|
||||||
- exclude (list):
|
exclude:
|
||||||
A list of names or gitignore-style patterns matching files or folders that
|
A list of names or gitignore-style patterns matching files or folders that
|
||||||
must not be copied.
|
must not be copied.
|
||||||
|
|
||||||
- skip_if_exists (list):
|
skip_if_exists:
|
||||||
A list of names or gitignore-style patterns matching files or folders,
|
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
|
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
|
destination folder. (It only makes sense if you are copying to a folder
|
||||||
that already exists).
|
that already exists).
|
||||||
|
|
||||||
- tasks (list):
|
tasks:
|
||||||
Optional lists of commands to run in order after finishing the copy.
|
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
|
Like in the templates files, you can use variables on the commands that
|
||||||
will be replaced by the real values before running the command.
|
will be replaced by the real values before running the command.
|
||||||
If one of the commands fail, the rest of them will not run.
|
If one of the commands fail, the rest of them will not run.
|
||||||
|
|
||||||
- envops (dict):
|
envops:
|
||||||
Extra options for the Jinja template environment.
|
Extra options for the Jinja template environment.
|
||||||
|
|
||||||
- extra_paths (list):
|
extra_paths:
|
||||||
Optional. Additional paths, outside the `src_path`, from where to search
|
Optional. Additional paths, outside the `src_path`, from where to search
|
||||||
for templates. This is intended to be used with shared parent templates,
|
for templates. This is intended to be used with shared parent templates,
|
||||||
files with macros, etc. outside the copied project skeleton.
|
files with macros, etc. outside the copied project skeleton.
|
||||||
|
|
||||||
- pretend (bool):
|
pretend:
|
||||||
Run but do not make any changes
|
Run but do not make any changes.
|
||||||
|
|
||||||
- force (bool):
|
force:
|
||||||
Overwrite files that already exist, without asking
|
Overwrite files that already exist, without asking.
|
||||||
|
|
||||||
- skip (bool):
|
skip:
|
||||||
Skip files that already exist, without asking
|
Skip files that already exist, without asking.
|
||||||
|
|
||||||
- quiet (bool):
|
quiet:
|
||||||
Suppress the status output
|
Suppress the status output.
|
||||||
|
|
||||||
- cleanup_on_error (bool):
|
cleanup_on_error:
|
||||||
Remove the destination folder if the copy process or one of the tasks fail.
|
Remove the destination folder if the copy process or one of the tasks fail.
|
||||||
|
|
||||||
- vcs_ref (str):
|
vcs_ref:
|
||||||
VCS reference to checkout in the template.
|
VCS reference to checkout in the template.
|
||||||
|
|
||||||
- only_diff (bool):
|
only_diff:
|
||||||
Try to update only the template diff.
|
Try to update only the template diff.
|
||||||
|
|
||||||
- subdirectory (str):
|
subdirectory:
|
||||||
Specify a subdirectory to use when generating the project.
|
Specify a subdirectory to use when generating the project.
|
||||||
"""
|
"""
|
||||||
conf = make_config(**locals())
|
conf = make_config(**locals())
|
||||||
@ -151,7 +152,11 @@ def copy(
|
|||||||
|
|
||||||
|
|
||||||
def copy_local(conf: ConfigData) -> None:
|
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)
|
must_filter = create_path_filter(conf.exclude)
|
||||||
|
|
||||||
render = Renderer(conf)
|
render = Renderer(conf)
|
||||||
@ -199,7 +204,12 @@ def copy_local(conf: ConfigData) -> None:
|
|||||||
print("") # padding space
|
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
|
# Ensure local repo is clean
|
||||||
if vcs.is_git_repo_root(conf.dst_path):
|
if vcs.is_git_repo_root(conf.dst_path):
|
||||||
with local.cwd(conf.dst_path):
|
with local.cwd(conf.dst_path):
|
||||||
@ -273,6 +283,19 @@ def get_source_paths(
|
|||||||
render: Renderer,
|
render: Renderer,
|
||||||
must_filter: Callable[[StrOrPath], bool],
|
must_filter: Callable[[StrOrPath], bool],
|
||||||
) -> List[Tuple[Path, Path]]:
|
) -> 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 = []
|
source_paths = []
|
||||||
files_set = set(files)
|
files_set = set(files)
|
||||||
for src_name in files:
|
for src_name in files:
|
||||||
@ -294,6 +317,14 @@ def get_source_paths(
|
|||||||
|
|
||||||
|
|
||||||
def render_folder(rel_folder: Path, conf: ConfigData) -> None:
|
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
|
dst_path = conf.dst_path / rel_folder
|
||||||
rel_path = f"{rel_folder}{os.path.sep}"
|
rel_path = f"{rel_folder}{os.path.sep}"
|
||||||
|
|
||||||
@ -319,7 +350,15 @@ def render_file(
|
|||||||
render: Renderer,
|
render: Renderer,
|
||||||
must_skip: CheckPathFunc,
|
must_skip: CheckPathFunc,
|
||||||
) -> None:
|
) -> 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
|
content: Optional[str] = None
|
||||||
if str(src_path).endswith(conf.templates_suffix):
|
if str(src_path).endswith(conf.templates_suffix):
|
||||||
content = render(src_path)
|
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:
|
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:
|
if content is None:
|
||||||
return filecmp.cmp(str(src_path), str(dst_path), shallow=False)
|
return filecmp.cmp(str(src_path), str(dst_path), shallow=False)
|
||||||
return dst_path.read_text() == content
|
return dst_path.read_text() == content
|
||||||
|
|
||||||
|
|
||||||
def overwrite_file(conf: ConfigData, dst_path: Path, rel_path: Path) -> bool:
|
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)
|
printf("conflict", rel_path, style=Style.DANGER, quiet=conf.quiet)
|
||||||
if conf.force:
|
if conf.force:
|
||||||
return True
|
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:
|
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):
|
for i, task in enumerate(tasks):
|
||||||
task_cmd = task["task"]
|
task_cmd = task["task"]
|
||||||
use_shell = isinstance(task_cmd, str)
|
use_shell = isinstance(task_cmd, str)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
"""Some utility functions."""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@ -125,6 +127,8 @@ def get_jinja_env(
|
|||||||
|
|
||||||
|
|
||||||
class Renderer:
|
class Renderer:
|
||||||
|
"""The Jinja template renderer."""
|
||||||
|
|
||||||
def __init__(self, conf: ConfigData) -> None:
|
def __init__(self, conf: ConfigData) -> None:
|
||||||
envops: EnvOps = conf.envops
|
envops: EnvOps = conf.envops
|
||||||
paths = [str(conf.src_path)] + list(map(str, conf.extra_paths or []))
|
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 pathlib import Path
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
"""Utilities related to VCS."""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
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 = "*"
|
python-versions = "*"
|
||||||
version = "0.2.0"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "The uncompromising code formatter."
|
description = "The uncompromising code formatter."
|
||||||
@ -225,6 +240,14 @@ version = "3.2.1"
|
|||||||
flake8 = ">=1.5"
|
flake8 = ">=1.5"
|
||||||
pycodestyle = "*"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "File identification library for Python"
|
description = "File identification library for Python"
|
||||||
@ -360,6 +383,66 @@ MarkupSafe = ">=0.23"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
i18n = ["Babel (>=0.8)"]
|
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]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Safely add untrusted strings to HTML/XML markup."
|
description = "Safely add untrusted strings to HTML/XML markup."
|
||||||
@ -376,6 +459,66 @@ optional = false
|
|||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.6.1"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "More routines for operating on iterables, beyond itertools"
|
description = "More routines for operating on iterables, beyond itertools"
|
||||||
@ -408,6 +551,29 @@ optional = false
|
|||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.4.3"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Node.js virtual environment builder"
|
description = "Node.js virtual environment builder"
|
||||||
@ -583,12 +749,22 @@ version = "2.2.0"
|
|||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Pygments is a syntax highlighting package written in Python."
|
description = "Pygments is a syntax highlighting package written in Python."
|
||||||
marker = "python_version >= \"3.4\""
|
|
||||||
name = "pygments"
|
name = "pygments"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
version = "2.6.1"
|
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]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Python parsing module"
|
description = "Python parsing module"
|
||||||
@ -677,6 +853,14 @@ six = "*"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["filelock"]
|
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]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "YAML parser and emitter for Python"
|
description = "YAML parser and emitter for Python"
|
||||||
@ -716,6 +900,14 @@ optional = false
|
|||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
version = "1.15.0"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||||
@ -724,6 +916,26 @@ optional = false
|
|||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.10.1"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Traitlets Python config system"
|
description = "Traitlets Python config system"
|
||||||
@ -805,7 +1017,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
|||||||
testing = ["jaraco.itertools", "func-timeout"]
|
testing = ["jaraco.itertools", "func-timeout"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "b3a1fc5823ae3bb22b89ef9a0034a9759afdbd94fc9de416392c4050dda95cb2"
|
content-hash = "7d7386c290b8b92a9a9a35c6e00862c60a4834deb36f9284be8524a556ee163a"
|
||||||
python-versions = "^3.6"
|
python-versions = "^3.6"
|
||||||
|
|
||||||
[metadata.files]
|
[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-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
|
||||||
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
|
{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 = [
|
black = [
|
||||||
{file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"},
|
{file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"},
|
||||||
{file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"},
|
{file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"},
|
||||||
@ -920,6 +1137,9 @@ flake8-comprehensions = [
|
|||||||
flake8-debugger = [
|
flake8-debugger = [
|
||||||
{file = "flake8-debugger-3.2.1.tar.gz", hash = "sha256:712d7c1ff69ddf3f0130e94cc88c2519e720760bce45e8c330bfdcb61ab4090d"},
|
{file = "flake8-debugger-3.2.1.tar.gz", hash = "sha256:712d7c1ff69ddf3f0130e94cc88c2519e720760bce45e8c330bfdcb61ab4090d"},
|
||||||
]
|
]
|
||||||
|
future = [
|
||||||
|
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
||||||
|
]
|
||||||
identify = [
|
identify = [
|
||||||
{file = "identify-1.4.19-py2.py3-none-any.whl", hash = "sha256:781fd3401f5d2b17b22a8b18b493a48d5d948e3330634e82742e23f9c20234ef"},
|
{file = "identify-1.4.19-py2.py3-none-any.whl", hash = "sha256:781fd3401f5d2b17b22a8b18b493a48d5d948e3330634e82742e23f9c20234ef"},
|
||||||
{file = "identify-1.4.19.tar.gz", hash = "sha256:249ebc7e2066d6393d27c1b1be3b70433f824a120b1d8274d362f1eb419e3b52"},
|
{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-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
|
||||||
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
|
{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 = [
|
markupsafe = [
|
||||||
{file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
|
{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"},
|
{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-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
|
||||||
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
|
{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 = [
|
more-itertools = [
|
||||||
{file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"},
|
{file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"},
|
||||||
{file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"},
|
{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-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||||
]
|
]
|
||||||
|
nltk = [
|
||||||
|
{file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"},
|
||||||
|
]
|
||||||
nodeenv = [
|
nodeenv = [
|
||||||
{file = "nodeenv-1.4.0-py2.py3-none-any.whl", hash = "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"},
|
{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-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"},
|
||||||
{file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
|
{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 = [
|
pyparsing = [
|
||||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
||||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
{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.tar.gz", hash = "sha256:1d4166dcac69adb38eeaedb88c8fada8588348258a3492ab49ba9161f2971129"},
|
||||||
{file = "pytest_xdist-1.32.0-py2.py3-none-any.whl", hash = "sha256:ba5ec9fde3410bd9a116ff7e4f26c92e02fa3d27975ef3ad03f330b3d4b54e91"},
|
{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 = [
|
pyyaml = [
|
||||||
{file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"},
|
{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"},
|
{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-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
|
||||||
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
|
{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 = [
|
toml = [
|
||||||
{file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"},
|
{file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"},
|
||||||
{file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
|
{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 = [
|
traitlets = [
|
||||||
{file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"},
|
{file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"},
|
||||||
{file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
|
{file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
|
||||||
|
@ -53,6 +53,9 @@ pytest = "*"
|
|||||||
pytest-cov = "*"
|
pytest-cov = "*"
|
||||||
pytest-xdist = "*"
|
pytest-xdist = "*"
|
||||||
pytest-timeout = "^1.4.1"
|
pytest-timeout = "^1.4.1"
|
||||||
|
mkdocs = "^1.1.2"
|
||||||
|
mkdocstrings = "^0.12.1"
|
||||||
|
mkdocs-material = "^5.4.0"
|
||||||
|
|
||||||
[tool.poetry-dynamic-versioning]
|
[tool.poetry-dynamic-versioning]
|
||||||
enable = true
|
enable = true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user