From 2471b9256d9d9dfea1124d20072201693b9b0865 Mon Sep 17 00:00:00 2001
From: Lihu Ben-Ezri-Ravin
Date: Wed, 24 Jun 2020 05:09:07 -0400
Subject: [PATCH 001/680] Find project root correctly (#1518)
Ensure root dir is a common parent of all inputs
Fixes #1493
---
src/black/__init__.py | 23 ++++++++++++++++-------
tests/test_black.py | 22 ++++++++++++++++++++++
2 files changed, 38 insertions(+), 7 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 2b2d3d88c73..d4c6e62bdbf 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -5825,8 +5825,8 @@ def gen_python_files(
def find_project_root(srcs: Iterable[str]) -> Path:
"""Return a directory containing .git, .hg, or pyproject.toml.
- That directory can be one of the directories passed in `srcs` or their
- common parent.
+ That directory will be a common parent of all files and directories
+ passed in `srcs`.
If no directory in the tree contains a marker that would specify it's the
project root, the root of the file system is returned.
@@ -5834,11 +5834,20 @@ def find_project_root(srcs: Iterable[str]) -> Path:
if not srcs:
return Path("/").resolve()
- common_base = min(Path(src).resolve() for src in srcs)
- if common_base.is_dir():
- # Append a fake file so `parents` below returns `common_base_dir`, too.
- common_base /= "fake-file"
- for directory in common_base.parents:
+ path_srcs = [Path(src).resolve() for src in srcs]
+
+ # A list of lists of parents for each 'src'. 'src' is included as a
+ # "parent" of itself if it is a directory
+ src_parents = [
+ list(path.parents) + ([path] if path.is_dir() else []) for path in path_srcs
+ ]
+
+ common_base = max(
+ set.intersection(*(set(parents) for parents in src_parents)),
+ key=lambda path: path.parts,
+ )
+
+ for directory in (common_base, *common_base.parents):
if (directory / ".git").exists():
return directory
diff --git a/tests/test_black.py b/tests/test_black.py
index 88839d86c5a..3ed5daa4b49 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -1801,6 +1801,28 @@ def __init__(self) -> None:
self.assertEqual(config["exclude"], r"\.pyi?$")
self.assertEqual(config["include"], r"\.py?$")
+ def test_find_project_root(self) -> None:
+ with TemporaryDirectory() as workspace:
+ root = Path(workspace)
+ test_dir = root / "test"
+ test_dir.mkdir()
+
+ src_dir = root / "src"
+ src_dir.mkdir()
+
+ root_pyproject = root / "pyproject.toml"
+ root_pyproject.touch()
+ src_pyproject = src_dir / "pyproject.toml"
+ src_pyproject.touch()
+ src_python = src_dir / "foo.py"
+ src_python.touch()
+
+ self.assertEqual(
+ black.find_project_root((src_dir, test_dir)), root.resolve()
+ )
+ self.assertEqual(black.find_project_root((src_dir,)), src_dir.resolve())
+ self.assertEqual(black.find_project_root((src_python,)), src_dir.resolve())
+
class BlackDTestCase(AioHTTPTestCase):
async def get_application(self) -> web.Application:
From 81a093e82a860c4b7e559532a847ccd967e6e456 Mon Sep 17 00:00:00 2001
From: Cooper Lees
Date: Wed, 1 Jul 2020 08:15:24 -0700
Subject: [PATCH 002/680] Add pip install from GitHub command to README.md
(#1529)
* Add pip install from GitHub command to README.md
* Make it prettier ...
---
README.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/README.md b/README.md
index beed8ba4943..4b65fb794ee 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,12 @@ _Contents:_ **[Installation and usage](#installation-and-usage)** |
_Black_ can be installed by running `pip install black`. It requires Python 3.6.0+ to
run but you can reformat Python 2 code with it, too.
+#### Install from GitHub
+
+If you can't wait for the latest _hotness_ and want to install from GitHub, use:
+
+`pip install git+git://github.com/psf/black`
+
### Usage
To get started right away with sensible defaults:
From 3209bf7ffa072fe6bc6fa700cec484bcdbd1d57c Mon Sep 17 00:00:00 2001
From: Sylvestre Ledru
Date: Mon, 6 Jul 2020 16:06:34 +0200
Subject: [PATCH 003/680] Mozilla uses black too (#1531)
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 4b65fb794ee..5ac76cbf786 100644
--- a/README.md
+++ b/README.md
@@ -435,7 +435,7 @@ code style: pytest, tox, Pyramid, Django Channels, Hypothesis, attrs, SQLAlchemy
Poetry, PyPA applications (Warehouse, Bandersnatch, Pipenv, virtualenv), pandas, Pillow,
every Datadog Agent Integration, Home Assistant.
-The following organizations use _Black_: Facebook, Dropbox.
+The following organizations use _Black_: Facebook, Dropbox, Mozilla.
Are we missing anyone? Let us know.
From 11f130b7deb74f424ebf57ef551953c9bc08393f Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra
Date: Mon, 6 Jul 2020 12:45:13 -0700
Subject: [PATCH 004/680] add Quora to orgs that use Black (#1532)
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 5ac76cbf786..fc2d4decf95 100644
--- a/README.md
+++ b/README.md
@@ -435,7 +435,7 @@ code style: pytest, tox, Pyramid, Django Channels, Hypothesis, attrs, SQLAlchemy
Poetry, PyPA applications (Warehouse, Bandersnatch, Pipenv, virtualenv), pandas, Pillow,
every Datadog Agent Integration, Home Assistant.
-The following organizations use _Black_: Facebook, Dropbox, Mozilla.
+The following organizations use _Black_: Facebook, Dropbox, Mozilla, Quora.
Are we missing anyone? Let us know.
From cc2facaac69a8ffa4486e6fe498842debb17ce12 Mon Sep 17 00:00:00 2001
From: Olexiy
Date: Wed, 8 Jul 2020 18:51:18 +0300
Subject: [PATCH 005/680] ISSUE 1533: Fix --config argument description (#1534)
Change --config argument description to "Read configuration from FILE."
The "--config FILE Read configuration from FILE path"
---
README.md | 2 +-
src/black/__init__.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index fc2d4decf95..33c170bd3c8 100644
--- a/README.md
+++ b/README.md
@@ -143,7 +143,7 @@ Options:
--exclude=.
--version Show the version and exit.
- --config FILE Read configuration from PATH.
+ --config FILE Read configuration from FILE path.
-h, --help Show this message and exit.
```
diff --git a/src/black/__init__.py b/src/black/__init__.py
index d4c6e62bdbf..3d0a2d69069 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -471,7 +471,7 @@ def target_version_option_callback(
),
is_eager=True,
callback=read_pyproject_toml,
- help="Read configuration from PATH.",
+ help="Read configuration from FILE path.",
)
@click.pass_context
def main(
From 8d036ceb3f0a0e0b52276e8d4afe840d1fcad11b Mon Sep 17 00:00:00 2001
From: jtpavlock
Date: Mon, 13 Jul 2020 15:27:05 -0700
Subject: [PATCH 006/680] Spelling fix in CONTRIBUTING.md (#1547)
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 525cb9c183d..0687aaeee52 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -45,7 +45,7 @@ $ black-primer [-k -w /tmp/black_test_repos]
## black-primer
`black-primer` is used by CI to pull down well-known _Black_ formatted projects and see
-if we get soure code changes. It will error on formatting changes or errors. Please run
+if we get source code changes. It will error on formatting changes or errors. Please run
before pushing your PR to see if you get the actions you would expect from _Black_ with
your PR. You may need to change
[primer.json](https://github.com/psf/black/blob/master/src/black_primer/primer.json)
From 98ac69f04ccaf3328b05e65d19a24f205825be4f Mon Sep 17 00:00:00 2001
From: dhaug-op <56020126+dhaug-op@users.noreply.github.com>
Date: Wed, 15 Jul 2020 17:06:30 +0200
Subject: [PATCH 007/680] Ensure path for finding root is absolute (#1550)
As Path.resolve() is buggy on windows (see https://bugs.python.org/issue38671)
an absolute path is ensured by prepending the Path.cwd()
---
src/black/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 3d0a2d69069..930f2cb0ae5 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -5834,7 +5834,7 @@ def find_project_root(srcs: Iterable[str]) -> Path:
if not srcs:
return Path("/").resolve()
- path_srcs = [Path(src).resolve() for src in srcs]
+ path_srcs = [Path(Path.cwd(), src).resolve() for src in srcs]
# A list of lists of parents for each 'src'. 'src' is included as a
# "parent" of itself if it is a directory
From 5010bb42798d6a5c7a5e35a54baad96df6f6814d Mon Sep 17 00:00:00 2001
From: Steven Maude
Date: Wed, 15 Jul 2020 19:36:14 +0100
Subject: [PATCH 008/680] Update curl command to use stable branch (#1543)
---
docs/editor_integration.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/editor_integration.md b/docs/editor_integration.md
index 00241f23335..eb83a1a4b43 100644
--- a/docs/editor_integration.md
+++ b/docs/editor_integration.md
@@ -146,7 +146,7 @@ or you can copy the plugin from
```
mkdir -p ~/.vim/pack/python/start/black/plugin
-curl https://raw.githubusercontent.com/psf/black/master/plugin/black.vim -o ~/.vim/pack/python/start/black/plugin/black.vim
+curl https://raw.githubusercontent.com/psf/black/stable/plugin/black.vim -o ~/.vim/pack/python/start/black/plugin/black.vim
```
Let me know if this requires any changes to work with Vim 8's builtin `packadd`, or
From 2955bdc6767d942cd489c73bd3bf600303d524c5 Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade
Date: Thu, 16 Jul 2020 00:53:48 +0300
Subject: [PATCH 009/680] pre-commit: show diff on failure on CI (#1552)
* pre-commit: --show-diff-on-failure
* pre-commit: --show-diff-on-failure
---
.github/workflows/lint.yml | 2 +-
.travis.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index cb8c534c9ac..fa7286eec1f 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -24,4 +24,4 @@ jobs:
python -m pip install -e '.[d]'
- name: Lint
- run: pre-commit run --all-files
+ run: pre-commit run --all-files --show-diff-on-failure
diff --git a/.travis.yml b/.travis.yml
index b2b127cfeb0..86cf24df51b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,7 +20,7 @@ matrix:
- name: "lint"
python: 3.7
env:
- - TEST_CMD="pre-commit run --all-files"
+ - TEST_CMD="pre-commit run --all-files --show-diff-on-failure"
- name: "3.6"
python: 3.6
- name: "3.7"
From 2c5041cfa0e5645275504c070ef7d0ca5609752f Mon Sep 17 00:00:00 2001
From: Vinicius Gubiani Ferreira
Date: Thu, 16 Jul 2020 01:51:39 -0300
Subject: [PATCH 010/680] docs: Improve pre-commit use (#1551)
Stable tag wasn't available and crashed when attempting to set initial
pre-commit. Also the python version needs to be installed so it would
be better to use the generic "python3" command.
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 33c170bd3c8..073150834d3 100644
--- a/README.md
+++ b/README.md
@@ -370,10 +370,10 @@ Use [pre-commit](https://pre-commit.com/). Once you
```yaml
repos:
- repo: https://github.com/psf/black
- rev: stable
+ rev: 19.10b0 # Replace by any tag/version: https://github.com/psf/black/tags
hooks:
- id: black
- language_version: python3.6
+ language_version: python3 # Should be a command that runs python3.6+
```
Then run `pre-commit install` and you're ready to go.
From 537ea8df35b1004bdb228b483907fb5dd92e5257 Mon Sep 17 00:00:00 2001
From: Maximilian Cosmo Sitter <48606431+mcsitter@users.noreply.github.com>
Date: Wed, 22 Jul 2020 02:29:38 +0200
Subject: [PATCH 011/680] Update to accomodate isort 5 release changes. (#1559)
Isort 5 introduced profiles and ensure_newline_before_comments options. Either needs to be added to work correctly with black.
Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com>
---
docs/compatible_configs.md | 15 ++++++++++++++-
docs/the_black_code_style.md | 13 +++++++------
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/docs/compatible_configs.md b/docs/compatible_configs.md
index aa7cd96f165..723fc889c00 100644
--- a/docs/compatible_configs.md
+++ b/docs/compatible_configs.md
@@ -23,6 +23,7 @@ multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
+ensure_newline_before_comments = True
line_length = 88
```
@@ -62,7 +63,15 @@ The option `force_grid_wrap = 0` is just to tell isort to only wrap imports that
the `line_length` limit.
Finally, isort should be told to wrap imports when they surpass _Black_'s default limit
-of 88 characters via `line_length = 88`.
+of 88 characters via `line_length = 88` as well as
+`ensure_newline_before_comments = True` to ensure spacing import sections with comments
+works the same as with _Black_.
+
+**Please note** `ensure_newline_before_comments = True` only works since isort >= 5 but
+does not break older versions so you can keep it if you are running previous versions.
+If only isort >= 5 is used you can add `profile = black` instead of all the options
+since [profiles](https://timothycrosley.github.io/isort/docs/configuration/profiles/)
+are available and do the configuring for you.
### Formats
@@ -75,6 +84,7 @@ multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
+ensure_newline_before_comments = True
line_length = 88
```
@@ -89,6 +99,7 @@ multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
+ensure_newline_before_comments = True
line_length = 88
```
@@ -103,6 +114,7 @@ multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
+ensure_newline_before_comments = true
line_length = 88
```
@@ -117,6 +129,7 @@ multi_line_output = 3
include_trailing_comma = True
force_grid_wrap = 0
use_parentheses = True
+ensure_newline_before_comments = True
line_length = 88
```
diff --git a/docs/the_black_code_style.md b/docs/the_black_code_style.md
index 21f217d388e..09d58307a05 100644
--- a/docs/the_black_code_style.md
+++ b/docs/the_black_code_style.md
@@ -153,13 +153,14 @@ the following configuration.
A compatible `.isort.cfg`
-```
+```cfg
[settings]
-multi_line_output=3
-include_trailing_comma=True
-force_grid_wrap=0
-use_parentheses=True
-line_length=88
+multi_line_output = 3
+include_trailing_comma = True
+force_grid_wrap = 0
+use_parentheses = True
+ensure_newline_before_comments = True
+line_length = 88
```
The equivalent command line is:
From b59a5246577346e6da2cc2802015f08524abf545 Mon Sep 17 00:00:00 2001
From: Kaligule
Date: Sat, 1 Aug 2020 23:21:55 +0200
Subject: [PATCH 012/680] fix spelling (#1567)
Co-authored-by: Hugo van Kemenade
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 073150834d3..d4b239b7358 100644
--- a/README.md
+++ b/README.md
@@ -354,8 +354,8 @@ rolling.
## black-primer
-`black-primer` is a tool built for CI (and huumans) to have _Black_ `--check` a number
-of (configured in `primer.json`) Git accessible projects in parallel.
+`black-primer` is a tool built for CI (and humans) to have _Black_ `--check` a number of
+(configured in `primer.json`) Git accessible projects in parallel.
[black_primer](https://github.com/psf/black/blob/master/docs/black_primer.md) has more
information regarding its usage and configuration.
From f825e7ef28c65689e06453d0c3a00310b90cdfdb Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra
Date: Wed, 12 Aug 2020 19:12:21 -0700
Subject: [PATCH 013/680] Remove slow assertion (#1592)
Partial fix for #1581
This assertion produces behavior quadratic in the number of leaves in a line, which is making Black extremely slow on files with very long expressions. On my benchmark file this change makes Black 10x faster.
---
src/black/__init__.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 930f2cb0ae5..7ce2ac896b3 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -4594,8 +4594,6 @@ def append_leaves(new_line: Line, old_line: Line, leaves: List[Leaf]) -> None:
set(@leaves) is a subset of set(@old_line.leaves).
"""
for old_leaf in leaves:
- assert old_leaf in old_line.leaves
-
new_leaf = Leaf(old_leaf.type, old_leaf.value)
replace_child(old_leaf, new_leaf)
new_line.append(new_leaf)
From 149b38d67430f0c580a05c821db767592e7d55e2 Mon Sep 17 00:00:00 2001
From: Chris Rose
Date: Wed, 12 Aug 2020 19:28:01 -0700
Subject: [PATCH 014/680] Add the direnv base directory to the default excludes
(#1564)
Co-authored-by: Chris Rose
---
README.md | 1 +
src/black/__init__.py | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index d4b239b7358..aead10cf585 100644
--- a/README.md
+++ b/README.md
@@ -548,6 +548,7 @@ Multiple contributions by:
- [Christian Clauss](mailto:cclauss@bluewin.ch)
- [Christian Heimes](mailto:christian@python.org)
- [Chuck Wooters](mailto:chuck.wooters@microsoft.com)
+- [Chris Rose](mailto:offline@offby1.net)
- Codey Oxley
- [Cong](mailto:congusbongus@gmail.com)
- [Cooper Ry Lees](mailto:me@cooperlees.com)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 7ce2ac896b3..df38fd01a9b 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -65,7 +65,7 @@
import colorama # noqa: F401
DEFAULT_LINE_LENGTH = 88
-DEFAULT_EXCLUDES = r"/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist)/" # noqa: B950
+DEFAULT_EXCLUDES = r"/(\.direnv|\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist)/" # noqa: B950
DEFAULT_INCLUDES = r"\.pyi?$"
CACHE_DIR = Path(user_cache_dir("black", version=__version__))
From 97c11f22aaf3eacad42d4f78309ffc1f6965e955 Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Wed, 12 Aug 2020 23:07:19 -0400
Subject: [PATCH 015/680] Make --exclude only apply to recursively found files
(#1591)
Ever since --force-exclude was added, --exclude started to touch files
that were given to Black through the CLI too. This is not documented
behaviour and neither expected as --exclude and --force-exclude now
behave the same!
Before this commit, get_sources() when encountering a file that was passed
explicitly through the CLI would pass a single Path object list to
gen_python_files(). This causes bad behaviour since that function
doesn't treat the exclude and force_exclude regexes differently. Which
is fine for recursively found files, but *not* for files given through
the CLI.
Now when get_sources() iterates through srcs and encounters
a file, it checks if the force_exclude regex matches, if not, then the
file will be added to the computed sources set.
A new function had to be created since before you can do regex matching,
the path must be normalized. The full process of normalizing the path is
somewhat long as there is special error handling. I didn't want to
duplicate this logic in get_sources() and gen_python_files() so that's
why there is a new helper function.
---
src/black/__init__.py | 99 ++++++++++++++++++++++++++++---------------
tests/test_black.py | 63 ++++++++++++++++++++-------
2 files changed, 112 insertions(+), 50 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index df38fd01a9b..803c7a1c633 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -583,9 +583,7 @@ def get_sources(
root = find_project_root(src)
sources: Set[Path] = set()
path_empty(src, "No Path provided. Nothing to do 😴", quiet, verbose, ctx)
- exclude_regexes = [exclude_regex]
- if force_exclude_regex is not None:
- exclude_regexes.append(force_exclude_regex)
+ gitignore = get_gitignore(root)
for s in src:
p = Path(s)
@@ -595,19 +593,30 @@ def get_sources(
p.iterdir(),
root,
include_regex,
- exclude_regexes,
+ exclude_regex,
+ force_exclude_regex,
report,
- get_gitignore(root),
+ gitignore,
)
)
elif s == "-":
sources.add(p)
elif p.is_file():
- sources.update(
- gen_python_files(
- [p], root, None, exclude_regexes, report, get_gitignore(root)
- )
- )
+ normalized_path = normalize_path_maybe_ignore(p, root, report)
+ if normalized_path is None:
+ continue
+
+ normalized_path = "/" + normalized_path
+ # Hard-exclude any files that matches the `--force-exclude` regex.
+ if force_exclude_regex:
+ force_exclude_match = force_exclude_regex.search(normalized_path)
+ else:
+ force_exclude_match = None
+ if force_exclude_match and force_exclude_match.group(0):
+ report.path_ignored(p, "matches the --force-exclude regular expression")
+ continue
+
+ sources.add(p)
else:
err(f"invalid path: {s}")
return sources
@@ -5757,16 +5766,40 @@ def get_gitignore(root: Path) -> PathSpec:
return PathSpec.from_lines("gitwildmatch", lines)
+def normalize_path_maybe_ignore(
+ path: Path, root: Path, report: "Report"
+) -> Optional[str]:
+ """Normalize `path`. May return `None` if `path` was ignored.
+
+ `report` is where "path ignored" output goes.
+ """
+ try:
+ normalized_path = path.resolve().relative_to(root).as_posix()
+ except OSError as e:
+ report.path_ignored(path, f"cannot be read because {e}")
+ return None
+
+ except ValueError:
+ if path.is_symlink():
+ report.path_ignored(path, f"is a symbolic link that points outside {root}")
+ return None
+
+ raise
+
+ return normalized_path
+
+
def gen_python_files(
paths: Iterable[Path],
root: Path,
include: Optional[Pattern[str]],
- exclude_regexes: Iterable[Pattern[str]],
+ exclude: Pattern[str],
+ force_exclude: Optional[Pattern[str]],
report: "Report",
gitignore: PathSpec,
) -> Iterator[Path]:
"""Generate all files under `path` whose paths are not excluded by the
- `exclude` regex, but are included by the `include` regex.
+ `exclude_regex` or `force_exclude` regexes, but are included by the `include` regex.
Symbolic links pointing outside of the `root` directory are ignored.
@@ -5774,43 +5807,41 @@ def gen_python_files(
"""
assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}"
for child in paths:
- # Then ignore with `exclude` option.
- try:
- normalized_path = child.resolve().relative_to(root).as_posix()
- except OSError as e:
- report.path_ignored(child, f"cannot be read because {e}")
+ normalized_path = normalize_path_maybe_ignore(child, root, report)
+ if normalized_path is None:
continue
- except ValueError:
- if child.is_symlink():
- report.path_ignored(
- child, f"is a symbolic link that points outside {root}"
- )
- continue
-
- raise
# First ignore files matching .gitignore
if gitignore.match_file(normalized_path):
report.path_ignored(child, "matches the .gitignore file content")
continue
+ # Then ignore with `--exclude` and `--force-exclude` options.
normalized_path = "/" + normalized_path
if child.is_dir():
normalized_path += "/"
- is_excluded = False
- for exclude in exclude_regexes:
- exclude_match = exclude.search(normalized_path) if exclude else None
- if exclude_match and exclude_match.group(0):
- report.path_ignored(child, "matches the --exclude regular expression")
- is_excluded = True
- break
- if is_excluded:
+ exclude_match = exclude.search(normalized_path) if exclude else None
+ if exclude_match and exclude_match.group(0):
+ report.path_ignored(child, "matches the --exclude regular expression")
+ continue
+
+ force_exclude_match = (
+ force_exclude.search(normalized_path) if force_exclude else None
+ )
+ if force_exclude_match and force_exclude_match.group(0):
+ report.path_ignored(child, "matches the --force-exclude regular expression")
continue
if child.is_dir():
yield from gen_python_files(
- child.iterdir(), root, include, exclude_regexes, report, gitignore
+ child.iterdir(),
+ root,
+ include,
+ exclude,
+ force_exclude,
+ report,
+ gitignore,
)
elif child.is_file():
diff --git a/tests/test_black.py b/tests/test_black.py
index 3ed5daa4b49..f4055581f86 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -110,6 +110,20 @@ def skip_if_exception(e: str) -> Iterator[None]:
raise
+class FakeContext(click.Context):
+ """A fake click Context for when calling functions that need it."""
+
+ def __init__(self) -> None:
+ self.default_map: Dict[str, Any] = {}
+
+
+class FakeParameter(click.Parameter):
+ """A fake click Parameter for when calling functions that need it."""
+
+ def __init__(self) -> None:
+ pass
+
+
class BlackRunner(CliRunner):
"""Modify CliRunner so that stderr is not merged with stdout.
@@ -1551,7 +1565,32 @@ def test_include_exclude(self) -> None:
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files(
- path.iterdir(), this_abs, include, [exclude], report, gitignore
+ path.iterdir(), this_abs, include, exclude, None, report, gitignore
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_exclude_for_issue_1572(self) -> None:
+ # Exclude shouldn't touch files that were explicitly given to Black through the
+ # CLI. Exclude is supposed to only apply to the recursive discovery of files.
+ # https://github.com/psf/black/issues/1572
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = str(path / "b/exclude/a.py")
+ report = black.Report()
+ expected = [Path(path / "b/exclude/a.py")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=include,
+ exclude=exclude,
+ force_exclude=None,
+ report=report,
)
)
self.assertEqual(sorted(expected), sorted(sources))
@@ -1572,7 +1611,7 @@ def test_gitignore_exclude(self) -> None:
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files(
- path.iterdir(), this_abs, include, [exclude], report, gitignore
+ path.iterdir(), this_abs, include, exclude, None, report, gitignore
)
)
self.assertEqual(sorted(expected), sorted(sources))
@@ -1600,7 +1639,8 @@ def test_empty_include(self) -> None:
path.iterdir(),
this_abs,
empty,
- [re.compile(black.DEFAULT_EXCLUDES)],
+ re.compile(black.DEFAULT_EXCLUDES),
+ None,
report,
gitignore,
)
@@ -1627,7 +1667,8 @@ def test_empty_exclude(self) -> None:
path.iterdir(),
this_abs,
re.compile(black.DEFAULT_INCLUDES),
- [empty],
+ empty,
+ None,
report,
gitignore,
)
@@ -1684,7 +1725,7 @@ def test_symlink_out_of_root_directory(self) -> None:
try:
list(
black.gen_python_files(
- path.iterdir(), root, include, exclude, report, gitignore
+ path.iterdir(), root, include, exclude, None, report, gitignore
)
)
except ValueError as ve:
@@ -1698,7 +1739,7 @@ def test_symlink_out_of_root_directory(self) -> None:
with self.assertRaises(ValueError):
list(
black.gen_python_files(
- path.iterdir(), root, include, exclude, report, gitignore
+ path.iterdir(), root, include, exclude, None, report, gitignore
)
)
path.iterdir.assert_called()
@@ -1777,16 +1818,6 @@ def test_parse_pyproject_toml(self) -> None:
def test_read_pyproject_toml(self) -> None:
test_toml_file = THIS_DIR / "test.toml"
-
- # Fake a click context and parameter so mypy stays happy
- class FakeContext(click.Context):
- def __init__(self) -> None:
- self.default_map: Dict[str, Any] = {}
-
- class FakeParameter(click.Parameter):
- def __init__(self) -> None:
- pass
-
fake_ctx = FakeContext()
black.read_pyproject_toml(
fake_ctx, FakeParameter(), str(test_toml_file),
From 8842c5ffa888aab2e4961fc54db45e7f3ca32ad9 Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra
Date: Thu, 13 Aug 2020 11:14:34 -0700
Subject: [PATCH 016/680] in verbose mode, print stack trace (#1594)
Make Black failures easier to debug
---
src/black/__init__.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 803c7a1c633..349da1e71e2 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -664,6 +664,8 @@ def reformat_one(
write_cache(cache, [src], mode)
report.done(src, changed)
except Exception as exc:
+ if report.verbose:
+ traceback.print_exc()
report.failed(src, str(exc))
@@ -3327,7 +3329,7 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]:
new_line.append(string_leaf)
append_leaves(
- new_line, line, LL[string_idx + 1 : rpar_idx] + LL[rpar_idx + 1 :],
+ new_line, line, LL[string_idx + 1 : rpar_idx] + LL[rpar_idx + 1 :]
)
LL[rpar_idx].remove()
From 5e1f620af7ff5d2bb4205e398849e5395999a0cb Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra
Date: Thu, 13 Aug 2020 16:40:45 -0700
Subject: [PATCH 017/680] fix some docstring crashes (#1593)
Allow removing some trailing whitespace
---
src/black/__init__.py | 2 +-
tests/data/docstring.py | 18 +++++++++++++++++-
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 349da1e71e2..36c03946b55 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -6063,7 +6063,7 @@ def _stringify_ast(
and field == "value"
and isinstance(value, str)
):
- normalized = re.sub(r" *\n[ \t]+", "\n ", value).strip()
+ normalized = re.sub(r" *\n[ \t]*", "\n", value).strip()
else:
normalized = value
yield f"{' ' * (depth+2)}{normalized!r}, # {value.__class__.__name__}"
diff --git a/tests/data/docstring.py b/tests/data/docstring.py
index f5adeb7bb7b..fcb8eb12a78 100644
--- a/tests/data/docstring.py
+++ b/tests/data/docstring.py
@@ -46,7 +46,7 @@ def zort():
def poit():
"""
- Lorem ipsum dolor sit amet.
+ Lorem ipsum dolor sit amet.
Consectetur adipiscing elit:
- sed do eiusmod tempor incididunt ut labore
@@ -58,6 +58,14 @@ def poit():
pass
+def under_indent():
+ """
+ These lines are indented in a way that does not
+make sense.
+ """
+ pass
+
+
def over_indent():
"""
This has a shallow indent
@@ -136,6 +144,14 @@ def poit():
pass
+def under_indent():
+ """
+ These lines are indented in a way that does not
+ make sense.
+ """
+ pass
+
+
def over_indent():
"""
This has a shallow indent
From d1ad8730e36819f787c720d1917de22b59806a75 Mon Sep 17 00:00:00 2001
From: David Szotten
Date: Fri, 14 Aug 2020 03:20:46 +0100
Subject: [PATCH 018/680] don't strip brackets before lsqb (#1575) (#1590)
if the string contains a PERCENT, it's not safe to remove brackets that
follow and operator with the same or higher precedence than PERCENT
---
src/black/__init__.py | 45 +++++++++++++++++++++++++++++---
tests/data/percent_precedence.py | 39 +++++++++++++++++++++++++++
tests/test_black.py | 8 ++++++
3 files changed, 89 insertions(+), 3 deletions(-)
create mode 100644 tests/data/percent_precedence.py
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 36c03946b55..b660e4788b4 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -3249,7 +3249,9 @@ class StringParenStripper(StringTransformer):
Requirements:
The line contains a string which is surrounded by parentheses and:
- The target string is NOT the only argument to a function call).
- - The RPAR is NOT followed by an attribute access (i.e. a dot).
+ - If the target string contains a PERCENT, the brackets are not
+ preceeded or followed by an operator with higher precedence than
+ PERCENT.
Transformations:
The parentheses mentioned in the 'Requirements' section are stripped.
@@ -3292,14 +3294,51 @@ def do_match(self, line: Line) -> TMatchResult:
string_parser = StringParser()
next_idx = string_parser.parse(LL, string_idx)
+ # if the leaves in the parsed string include a PERCENT, we need to
+ # make sure the initial LPAR is NOT preceded by an operator with
+ # higher or equal precedence to PERCENT
+ if (
+ is_valid_index(idx - 2)
+ and token.PERCENT in {leaf.type for leaf in LL[idx - 1 : next_idx]}
+ and (
+ (
+ LL[idx - 2].type
+ in {
+ token.STAR,
+ token.AT,
+ token.SLASH,
+ token.DOUBLESLASH,
+ token.PERCENT,
+ token.TILDE,
+ token.DOUBLESTAR,
+ token.AWAIT,
+ token.LSQB,
+ token.LPAR,
+ }
+ )
+ or (
+ # only unary PLUS/MINUS
+ not is_valid_index(idx - 3)
+ and (LL[idx - 2].type in {token.PLUS, token.MINUS})
+ )
+ )
+ ):
+ continue
+
# Should be followed by a non-empty RPAR...
if (
is_valid_index(next_idx)
and LL[next_idx].type == token.RPAR
and not is_empty_rpar(LL[next_idx])
):
- # That RPAR should NOT be followed by a '.' symbol.
- if is_valid_index(next_idx + 1) and LL[next_idx + 1].type == token.DOT:
+ # That RPAR should NOT be followed by anything with higher
+ # precedence than PERCENT
+ if is_valid_index(next_idx + 1) and LL[next_idx + 1].type in {
+ token.DOUBLESTAR,
+ token.LSQB,
+ token.LPAR,
+ token.DOT,
+ }:
continue
return Ok(string_idx)
diff --git a/tests/data/percent_precedence.py b/tests/data/percent_precedence.py
new file mode 100644
index 00000000000..44d30f16e03
--- /dev/null
+++ b/tests/data/percent_precedence.py
@@ -0,0 +1,39 @@
+("" % a) ** 2
+("" % a)[0]
+("" % a)()
+("" % a).b
+
+2 * ("" % a)
+2 @ ("" % a)
+2 / ("" % a)
+2 // ("" % a)
+2 % ("" % a)
++("" % a)
+b + ("" % a)
+-("" % a)
+b - ("" % a)
+~("" % a)
+2 ** ("" % a)
+await ("" % a)
+b[("" % a)]
+b(("" % a))
+# output
+("" % a) ** 2
+("" % a)[0]
+("" % a)()
+("" % a).b
+
+2 * ("" % a)
+2 @ ("" % a)
+2 / ("" % a)
+2 // ("" % a)
+2 % ("" % a)
++("" % a)
+b + "" % a
+-("" % a)
+b - "" % a
+~("" % a)
+2 ** ("" % a)
+await ("" % a)
+b[("" % a)]
+b(("" % a))
diff --git a/tests/test_black.py b/tests/test_black.py
index f4055581f86..3c766330a08 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -501,6 +501,14 @@ def test_slices(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, black.FileMode())
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_percent_precedence(self) -> None:
+ source, expected = read_data("percent_precedence")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
@patch("black.dump_to_file", dump_to_stderr)
def test_comments(self) -> None:
source, expected = read_data("comments")
From 820f38708fd41a1b992716b1f65c9b0656f589d0 Mon Sep 17 00:00:00 2001
From: David Szotten
Date: Fri, 14 Aug 2020 17:17:56 +0100
Subject: [PATCH 019/680] fix unary op detection (#1600)
---
src/black/__init__.py | 20 ++++++++++----------
tests/data/percent_precedence.py | 2 ++
2 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index b660e4788b4..391233ed448 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -3297,12 +3297,12 @@ def do_match(self, line: Line) -> TMatchResult:
# if the leaves in the parsed string include a PERCENT, we need to
# make sure the initial LPAR is NOT preceded by an operator with
# higher or equal precedence to PERCENT
- if (
- is_valid_index(idx - 2)
- and token.PERCENT in {leaf.type for leaf in LL[idx - 1 : next_idx]}
- and (
+ if is_valid_index(idx - 2):
+ # mypy can't quite follow unless we name this
+ before_lpar = LL[idx - 2]
+ if token.PERCENT in {leaf.type for leaf in LL[idx - 1 : next_idx]} and (
(
- LL[idx - 2].type
+ before_lpar.type
in {
token.STAR,
token.AT,
@@ -3318,12 +3318,12 @@ def do_match(self, line: Line) -> TMatchResult:
)
or (
# only unary PLUS/MINUS
- not is_valid_index(idx - 3)
- and (LL[idx - 2].type in {token.PLUS, token.MINUS})
+ before_lpar.parent
+ and before_lpar.parent.type == syms.factor
+ and (before_lpar.type in {token.PLUS, token.MINUS})
)
- )
- ):
- continue
+ ):
+ continue
# Should be followed by a non-empty RPAR...
if (
diff --git a/tests/data/percent_precedence.py b/tests/data/percent_precedence.py
index 44d30f16e03..b895443fb46 100644
--- a/tests/data/percent_precedence.py
+++ b/tests/data/percent_precedence.py
@@ -12,6 +12,7 @@
b + ("" % a)
-("" % a)
b - ("" % a)
+b + -("" % a)
~("" % a)
2 ** ("" % a)
await ("" % a)
@@ -32,6 +33,7 @@
b + "" % a
-("" % a)
b - "" % a
+b + -("" % a)
~("" % a)
2 ** ("" % a)
await ("" % a)
From 80425ca440136002a64e24d6be7c3ba59cc20f9c Mon Sep 17 00:00:00 2001
From: Daanyaal Syed
Date: Mon, 17 Aug 2020 14:55:38 -0400
Subject: [PATCH 020/680] Fix inline code style in README (#1608)
Refer to `pyproject.toml` in HTML section of README with HTML code tags
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index aead10cf585..37b93c143a2 100644
--- a/README.md
+++ b/README.md
@@ -293,7 +293,7 @@ the equivalent of r-strings in Python. Multiline strings are treated as verbose
expressions by Black. Use `[ ]` to denote a significant space character.
-Example `pyproject.toml`
+Example pyproject.toml
```toml
[tool.black]
From e1027e2bee45ce4830febf9e5e9b4d3f98ec5bb7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Tue, 18 Aug 2020 12:31:15 +0200
Subject: [PATCH 021/680] Update all dependencies to latest versions
---
Pipfile | 2 +-
Pipfile.lock | 624 +++++++++++++++++++++++----------------------------
2 files changed, 284 insertions(+), 342 deletions(-)
diff --git a/Pipfile b/Pipfile
index 3afe3bb9efd..feefb857132 100644
--- a/Pipfile
+++ b/Pipfile
@@ -18,7 +18,7 @@ setuptools = ">=39.2.0"
setuptools-scm = "*"
twine = ">=1.11.0"
wheel = ">=0.31.1"
-black = {editable = true,extras = ["d"],path = "."}
+black = {editable = true, extras = ["d"], path = "."}
[packages]
aiohttp = ">=3.3.2"
diff --git a/Pipfile.lock b/Pipfile.lock
index b0da4743793..b8dfd6b5422 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -42,17 +42,18 @@
},
"appdirs": {
"hashes": [
- "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
- "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
+ "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
+ "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
],
"index": "pypi",
- "version": "==1.4.3"
+ "version": "==1.4.4"
},
"async-timeout": {
"hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
+ "markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
@@ -60,6 +61,7 @@
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==19.3.0"
},
"black": {
@@ -78,11 +80,11 @@
},
"click": {
"hashes": [
- "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc",
- "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"
+ "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
+ "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
"index": "pypi",
- "version": "==7.1.1"
+ "version": "==7.1.2"
},
"dataclasses": {
"hashes": [
@@ -90,36 +92,40 @@
"sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"
],
"index": "pypi",
- "version": "==0.6"
+ "python_version <": "3.7",
+ "version": "==0.6",
+ "version >": "0.6"
},
"idna": {
"hashes": [
- "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
- "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
+ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+ "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
- "version": "==2.9"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.10"
},
"multidict": {
"hashes": [
- "sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1",
- "sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35",
- "sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928",
- "sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969",
- "sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e",
- "sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78",
- "sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1",
- "sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136",
- "sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8",
- "sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2",
- "sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e",
- "sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4",
- "sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5",
- "sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd",
- "sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab",
- "sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20",
- "sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3"
- ],
- "version": "==4.7.5"
+ "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a",
+ "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000",
+ "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2",
+ "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507",
+ "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5",
+ "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7",
+ "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d",
+ "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463",
+ "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19",
+ "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3",
+ "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b",
+ "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c",
+ "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87",
+ "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7",
+ "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430",
+ "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255",
+ "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==4.7.6"
},
"mypy-extensions": {
"hashes": [
@@ -131,38 +137,38 @@
},
"pathspec": {
"hashes": [
- "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424",
- "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"
+ "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
+ "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
],
"index": "pypi",
- "version": "==0.7.0"
+ "version": "==0.8.0"
},
"regex": {
"hashes": [
- "sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431",
- "sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242",
- "sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1",
- "sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d",
- "sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045",
- "sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b",
- "sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400",
- "sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa",
- "sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0",
- "sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69",
- "sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74",
- "sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb",
- "sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26",
- "sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5",
- "sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2",
- "sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce",
- "sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab",
- "sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e",
- "sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70",
- "sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc",
- "sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"
- ],
- "index": "pypi",
- "version": "==2020.2.20"
+ "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204",
+ "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162",
+ "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f",
+ "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb",
+ "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6",
+ "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7",
+ "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88",
+ "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99",
+ "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644",
+ "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a",
+ "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840",
+ "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067",
+ "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd",
+ "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4",
+ "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e",
+ "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89",
+ "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e",
+ "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc",
+ "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf",
+ "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341",
+ "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"
+ ],
+ "index": "pypi",
+ "version": "==2020.7.14"
},
"setuptools-scm": {
"hashes": [
@@ -180,10 +186,10 @@
},
"toml": {
"hashes": [
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+ "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"index": "pypi",
"version": "==0.10.1"
@@ -216,34 +222,35 @@
},
"typing-extensions": {
"hashes": [
- "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2",
- "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d",
- "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"
+ "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5",
+ "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae",
+ "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"
],
"index": "pypi",
- "version": "==3.7.4.1"
+ "version": "==3.7.4.2"
},
"yarl": {
"hashes": [
- "sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce",
- "sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6",
- "sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce",
- "sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae",
- "sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d",
- "sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f",
- "sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b",
- "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b",
- "sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb",
- "sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462",
- "sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea",
- "sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70",
- "sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1",
- "sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a",
- "sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b",
- "sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080",
- "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2"
- ],
- "version": "==1.4.2"
+ "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409",
+ "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593",
+ "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2",
+ "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8",
+ "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d",
+ "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692",
+ "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02",
+ "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a",
+ "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8",
+ "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6",
+ "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511",
+ "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e",
+ "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a",
+ "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb",
+ "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f",
+ "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317",
+ "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.5.1"
}
},
"develop": {
@@ -282,17 +289,18 @@
},
"appdirs": {
"hashes": [
- "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
- "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
+ "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
+ "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
],
"index": "pypi",
- "version": "==1.4.3"
+ "version": "==1.4.4"
},
"async-timeout": {
"hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
+ "markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
@@ -300,6 +308,7 @@
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==19.3.0"
},
"babel": {
@@ -327,51 +336,18 @@
},
"certifi": {
"hashes": [
- "sha256:5ad7e9a056d25ffa5082862e36f119f7f7cec6457fa07ee2f8c339814b80c9b1",
- "sha256:9cd41137dc19af6a5e03b630eefe7d1f458d964d406342dd3edf625839b944cc"
- ],
- "version": "==2020.4.5.2"
- },
- "cffi": {
- "hashes": [
- "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff",
- "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b",
- "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac",
- "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0",
- "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384",
- "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26",
- "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6",
- "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b",
- "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e",
- "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd",
- "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2",
- "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66",
- "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc",
- "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8",
- "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55",
- "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4",
- "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5",
- "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d",
- "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78",
- "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa",
- "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793",
- "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f",
- "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a",
- "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f",
- "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30",
- "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f",
- "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3",
- "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"
- ],
- "version": "==1.14.0"
+ "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
+ "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
+ ],
+ "version": "==2020.6.20"
},
"cfgv": {
"hashes": [
- "sha256:1ccf53320421aeeb915275a196e23b3b8ae87dea8ac6698b1638001d4a486d53",
- "sha256:c8e8f552ffcc6194f4e18dd4f68d9aef0c0d58ae7e7be8c82bee3c5e9edfa513"
+ "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d",
+ "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"
],
"markers": "python_full_version >= '3.6.1'",
- "version": "==3.1.0"
+ "version": "==3.2.0"
},
"chardet": {
"hashes": [
@@ -382,11 +358,19 @@
},
"click": {
"hashes": [
- "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc",
- "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"
+ "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
+ "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
"index": "pypi",
- "version": "==7.1.1"
+ "version": "==7.1.2"
+ },
+ "colorama": {
+ "hashes": [
+ "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff",
+ "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==0.4.3"
},
"commonmark": {
"hashes": [
@@ -397,71 +381,50 @@
},
"coverage": {
"hashes": [
- "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0",
- "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30",
- "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b",
- "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0",
- "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823",
- "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe",
- "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037",
- "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6",
- "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31",
- "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd",
- "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892",
- "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1",
- "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78",
- "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac",
- "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006",
- "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014",
- "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2",
- "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7",
- "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8",
- "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7",
- "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9",
- "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1",
- "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307",
- "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a",
- "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435",
- "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0",
- "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5",
- "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441",
- "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732",
- "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de",
- "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1"
- ],
- "index": "pypi",
- "version": "==5.0.4"
- },
- "cryptography": {
- "hashes": [
- "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6",
- "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b",
- "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5",
- "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf",
- "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e",
- "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b",
- "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae",
- "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b",
- "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0",
- "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b",
- "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d",
- "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229",
- "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3",
- "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365",
- "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55",
- "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270",
- "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e",
- "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785",
- "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==2.9.2"
+ "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb",
+ "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3",
+ "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716",
+ "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034",
+ "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3",
+ "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8",
+ "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0",
+ "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f",
+ "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4",
+ "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962",
+ "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d",
+ "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b",
+ "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4",
+ "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3",
+ "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258",
+ "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59",
+ "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01",
+ "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd",
+ "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b",
+ "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d",
+ "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89",
+ "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd",
+ "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b",
+ "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d",
+ "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46",
+ "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546",
+ "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082",
+ "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b",
+ "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4",
+ "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8",
+ "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811",
+ "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd",
+ "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651",
+ "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"
+ ],
+ "index": "pypi",
+ "version": "==5.2.1"
},
"distlib": {
"hashes": [
- "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"
+ "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb",
+ "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"
],
- "version": "==0.3.0"
+ "version": "==0.3.1"
},
"docutils": {
"hashes": [
@@ -472,13 +435,6 @@
"index": "pypi",
"version": "==0.15"
},
- "entrypoints": {
- "hashes": [
- "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
- "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
- ],
- "version": "==0.3"
- },
"filelock": {
"hashes": [
"sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59",
@@ -488,11 +444,11 @@
},
"flake8": {
"hashes": [
- "sha256:45681a117ecc81e870cbf1262835ae4af5e7a8b08e40b944a8a6e6b895914cfb",
- "sha256:49356e766643ad15072a789a20915d3c91dc89fd313ccd71802303fd67e4deca"
+ "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c",
+ "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"
],
"index": "pypi",
- "version": "==3.7.9"
+ "version": "==3.8.3"
},
"flake8-bugbear": {
"hashes": [
@@ -512,18 +468,19 @@
},
"identify": {
"hashes": [
- "sha256:249ebc7e2066d6393d27c1b1be3b70433f824a120b1d8274d362f1eb419e3b52",
- "sha256:781fd3401f5d2b17b22a8b18b493a48d5d948e3330634e82742e23f9c20234ef"
+ "sha256:69c4769f085badafd0e04b1763e847258cbbf6d898e8678ebffc91abdb86f6c6",
+ "sha256:d6ae6daee50ba1b493e9ca4d36a5edd55905d2cf43548fdc20b2a14edef102e7"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==1.4.19"
+ "version": "==1.4.28"
},
"idna": {
"hashes": [
- "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
- "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
+ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+ "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
- "version": "==2.9"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.10"
},
"imagesize": {
"hashes": [
@@ -533,14 +490,6 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.0"
},
- "jeepney": {
- "hashes": [
- "sha256:3479b861cc2b6407de5188695fa1a8d57e5072d7059322469b62628869b8e36e",
- "sha256:d6c6b49683446d2407d2fe3acb7a368a77ff063f9182fe427da15d622adc24cf"
- ],
- "markers": "sys_platform == 'linux'",
- "version": "==0.4.3"
- },
"jinja2": {
"hashes": [
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
@@ -551,11 +500,11 @@
},
"keyring": {
"hashes": [
- "sha256:3401234209015144a5d75701e71cb47239e552b0882313e9f51e8976f9e27843",
- "sha256:c53e0e5ccde3ad34284a40ce7976b5b3a3d6de70344c3f8ee44364cc340976ec"
+ "sha256:22df6abfed49912fc560806030051067fba9f0069cffa79da72899aeea4ccbd5",
+ "sha256:e7a17caf40c40b6bb8c4772224a487e4a63013560ed0c521065aeba7ecd42182"
],
"markers": "python_version >= '3.6'",
- "version": "==21.2.1"
+ "version": "==21.3.0"
},
"markupsafe": {
"hashes": [
@@ -605,45 +554,46 @@
},
"multidict": {
"hashes": [
- "sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1",
- "sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35",
- "sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928",
- "sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969",
- "sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e",
- "sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78",
- "sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1",
- "sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136",
- "sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8",
- "sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2",
- "sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e",
- "sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4",
- "sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5",
- "sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd",
- "sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab",
- "sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20",
- "sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3"
- ],
- "version": "==4.7.5"
+ "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a",
+ "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000",
+ "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2",
+ "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507",
+ "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5",
+ "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7",
+ "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d",
+ "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463",
+ "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19",
+ "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3",
+ "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b",
+ "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c",
+ "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87",
+ "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7",
+ "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430",
+ "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255",
+ "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==4.7.6"
},
"mypy": {
"hashes": [
- "sha256:15b948e1302682e3682f11f50208b726a246ab4e6c1b39f9264a8796bb416aa2",
- "sha256:219a3116ecd015f8dca7b5d2c366c973509dfb9a8fc97ef044a36e3da66144a1",
- "sha256:3b1fc683fb204c6b4403a1ef23f0b1fac8e4477091585e0c8c54cbdf7d7bb164",
- "sha256:3beff56b453b6ef94ecb2996bea101a08f1f8a9771d3cbf4988a61e4d9973761",
- "sha256:7687f6455ec3ed7649d1ae574136835a4272b65b3ddcf01ab8704ac65616c5ce",
- "sha256:7ec45a70d40ede1ec7ad7f95b3c94c9cf4c186a32f6bacb1795b60abd2f9ef27",
- "sha256:86c857510a9b7c3104cf4cde1568f4921762c8f9842e987bc03ed4f160925754",
- "sha256:8a627507ef9b307b46a1fea9513d5c98680ba09591253082b4c48697ba05a4ae",
- "sha256:8dfb69fbf9f3aeed18afffb15e319ca7f8da9642336348ddd6cab2713ddcf8f9",
- "sha256:a34b577cdf6313bf24755f7a0e3f3c326d5c1f4fe7422d1d06498eb25ad0c600",
- "sha256:a8ffcd53cb5dfc131850851cc09f1c44689c2812d0beb954d8138d4f5fc17f65",
- "sha256:b90928f2d9eb2f33162405f32dde9f6dcead63a0971ca8a1b50eb4ca3e35ceb8",
- "sha256:c56ffe22faa2e51054c5f7a3bc70a370939c2ed4de308c690e7949230c995913",
- "sha256:f91c7ae919bbc3f96cd5e5b2e786b2b108343d1d7972ea130f7de27fdd547cf3"
+ "sha256:2c6cde8aa3426c1682d35190b59b71f661237d74b053822ea3d748e2c9578a7c",
+ "sha256:3fdda71c067d3ddfb21da4b80e2686b71e9e5c72cca65fa216d207a358827f86",
+ "sha256:5dd13ff1f2a97f94540fd37a49e5d255950ebcdf446fb597463a40d0df3fac8b",
+ "sha256:6731603dfe0ce4352c555c6284c6db0dc935b685e9ce2e4cf220abe1e14386fd",
+ "sha256:6bb93479caa6619d21d6e7160c552c1193f6952f0668cdda2f851156e85186fc",
+ "sha256:81c7908b94239c4010e16642c9102bfc958ab14e36048fa77d0be3289dda76ea",
+ "sha256:9c7a9a7ceb2871ba4bac1cf7217a7dd9ccd44c27c2950edbc6dc08530f32ad4e",
+ "sha256:a4a2cbcfc4cbf45cd126f531dedda8485671545b43107ded25ce952aac6fb308",
+ "sha256:b7fbfabdbcc78c4f6fc4712544b9b0d6bf171069c6e0e3cb82440dd10ced3406",
+ "sha256:c05b9e4fb1d8a41d41dec8786c94f3b95d3c5f528298d769eb8e73d293abc48d",
+ "sha256:d7df6eddb6054d21ca4d3c6249cae5578cb4602951fd2b6ee2f5510ffb098707",
+ "sha256:e0b61738ab504e656d1fe4ff0c0601387a5489ca122d55390ade31f9ca0e252d",
+ "sha256:eff7d4a85e9eea55afa34888dfeaccde99e7520b51f867ac28a48492c0b1130c",
+ "sha256:f05644db6779387ccdb468cc47a44b4356fc2ffa9287135d05b70a98dc83b89a"
],
"index": "pypi",
- "version": "==0.770"
+ "version": "==0.782"
},
"mypy-extensions": {
"hashes": [
@@ -669,11 +619,11 @@
},
"pathspec": {
"hashes": [
- "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424",
- "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"
+ "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
+ "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
],
"index": "pypi",
- "version": "==0.7.0"
+ "version": "==0.8.0"
},
"pkginfo": {
"hashes": [
@@ -684,11 +634,11 @@
},
"pre-commit": {
"hashes": [
- "sha256:487c675916e6f99d355ec5595ad77b325689d423ef4839db1ed2f02f639c9522",
- "sha256:c0aa11bce04a7b46c5544723aedf4e81a4d5f64ad1205a30a9ea12d5e81969e1"
+ "sha256:1657663fdd63a321a4a739915d7d03baedd555b25054449090f97bb0cb30a915",
+ "sha256:e8b1315c585052e729ab7e99dcca5698266bedce9067d21dc909c23e3ceed626"
],
"index": "pypi",
- "version": "==2.2.0"
+ "version": "==2.6.0"
},
"pycodestyle": {
"hashes": [
@@ -698,14 +648,6 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.6.0"
},
- "pycparser": {
- "hashes": [
- "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
- "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==2.20"
- },
"pyflakes": {
"hashes": [
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
@@ -755,11 +697,11 @@
},
"readme-renderer": {
"hashes": [
- "sha256:1b6d8dd1673a0b293766b4106af766b6eff3654605f9c4f239e65de6076bc222",
- "sha256:e67d64242f0174a63c3b727801a2fff4c1f38ebe5d71d95ff7ece081945a6cd4"
+ "sha256:cbe9db71defedd2428a1589cdc545f9bd98e59297449f69d721ef8f1cfced68d",
+ "sha256:cc4957a803106e820d05d14f71033092537a22daa4f406dfbdd61177e0936376"
],
"index": "pypi",
- "version": "==25.0"
+ "version": "==26.0"
},
"recommonmark": {
"hashes": [
@@ -771,38 +713,38 @@
},
"regex": {
"hashes": [
- "sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431",
- "sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242",
- "sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1",
- "sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d",
- "sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045",
- "sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b",
- "sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400",
- "sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa",
- "sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0",
- "sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69",
- "sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74",
- "sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb",
- "sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26",
- "sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5",
- "sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2",
- "sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce",
- "sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab",
- "sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e",
- "sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70",
- "sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc",
- "sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"
- ],
- "index": "pypi",
- "version": "==2020.2.20"
+ "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204",
+ "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162",
+ "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f",
+ "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb",
+ "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6",
+ "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7",
+ "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88",
+ "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99",
+ "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644",
+ "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a",
+ "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840",
+ "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067",
+ "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd",
+ "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4",
+ "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e",
+ "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89",
+ "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e",
+ "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc",
+ "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf",
+ "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341",
+ "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"
+ ],
+ "index": "pypi",
+ "version": "==2020.7.14"
},
"requests": {
"hashes": [
- "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
- "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
+ "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
+ "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==2.23.0"
+ "version": "==2.24.0"
},
"requests-toolbelt": {
"hashes": [
@@ -811,13 +753,12 @@
],
"version": "==0.9.1"
},
- "secretstorage": {
+ "rfc3986": {
"hashes": [
- "sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6",
- "sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b"
+ "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d",
+ "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"
],
- "markers": "sys_platform == 'linux'",
- "version": "==3.1.2"
+ "version": "==1.4.0"
},
"setuptools-scm": {
"hashes": [
@@ -850,11 +791,11 @@
},
"sphinx": {
"hashes": [
- "sha256:b4c750d546ab6d7e05bdff6ac24db8ae3e8b8253a3569b754e445110a0a12b66",
- "sha256:fc312670b56cb54920d6cc2ced455a22a547910de10b3142276495ced49231cb"
+ "sha256:321d6d9b16fa381a5306e5a0b76cd48ffbc588e6340059a729c6fdd66087e0e8",
+ "sha256:ce6fd7ff5b215af39e2fcd44d4a321f6694b4530b6f2b2109b64d120773faea0"
],
"index": "pypi",
- "version": "==2.4.4"
+ "version": "==3.2.1"
},
"sphinxcontrib-applehelp": {
"hashes": [
@@ -906,29 +847,29 @@
},
"toml": {
"hashes": [
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+ "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"index": "pypi",
"version": "==0.10.1"
},
"tqdm": {
"hashes": [
- "sha256:07c06493f1403c1380b630ae3dcbe5ae62abcf369a93bbc052502279f189ab8c",
- "sha256:cd140979c2bebd2311dfb14781d8f19bd5a9debb92dcab9f6ef899c987fcf71f"
+ "sha256:1a336d2b829be50e46b84668691e0a2719f26c97c62846298dd5ae2937e4d5cf",
+ "sha256:564d632ea2b9cb52979f7956e093e831c28d441c11751682f84c86fc46e4fd21"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==4.46.1"
+ "version": "==4.48.2"
},
"twine": {
"hashes": [
- "sha256:c1af8ca391e43b0a06bbc155f7f67db0bf0d19d284bfc88d1675da497a946124",
- "sha256:d561a5e511f70275e5a485a6275ff61851c16ffcb3a95a602189161112d9f160"
+ "sha256:34352fd52ec3b9d29837e6072d5a2a7c6fe4290e97bba46bb8d478b5c598f7ab",
+ "sha256:ba9ff477b8d6de0c89dd450e70b2185da190514e91c42cc62f96850025c10472"
],
"index": "pypi",
- "version": "==3.1.1"
+ "version": "==3.2.0"
},
"typed-ast": {
"hashes": [
@@ -958,28 +899,28 @@
},
"typing-extensions": {
"hashes": [
- "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2",
- "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d",
- "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"
+ "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5",
+ "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae",
+ "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"
],
"index": "pypi",
- "version": "==3.7.4.1"
+ "version": "==3.7.4.2"
},
"urllib3": {
"hashes": [
- "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
- "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
+ "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a",
+ "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
- "version": "==1.25.9"
+ "version": "==1.25.10"
},
"virtualenv": {
"hashes": [
- "sha256:5102fbf1ec57e80671ef40ed98a84e980a71194cedf30c87c2b25c3a9e0b0107",
- "sha256:ccfb8e1e05a1174f7bd4c163700277ba730496094fe1a58bea9d4ac140a207c8"
+ "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc",
+ "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==20.0.23"
+ "version": "==20.0.31"
},
"webencodings": {
"hashes": [
@@ -990,33 +931,34 @@
},
"wheel": {
"hashes": [
- "sha256:8788e9155fe14f54164c1b9eb0a319d98ef02c160725587ad60f14ddc57b6f96",
- "sha256:df277cb51e61359aba502208d680f90c0493adec6f0e848af94948778aed386e"
+ "sha256:497add53525d16c173c2c1c733b8f655510e909ea78cc0e29d374243544b77a2",
+ "sha256:99a22d87add3f634ff917310a3d87e499f19e663413a52eb9232c447aa646c9f"
],
"index": "pypi",
- "version": "==0.34.2"
+ "version": "==0.35.1"
},
"yarl": {
"hashes": [
- "sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce",
- "sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6",
- "sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce",
- "sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae",
- "sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d",
- "sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f",
- "sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b",
- "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b",
- "sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb",
- "sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462",
- "sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea",
- "sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70",
- "sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1",
- "sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a",
- "sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b",
- "sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080",
- "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2"
- ],
- "version": "==1.4.2"
+ "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409",
+ "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593",
+ "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2",
+ "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8",
+ "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d",
+ "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692",
+ "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02",
+ "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a",
+ "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8",
+ "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6",
+ "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511",
+ "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e",
+ "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a",
+ "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb",
+ "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f",
+ "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317",
+ "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==1.5.1"
}
}
}
From e5bb92f53c9ad736b4cade4738c8e6727f68a88c Mon Sep 17 00:00:00 2001
From: Jelle Zijlstra
Date: Thu, 20 Aug 2020 05:23:28 -0700
Subject: [PATCH 022/680] Disable string splitting/merging by default (#1609)
* put experimental string stuff behind a flag
* update tests
* don't need an output section if it's the same as the input
* Primer: Expect no formatting changes in attrs, hypothesis and poetry with --experimental-string-processing off
Co-authored-by: Hugo van Kemenade
---
src/black/__init__.py | 86 ++++---
src/black_primer/primer.json | 6 +-
tests/data/long_strings_flag_disabled.py | 278 +++++++++++++++++++++++
tests/test_black.py | 172 +++++++-------
4 files changed, 420 insertions(+), 122 deletions(-)
create mode 100644 tests/data/long_strings_flag_disabled.py
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 391233ed448..2613b2f9e16 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -240,6 +240,7 @@ class Mode:
target_versions: Set[TargetVersion] = field(default_factory=set)
line_length: int = DEFAULT_LINE_LENGTH
string_normalization: bool = True
+ experimental_string_processing: bool = False
is_pyi: bool = False
def get_cache_key(self) -> str:
@@ -376,6 +377,15 @@ def target_version_option_callback(
is_flag=True,
help="Don't normalize string quotes or prefixes.",
)
+@click.option(
+ "--experimental-string-processing",
+ is_flag=True,
+ hidden=True,
+ help=(
+ "Experimental option that performs more normalization on string literals."
+ " Currently disabled because it leads to some crashes."
+ ),
+)
@click.option(
"--check",
is_flag=True,
@@ -485,6 +495,7 @@ def main(
fast: bool,
pyi: bool,
skip_string_normalization: bool,
+ experimental_string_processing: bool,
quiet: bool,
verbose: bool,
include: str,
@@ -505,6 +516,7 @@ def main(
line_length=line_length,
is_pyi=pyi,
string_normalization=not skip_string_normalization,
+ experimental_string_processing=experimental_string_processing,
)
if config and verbose:
out(f"Using configuration from {config}.", bold=False, fg="blue")
@@ -984,10 +996,7 @@ def f(
before, after = elt.maybe_empty_lines(current_line)
dst_contents.append(str(empty_line) * before)
for line in transform_line(
- current_line,
- line_length=mode.line_length,
- normalize_strings=mode.string_normalization,
- features=split_line_features,
+ current_line, mode=mode, features=split_line_features
):
dst_contents.append(str(line))
return "".join(dst_contents)
@@ -2649,10 +2658,7 @@ def make_comment(content: str) -> str:
def transform_line(
- line: Line,
- line_length: int,
- normalize_strings: bool,
- features: Collection[Feature] = (),
+ line: Line, mode: Mode, features: Collection[Feature] = ()
) -> Iterator[Line]:
"""Transform a `line`, potentially splitting it into many lines.
@@ -2668,7 +2674,7 @@ def transform_line(
def init_st(ST: Type[StringTransformer]) -> StringTransformer:
"""Initialize StringTransformer"""
- return ST(line_length, normalize_strings)
+ return ST(mode.line_length, mode.string_normalization)
string_merge = init_st(StringMerger)
string_paren_strip = init_st(StringParenStripper)
@@ -2681,21 +2687,26 @@ def init_st(ST: Type[StringTransformer]) -> StringTransformer:
and not line.should_explode
and not line.is_collection_with_optional_trailing_comma
and (
- is_line_short_enough(line, line_length=line_length, line_str=line_str)
+ is_line_short_enough(line, line_length=mode.line_length, line_str=line_str)
or line.contains_unsplittable_type_ignore()
)
and not (line.contains_standalone_comments() and line.inside_brackets)
):
# Only apply basic string preprocessing, since lines shouldn't be split here.
- transformers = [string_merge, string_paren_strip]
+ if mode.experimental_string_processing:
+ transformers = [string_merge, string_paren_strip]
+ else:
+ transformers = []
elif line.is_def:
transformers = [left_hand_split]
else:
def rhs(line: Line, features: Collection[Feature]) -> Iterator[Line]:
- for omit in generate_trailers_to_omit(line, line_length):
- lines = list(right_hand_split(line, line_length, features, omit=omit))
- if is_line_short_enough(lines[0], line_length=line_length):
+ for omit in generate_trailers_to_omit(line, mode.line_length):
+ lines = list(
+ right_hand_split(line, mode.line_length, features, omit=omit)
+ )
+ if is_line_short_enough(lines[0], line_length=mode.line_length):
yield from lines
return
@@ -2706,24 +2717,30 @@ def rhs(line: Line, features: Collection[Feature]) -> Iterator[Line]:
# See #762 and #781 for the full story.
yield from right_hand_split(line, line_length=1, features=features)
- if line.inside_brackets:
- transformers = [
- string_merge,
- string_paren_strip,
- delimiter_split,
- standalone_comment_split,
- string_split,
- string_paren_wrap,
- rhs,
- ]
+ if mode.experimental_string_processing:
+ if line.inside_brackets:
+ transformers = [
+ string_merge,
+ string_paren_strip,
+ delimiter_split,
+ standalone_comment_split,
+ string_split,
+ string_paren_wrap,
+ rhs,
+ ]
+ else:
+ transformers = [
+ string_merge,
+ string_paren_strip,
+ string_split,
+ string_paren_wrap,
+ rhs,
+ ]
else:
- transformers = [
- string_merge,
- string_paren_strip,
- string_split,
- string_paren_wrap,
- rhs,
- ]
+ if line.inside_brackets:
+ transformers = [delimiter_split, standalone_comment_split, rhs]
+ else:
+ transformers = [rhs]
for transform in transformers:
# We are accumulating lines in `result` because we might want to abort
@@ -2738,12 +2755,7 @@ def rhs(line: Line, features: Collection[Feature]) -> Iterator[Line]:
)
result.extend(
- transform_line(
- transformed_line,
- line_length=line_length,
- normalize_strings=normalize_strings,
- features=features,
- )
+ transform_line(transformed_line, mode=mode, features=features)
)
except CannotTransform:
continue
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index f5cc3fdf931..7d8271188a2 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -10,7 +10,7 @@
},
"attrs": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/python-attrs/attrs.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -47,7 +47,7 @@
},
"hypothesis": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/HypothesisWorks/hypothesis.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -63,7 +63,7 @@
},
"poetry": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/python-poetry/poetry.git",
"long_checkout": false,
"py_versions": ["all"]
diff --git a/tests/data/long_strings_flag_disabled.py b/tests/data/long_strings_flag_disabled.py
new file mode 100644
index 00000000000..1ea864d4bd5
--- /dev/null
+++ b/tests/data/long_strings_flag_disabled.py
@@ -0,0 +1,278 @@
+x = "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three."
+
+x += "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three."
+
+y = "Short string"
+
+print(
+ "This is a really long string inside of a print statement with extra arguments attached at the end of it.",
+ x,
+ y,
+ z,
+)
+
+print(
+ "This is a really long string inside of a print statement with no extra arguments attached at the end of it."
+)
+
+D1 = {
+ "The First": "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.",
+ "The Second": "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.",
+}
+
+D2 = {
+ 1.0: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.",
+ 2.0: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.",
+}
+
+D3 = {
+ x: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.",
+ y: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.",
+}
+
+D4 = {
+ "A long and ridiculous {}".format(
+ string_key
+ ): "This is a really really really long string that has to go i,side of a dictionary. It is soooo bad.",
+ some_func(
+ "calling", "some", "stuff"
+ ): "This is a really really really long string that has to go inside of a dictionary. It is {soooo} bad (#{x}).".format(
+ sooo="soooo", x=2
+ ),
+ "A %s %s"
+ % (
+ "formatted",
+ "string",
+ ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)."
+ % ("soooo", 2),
+}
+
+func_with_keywords(
+ my_arg,
+ my_kwarg="Long keyword strings also need to be wrapped, but they will probably need to be handled a little bit differently.",
+)
+
+bad_split1 = (
+ "But what should happen when code has already been formatted but in the wrong way? Like"
+ " with a space at the end instead of the beginning. Or what about when it is split too soon?"
+)
+
+bad_split2 = (
+ "But what should happen when code has already "
+ "been formatted but in the wrong way? Like "
+ "with a space at the end instead of the "
+ "beginning. Or what about when it is split too "
+ "soon? In the case of a split that is too "
+ "short, black will try to honer the custom "
+ "split."
+)
+
+bad_split3 = (
+ "What if we have inline comments on " # First Comment
+ "each line of a bad split? In that " # Second Comment
+ "case, we should just leave it alone." # Third Comment
+)
+
+bad_split_func1(
+ "But what should happen when code has already "
+ "been formatted but in the wrong way? Like "
+ "with a space at the end instead of the "
+ "beginning. Or what about when it is split too "
+ "soon? In the case of a split that is too "
+ "short, black will try to honer the custom "
+ "split.",
+ xxx,
+ yyy,
+ zzz,
+)
+
+bad_split_func2(
+ xxx,
+ yyy,
+ zzz,
+ long_string_kwarg="But what should happen when code has already been formatted but in the wrong way? Like "
+ "with a space at the end instead of the beginning. Or what about when it is split too "
+ "soon?",
+)
+
+bad_split_func3(
+ (
+ "But what should happen when code has already "
+ r"been formatted but in the wrong way? Like "
+ "with a space at the end instead of the "
+ r"beginning. Or what about when it is split too "
+ r"soon? In the case of a split that is too "
+ "short, black will try to honer the custom "
+ "split."
+ ),
+ xxx,
+ yyy,
+ zzz,
+)
+
+raw_string = r"This is a long raw string. When re-formatting this string, black needs to make sure it prepends the 'r' onto the new string."
+
+fmt_string1 = "We also need to be sure to preserve any and all {} which may or may not be attached to the string in question.".format(
+ "method calls"
+)
+
+fmt_string2 = "But what about when the string is {} but {}".format(
+ "short",
+ "the method call is really really really really really really really really long?",
+)
+
+old_fmt_string1 = (
+ "While we are on the topic of %s, we should also note that old-style formatting must also be preserved, since some %s still uses it."
+ % ("formatting", "code")
+)
+
+old_fmt_string2 = "This is a %s %s %s %s" % (
+ "really really really really really",
+ "old",
+ "way to format strings!",
+ "Use f-strings instead!",
+)
+
+old_fmt_string3 = (
+ "Whereas only the strings after the percent sign were long in the last example, this example uses a long initial string as well. This is another %s %s %s %s"
+ % (
+ "really really really really really",
+ "old",
+ "way to format strings!",
+ "Use f-strings instead!",
+ )
+)
+
+fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one."
+
+fstring_with_no_fexprs = f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever."
+
+comment_string = "Long lines with inline comments should have their comments appended to the reformatted string's enclosing right parentheses." # This comment gets thrown to the top.
+
+arg_comment_string = print(
+ "Long lines with inline comments which are apart of (and not the only member of) an argument list should have their comments appended to the reformatted string's enclosing left parentheses.", # This comment stays on the bottom.
+ "Arg #2",
+ "Arg #3",
+ "Arg #4",
+ "Arg #5",
+)
+
+pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501
+
+pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa
+
+"""This is a really really really long triple quote string and it should not be touched."""
+
+triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched."""
+
+assert (
+ some_type_of_boolean_expression
+), "Followed by a really really really long string that is used to provide context to the AssertionError exception."
+
+assert (
+ some_type_of_boolean_expression
+), "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format(
+ "formatting"
+)
+
+assert some_type_of_boolean_expression, (
+ "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string %s."
+ % "formatting"
+)
+
+assert some_type_of_boolean_expression, (
+ "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic %s %s."
+ % ("string", "formatting")
+)
+
+some_function_call(
+ "With a reallly generic name and with a really really long string that is, at some point down the line, "
+ + added
+ + " to a variable and then added to another string."
+)
+
+some_function_call(
+ "With a reallly generic name and with a really really long string that is, at some point down the line, "
+ + added
+ + " to a variable and then added to another string. But then what happens when the final string is also supppppperrrrr long?! Well then that second (realllllllly long) string should be split too.",
+ "and a second argument",
+ and_a_third,
+)
+
+return "A really really really really really really really really really really really really really long {} {}".format(
+ "return", "value"
+)
+
+func_with_bad_comma(
+ "This is a really long string argument to a function that has a trailing comma which should NOT be there.",
+)
+
+func_with_bad_comma(
+ "This is a really long string argument to a function that has a trailing comma which should NOT be there.", # comment after comma
+)
+
+func_with_bad_comma(
+ (
+ "This is a really long string argument to a function that has a trailing comma"
+ " which should NOT be there."
+ ),
+)
+
+func_with_bad_comma(
+ (
+ "This is a really long string argument to a function that has a trailing comma"
+ " which should NOT be there."
+ ), # comment after comma
+)
+
+func_with_bad_parens(
+ ("short string that should have parens stripped"), x, y, z,
+)
+
+func_with_bad_parens(
+ x, y, ("short string that should have parens stripped"), z,
+)
+
+annotated_variable: Final = (
+ "This is a large "
+ + STRING
+ + " that has been "
+ + CONCATENATED
+ + "using the '+' operator."
+)
+annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
+annotated_variable: Literal[
+ "fakse_literal"
+] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped."
+
+backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\"
+backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\"
+backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\"
+
+short_string = "Hi" " there."
+
+func_call(short_string=("Hi" " there."))
+
+raw_strings = r"Don't" " get" r" merged" " unless they are all raw."
+
+
+def foo():
+ yield "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three."
+
+
+x = f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four."
+
+long_unmergable_string_with_pragma = (
+ "This is a really long string that can't be merged because it has a likely pragma at the end" # type: ignore
+ " of it."
+)
+
+long_unmergable_string_with_pragma = (
+ "This is a really long string that can't be merged because it has a likely pragma at the end" # noqa
+ " of it."
+)
+
+long_unmergable_string_with_pragma = (
+ "This is a really long string that can't be merged because it has a likely pragma at the end" # pylint: disable=some-pylint-check
+ " of it."
+)
diff --git a/tests/test_black.py b/tests/test_black.py
index 3c766330a08..686232a7f9c 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -3,6 +3,7 @@
import logging
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager
+from dataclasses import replace
from functools import partial
from io import BytesIO, TextIOWrapper
import os
@@ -36,8 +37,9 @@
from .test_primer import PrimerCLITests # noqa: F401
-ff = partial(black.format_file_in_place, mode=black.FileMode(), fast=True)
-fs = partial(black.format_str, mode=black.FileMode())
+DEFAULT_MODE = black.FileMode(experimental_string_processing=True)
+ff = partial(black.format_file_in_place, mode=DEFAULT_MODE, fast=True)
+fs = partial(black.format_str, mode=DEFAULT_MODE)
THIS_FILE = Path(__file__)
THIS_DIR = THIS_FILE.parent
PROJECT_ROOT = THIS_DIR.parent
@@ -190,13 +192,13 @@ def invokeBlack(
)
@patch("black.dump_to_file", dump_to_stderr)
- def checkSourceFile(self, name: str) -> None:
+ def checkSourceFile(self, name: str, mode: black.FileMode = DEFAULT_MODE) -> None:
path = THIS_DIR.parent / name
source, expected = read_data(str(path), data=False)
- actual = fs(source)
+ actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, mode)
self.assertFalse(ff(path))
@patch("black.dump_to_file", dump_to_stderr)
@@ -205,7 +207,7 @@ def test_empty(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_empty_ff(self) -> None:
expected = ""
@@ -267,7 +269,7 @@ def test_piping(self) -> None:
self.assertEqual(result.exit_code, 0)
self.assertFormatEqual(expected, result.output)
black.assert_equivalent(source, result.output)
- black.assert_stable(source, result.output, black.FileMode())
+ black.assert_stable(source, result.output, DEFAULT_MODE)
def test_piping_diff(self) -> None:
diff_header = re.compile(
@@ -320,7 +322,7 @@ def test_function(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_function2(self) -> None:
@@ -328,7 +330,7 @@ def test_function2(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_function_trailing_comma(self) -> None:
@@ -336,7 +338,7 @@ def test_function_trailing_comma(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_expression(self) -> None:
@@ -344,14 +346,14 @@ def test_expression(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_pep_572(self) -> None:
source, expected = read_data("pep_572")
actual = fs(source)
self.assertFormatEqual(expected, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
if sys.version_info >= (3, 8):
black.assert_equivalent(source, actual)
@@ -375,7 +377,7 @@ def test_expression_ff(self) -> None:
self.assertFormatEqual(expected, actual)
with patch("black.dump_to_file", dump_to_stderr):
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_expression_diff(self) -> None:
source, _ = read_data("expression.py")
@@ -427,14 +429,14 @@ def test_fstring(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_pep_570(self) -> None:
source, expected = read_data("pep_570")
actual = fs(source)
self.assertFormatEqual(expected, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
if sys.version_info >= (3, 8):
black.assert_equivalent(source, actual)
@@ -452,8 +454,8 @@ def test_string_quotes(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
- mode = black.FileMode(string_normalization=False)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+ mode = replace(DEFAULT_MODE, string_normalization=False)
not_normalized = fs(source, mode=mode)
self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
black.assert_equivalent(source, not_normalized)
@@ -465,7 +467,7 @@ def test_docstring(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_long_strings(self) -> None:
"""Tests for splitting long strings."""
@@ -473,7 +475,15 @@ def test_long_strings(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ def test_long_strings_flag_disabled(self) -> None:
+ """Tests for turning off the string processing logic."""
+ source, expected = read_data("long_strings_flag_disabled")
+ mode = replace(DEFAULT_MODE, experimental_string_processing=False)
+ actual = fs(source, mode=mode)
+ self.assertFormatEqual(expected, actual)
+ black.assert_stable(expected, actual, mode)
@patch("black.dump_to_file", dump_to_stderr)
def test_long_strings__edge_case(self) -> None:
@@ -482,7 +492,7 @@ def test_long_strings__edge_case(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_long_strings__regression(self) -> None:
@@ -491,7 +501,7 @@ def test_long_strings__regression(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_slices(self) -> None:
@@ -499,7 +509,7 @@ def test_slices(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_percent_precedence(self) -> None:
@@ -507,7 +517,7 @@ def test_percent_precedence(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments(self) -> None:
@@ -515,7 +525,7 @@ def test_comments(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments2(self) -> None:
@@ -523,7 +533,7 @@ def test_comments2(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments3(self) -> None:
@@ -531,7 +541,7 @@ def test_comments3(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments4(self) -> None:
@@ -539,7 +549,7 @@ def test_comments4(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments5(self) -> None:
@@ -547,7 +557,7 @@ def test_comments5(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments6(self) -> None:
@@ -555,7 +565,7 @@ def test_comments6(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments7(self) -> None:
@@ -563,7 +573,7 @@ def test_comments7(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comment_after_escaped_newline(self) -> None:
@@ -571,7 +581,7 @@ def test_comment_after_escaped_newline(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_cantfit(self) -> None:
@@ -579,7 +589,7 @@ def test_cantfit(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_import_spacing(self) -> None:
@@ -587,7 +597,7 @@ def test_import_spacing(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_composition(self) -> None:
@@ -595,7 +605,7 @@ def test_composition(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_empty_lines(self) -> None:
@@ -603,7 +613,7 @@ def test_empty_lines(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_remove_parens(self) -> None:
@@ -611,7 +621,7 @@ def test_remove_parens(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_string_prefixes(self) -> None:
@@ -619,12 +629,12 @@ def test_string_prefixes(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_numeric_literals(self) -> None:
source, expected = read_data("numeric_literals")
- mode = black.FileMode(target_versions=black.PY36_VERSIONS)
+ mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@@ -633,7 +643,7 @@ def test_numeric_literals(self) -> None:
@patch("black.dump_to_file", dump_to_stderr)
def test_numeric_literals_ignoring_underscores(self) -> None:
source, expected = read_data("numeric_literals_skip_underscores")
- mode = black.FileMode(target_versions=black.PY36_VERSIONS)
+ mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@@ -644,7 +654,7 @@ def test_numeric_literals_py2(self) -> None:
source, expected = read_data("numeric_literals_py2")
actual = fs(source)
self.assertFormatEqual(expected, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_python2(self) -> None:
@@ -652,12 +662,12 @@ def test_python2(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_python2_print_function(self) -> None:
source, expected = read_data("python2_print_function")
- mode = black.FileMode(target_versions={TargetVersion.PY27})
+ mode = replace(DEFAULT_MODE, target_versions={TargetVersion.PY27})
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@@ -669,11 +679,11 @@ def test_python2_unicode_literals(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_stub(self) -> None:
- mode = black.FileMode(is_pyi=True)
+ mode = replace(DEFAULT_MODE, is_pyi=True)
source, expected = read_data("stub.pyi")
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
@@ -688,7 +698,7 @@ def test_async_as_identifier(self) -> None:
major, minor = sys.version_info[:2]
if major < 3 or (major <= 3 and minor < 7):
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
# ensure black can parse this when the target is 3.6
self.invokeBlack([str(source_path), "--target-version", "py36"])
# but not on 3.7, because async/await is no longer an identifier
@@ -703,7 +713,7 @@ def test_python37(self) -> None:
major, minor = sys.version_info[:2]
if major > 3 or (major == 3 and minor >= 7):
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
# ensure black can parse this when the target is 3.7
self.invokeBlack([str(source_path), "--target-version", "py37"])
# but not on 3.6, because we use async as a reserved keyword
@@ -717,7 +727,7 @@ def test_python38(self) -> None:
major, minor = sys.version_info[:2]
if major > 3 or (major == 3 and minor >= 8):
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff(self) -> None:
@@ -725,7 +735,7 @@ def test_fmtonoff(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff2(self) -> None:
@@ -733,7 +743,7 @@ def test_fmtonoff2(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff3(self) -> None:
@@ -741,7 +751,7 @@ def test_fmtonoff3(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff4(self) -> None:
@@ -749,7 +759,7 @@ def test_fmtonoff4(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_remove_empty_parentheses_after_class(self) -> None:
@@ -757,7 +767,7 @@ def test_remove_empty_parentheses_after_class(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_new_line_between_class_and_code(self) -> None:
@@ -765,7 +775,7 @@ def test_new_line_between_class_and_code(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_bracket_match(self) -> None:
@@ -773,7 +783,7 @@ def test_bracket_match(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_tuple_assign(self) -> None:
@@ -781,7 +791,7 @@ def test_tuple_assign(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_beginning_backslash(self) -> None:
@@ -789,7 +799,7 @@ def test_beginning_backslash(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_tab_comment_indentation(self) -> None:
contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
@@ -1218,7 +1228,7 @@ def err(msg: str, **kwargs: Any) -> None:
def test_format_file_contents(self) -> None:
empty = ""
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with self.assertRaises(black.NothingChanged):
black.format_file_contents(empty, mode=mode, fast=False)
just_nl = "\n"
@@ -1263,7 +1273,7 @@ def err(msg: str, **kwargs: Any) -> None:
self.assertEqual("".join(err_lines), "")
def test_cache_broken_file(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace:
cache_file = black.get_cache_file(mode)
with cache_file.open("w") as fobj:
@@ -1277,7 +1287,7 @@ def test_cache_broken_file(self) -> None:
self.assertIn(src, cache)
def test_cache_single_file_already_cached(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace:
src = (workspace / "test.py").resolve()
with src.open("w") as fobj:
@@ -1289,7 +1299,7 @@ def test_cache_single_file_already_cached(self) -> None:
@event_loop()
def test_cache_multiple_files(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace, patch(
"black.ProcessPoolExecutor", new=ThreadPoolExecutor
):
@@ -1310,7 +1320,7 @@ def test_cache_multiple_files(self) -> None:
self.assertIn(two, cache)
def test_no_cache_when_writeback_diff(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace:
src = (workspace / "test.py").resolve()
with src.open("w") as fobj:
@@ -1320,7 +1330,7 @@ def test_no_cache_when_writeback_diff(self) -> None:
self.assertFalse(cache_file.exists())
def test_no_cache_when_stdin(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir():
result = CliRunner().invoke(
black.main, ["-"], input=BytesIO(b"print('hello')")
@@ -1330,12 +1340,12 @@ def test_no_cache_when_stdin(self) -> None:
self.assertFalse(cache_file.exists())
def test_read_cache_no_cachefile(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir():
self.assertEqual(black.read_cache(mode), {})
def test_write_cache_read_cache(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace:
src = (workspace / "test.py").resolve()
src.touch()
@@ -1361,7 +1371,7 @@ def test_filter_cached(self) -> None:
self.assertEqual(done, {cached})
def test_write_cache_creates_directory_if_needed(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir(exists=False) as workspace:
self.assertFalse(workspace.exists())
black.write_cache({}, [], mode)
@@ -1369,7 +1379,7 @@ def test_write_cache_creates_directory_if_needed(self) -> None:
@event_loop()
def test_failed_formatting_does_not_get_cached(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace, patch(
"black.ProcessPoolExecutor", new=ThreadPoolExecutor
):
@@ -1385,7 +1395,7 @@ def test_failed_formatting_does_not_get_cached(self) -> None:
self.assertIn(clean, cache)
def test_write_cache_write_fail(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir(), patch.object(Path, "open") as mock:
mock.side_effect = OSError
black.write_cache({}, [], mode)
@@ -1428,8 +1438,8 @@ def test_broken_symlink(self) -> None:
self.invokeBlack([str(workspace.resolve())])
def test_read_cache_line_lengths(self) -> None:
- mode = black.FileMode()
- short_mode = black.FileMode(line_length=1)
+ mode = DEFAULT_MODE
+ short_mode = replace(DEFAULT_MODE, line_length=1)
with cache_dir() as workspace:
path = (workspace / "file.py").resolve()
path.touch()
@@ -1444,11 +1454,11 @@ def test_tricky_unicode_symbols(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_single_file_force_pyi(self) -> None:
- reg_mode = black.FileMode()
- pyi_mode = black.FileMode(is_pyi=True)
+ reg_mode = DEFAULT_MODE
+ pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
contents, expected = read_data("force_pyi")
with cache_dir() as workspace:
path = (workspace / "file.py").resolve()
@@ -1466,8 +1476,8 @@ def test_single_file_force_pyi(self) -> None:
@event_loop()
def test_multi_file_force_pyi(self) -> None:
- reg_mode = black.FileMode()
- pyi_mode = black.FileMode(is_pyi=True)
+ reg_mode = DEFAULT_MODE
+ pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
contents, expected = read_data("force_pyi")
with cache_dir() as workspace:
paths = [
@@ -1499,8 +1509,8 @@ def test_pipe_force_pyi(self) -> None:
self.assertFormatEqual(actual, expected)
def test_single_file_force_py36(self) -> None:
- reg_mode = black.FileMode()
- py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
+ reg_mode = DEFAULT_MODE
+ py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
source, expected = read_data("force_py36")
with cache_dir() as workspace:
path = (workspace / "file.py").resolve()
@@ -1518,8 +1528,8 @@ def test_single_file_force_py36(self) -> None:
@event_loop()
def test_multi_file_force_py36(self) -> None:
- reg_mode = black.FileMode()
- py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
+ reg_mode = DEFAULT_MODE
+ py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
source, expected = read_data("force_py36")
with cache_dir() as workspace:
paths = [
@@ -1546,7 +1556,7 @@ def test_collections(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_pipe_force_py36(self) -> None:
source, expected = read_data("force_py36")
@@ -1827,9 +1837,7 @@ def test_parse_pyproject_toml(self) -> None:
def test_read_pyproject_toml(self) -> None:
test_toml_file = THIS_DIR / "test.toml"
fake_ctx = FakeContext()
- black.read_pyproject_toml(
- fake_ctx, FakeParameter(), str(test_toml_file),
- )
+ black.read_pyproject_toml(fake_ctx, FakeParameter(), str(test_toml_file))
config = fake_ctx.default_map
self.assertEqual(config["verbose"], "1")
self.assertEqual(config["check"], "no")
From 37a0020e073555ffe0921ec1356a27610aadcca4 Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Thu, 20 Aug 2020 18:06:41 -0400
Subject: [PATCH 023/680] Upgrade docs to Sphinx 3+ and add doc build test
(#1613)
* Upgrade docs to Sphinx 3+
* Fix all the warnings...
- Fixed bad docstrings
- Fixed bad fenced code blocks in documentation
- Blocklisted some sections from being generated from the README
- Added missing documentation to index.rst
- Fixed an invalid autofunction directive in reference/reference_functions.rst
- Pin another documentation dependency
* Add documentation build test
---
.github/workflows/doc.yml | 36 +++++++++++++++++++++++++
README.md | 4 +--
docs/black_primer.md | 6 ++---
docs/compatible_configs.md | 2 +-
docs/conf.py | 37 ++++++++++++++++++--------
docs/index.rst | 3 ++-
docs/reference/reference_functions.rst | 2 +-
docs/requirements.txt | 5 ++--
src/black/__init__.py | 1 +
9 files changed, 75 insertions(+), 21 deletions(-)
create mode 100644 .github/workflows/doc.yml
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
new file mode 100644
index 00000000000..6023a02a7f7
--- /dev/null
+++ b/.github/workflows/doc.yml
@@ -0,0 +1,36 @@
+name: Documentation Build
+
+on:
+ push:
+ paths:
+ - "docs/**"
+ - "README.md"
+ - "CHANGES.md"
+ - "CONTRIBUTING.md"
+ pull_request:
+ paths:
+ - "docs/**"
+ - "README.md"
+ - "CHANGES.md"
+ - "CONTRIBUTING.md"
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip setuptools wheel
+ python -m pip install -e "."
+ python -m pip install -r "docs/requirements.txt"
+
+ - name: Build documentation
+ run: sphinx-build -a -b html -W docs/ docs/_build/
diff --git a/README.md b/README.md
index 37b93c143a2..44f2d207c8b 100644
--- a/README.md
+++ b/README.md
@@ -464,7 +464,7 @@ Twisted and CPython:
Use the badge in your project's README.md:
-```markdown
+```md
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
```
@@ -625,7 +625,7 @@ Multiple contributions by:
- [Miroslav Shubernetskiy](mailto:miroslav@miki725.com)
- MomIsBestFriend
- [Nathan Goldbaum](mailto:ngoldbau@illinois.edu)
-- [Nathan Hunt](mailtoneighthan.hunt@gmail.com)
+- [Nathan Hunt](mailto:neighthan.hunt@gmail.com)
- [Neraste](mailto:neraste.herr10@gmail.com)
- [Nikolaus Waxweiler](mailto:madigens@gmail.com)
- [Ofek Lev](mailto:ofekmeister@gmail.com)
diff --git a/docs/black_primer.md b/docs/black_primer.md
index af4184233e2..a2dd964b7dc 100644
--- a/docs/black_primer.md
+++ b/docs/black_primer.md
@@ -71,7 +71,7 @@ each parameter is explained below:
"expect_formatting_changes": true,
"git_clone_url": "https://github.com/cooperlees/aioexabgp.git",
"long_checkout": false,
- "py_versions": ["all", "3.8"] // "all" ignores all other versions
+ "py_versions": ["all", "3.8"]
}
}
}
@@ -103,9 +103,9 @@ Failed projects:
+++ tests/b303_b304.py 2020-05-17 20:06:42.753851 +0000
@@ -26,11 +26,11 @@
maxint = 5 # this is okay
- # the following shouldn't crash
+ # the following should not crash
(a, b, c) = list(range(3))
- # it's different than this
+ # it is different than this
a, b, c = list(range(3))
- a, b, c, = list(range(3))
+ a, b, c = list(range(3))
diff --git a/docs/compatible_configs.md b/docs/compatible_configs.md
index 723fc889c00..25e959e3281 100644
--- a/docs/compatible_configs.md
+++ b/docs/compatible_configs.md
@@ -241,7 +241,7 @@ characters via `max-line-length = 88`.
pylintrc
-```rc
+```ini
[MESSAGES CONTROL]
disable = C0330, C0326
diff --git a/docs/conf.py b/docs/conf.py
index 3343087cfbd..575a14011e4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -17,18 +17,15 @@
import string
from typing import Callable, List, Optional, Pattern, Tuple, Set
from dataclasses import dataclass
-import os
import logging
from pkg_resources import get_distribution
-from recommonmark.parser import CommonMarkParser
logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.INFO)
LOG = logging.getLogger(__name__)
-# Get a relative path so logs printing out SRC isn't too long.
-CURRENT_DIR = Path(__file__).parent.relative_to(os.getcwd())
+CURRENT_DIR = Path(__file__).parent
README = CURRENT_DIR / ".." / "README.md"
REFERENCE_DIR = CURRENT_DIR / "reference"
STATIC_DIR = CURRENT_DIR / "_static"
@@ -200,7 +197,7 @@ def process_sections(
# -- Project information -----------------------------------------------------
project = "Black"
-copyright = "2018, Łukasz Langa and contributors to Black"
+copyright = "2020, Łukasz Langa and contributors to Black"
author = "Łukasz Langa and contributors to Black"
# Autopopulate version
@@ -213,7 +210,6 @@ def process_sections(
custom_sections = [
DocSection("the_black_code_style", CURRENT_DIR / "the_black_code_style.md",),
- DocSection("pragmatism", CURRENT_DIR / "the_black_code_style.md",),
DocSection("editor_integration", CURRENT_DIR / "editor_integration.md"),
DocSection("blackd", CURRENT_DIR / "blackd.md"),
DocSection("black_primer", CURRENT_DIR / "black_primer.md"),
@@ -221,28 +217,47 @@ def process_sections(
DocSection("change_log", CURRENT_DIR / ".." / "CHANGES.md"),
]
+# Sphinx complains when there is a source file that isn't referenced in any of the docs.
+# Since some sections autogenerated from the README are unused warnings will appear.
+#
+# Sections must be listed to what their name is when passed through make_filename().
+blocklisted_sections_from_readme = {
+ "license",
+ "pragmatism",
+ "testimonials",
+ "used_by",
+}
make_pypi_svg(release)
readme_sections = get_sections_from_readme()
+readme_sections = [
+ x for x in readme_sections if x.name not in blocklisted_sections_from_readme
+]
+
process_sections(custom_sections, readme_sections)
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-#
-# needs_sphinx = '1.0'
+needs_sphinx = "3.0"
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.napoleon"]
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.napoleon",
+ "recommonmark",
+]
+
+# If you need extensions of a certain version or higher, list them here.
+needs_extensions = {"recommonmark": "0.5"}
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
-source_parsers = {".md": CommonMarkParser}
-
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
source_suffix = [".rst", ".md"]
diff --git a/docs/index.rst b/docs/index.rst
index 676644ec6c6..f03d247d949 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -51,10 +51,12 @@ Contents
installation_and_usage
the_black_code_style
pyproject_toml
+ compatible_configs
editor_integration
blackd
black_primer
version_control_integration
+ github_actions
ignoring_unmodified_files
contributing_to_black
show_your_style
@@ -66,5 +68,4 @@ Indices and tables
==================
* :ref:`genindex`
-* :ref:`modindex`
* :ref:`search`
diff --git a/docs/reference/reference_functions.rst b/docs/reference/reference_functions.rst
index b10eea9b01f..1beecc10325 100644
--- a/docs/reference/reference_functions.rst
+++ b/docs/reference/reference_functions.rst
@@ -89,7 +89,7 @@ Split functions
.. autofunction:: black.standalone_comment_split
-.. autofunction:: black.split_line
+.. autofunction:: black.transform_line
Caching
-------
diff --git a/docs/requirements.txt b/docs/requirements.txt
index a36fd8a675b..4cad9bc205b 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,2 +1,3 @@
-recommonmark==0.4.0
-Sphinx==1.7.2
+recommonmark==0.6.0
+Sphinx==3.2.1
+Pygments==2.6.1
\ No newline at end of file
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 2613b2f9e16..d251136942a 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -952,6 +952,7 @@ def f(arg: str = "") -> None:
...
A more complex example:
+
>>> print(
... black.format_str(
... "def f(arg:str='')->None: hey",
From 4938cc9e9abf2581cb154c6a8d1ae66eb18e0d65 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Tue, 29 Oct 2019 00:34:46 +0100
Subject: [PATCH 024/680] Reset trailing comma handling
---
src/black/__init__.py | 138 ++++++++++++++----------------------------
1 file changed, 47 insertions(+), 91 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index d251136942a..2250943665a 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -1494,69 +1494,6 @@ def is_stub_class(self) -> bool:
Leaf(token.DOT, ".") for _ in range(3)
]
- @property
- def is_collection_with_optional_trailing_comma(self) -> bool:
- """Is this line a collection literal with a trailing comma that's optional?
-
- Note that the trailing comma in a 1-tuple is not optional.
- """
- if not self.leaves or len(self.leaves) < 4:
- return False
-
- # Look for and address a trailing colon.
- if self.leaves[-1].type == token.COLON:
- closer = self.leaves[-2]
- close_index = -2
- else:
- closer = self.leaves[-1]
- close_index = -1
- if closer.type not in CLOSING_BRACKETS or self.inside_brackets:
- return False
-
- if closer.type == token.RPAR:
- # Tuples require an extra check, because if there's only
- # one element in the tuple removing the comma unmakes the
- # tuple.
- #
- # We also check for parens before looking for the trailing
- # comma because in some cases (eg assigning a dict
- # literal) the literal gets wrapped in temporary parens
- # during parsing. This case is covered by the
- # collections.py test data.
- opener = closer.opening_bracket
- for _open_index, leaf in enumerate(self.leaves):
- if leaf is opener:
- break
-
- else:
- # Couldn't find the matching opening paren, play it safe.
- return False
-
- commas = 0
- comma_depth = self.leaves[close_index - 1].bracket_depth
- for leaf in self.leaves[_open_index + 1 : close_index]:
- if leaf.bracket_depth == comma_depth and leaf.type == token.COMMA:
- commas += 1
- if commas > 1:
- # We haven't looked yet for the trailing comma because
- # we might also have caught noop parens.
- return self.leaves[close_index - 1].type == token.COMMA
-
- elif commas == 1:
- return False # it's either a one-tuple or didn't have a trailing comma
-
- if self.leaves[close_index - 1].type in CLOSING_BRACKETS:
- close_index -= 1
- closer = self.leaves[close_index]
- if closer.type == token.RPAR:
- # TODO: this is a gut feeling. Will we ever see this?
- return False
-
- if self.leaves[close_index - 1].type != token.COMMA:
- return False
-
- return True
-
@property
def is_def(self) -> bool:
"""Is this a function definition? (Also returns True for async defs.)"""
@@ -1683,40 +1620,60 @@ def contains_multiline_strings(self) -> bool:
def maybe_remove_trailing_comma(self, closing: Leaf) -> bool:
"""Remove trailing comma if there is one and it's safe."""
- if not (self.leaves and self.leaves[-1].type == token.COMMA):
- return False
-
- # We remove trailing commas only in the case of importing a
- # single name from a module.
if not (
self.leaves
- and self.is_import
- and len(self.leaves) > 4
and self.leaves[-1].type == token.COMMA
and closing.type in CLOSING_BRACKETS
- and self.leaves[-4].type == token.NAME
- and (
- # regular `from foo import bar,`
- self.leaves[-4].value == "import"
- # `from foo import (bar as baz,)
- or (
- len(self.leaves) > 6
- and self.leaves[-6].value == "import"
- and self.leaves[-3].value == "as"
- )
- # `from foo import bar as baz,`
- or (
- len(self.leaves) > 5
- and self.leaves[-5].value == "import"
- and self.leaves[-3].value == "as"
- )
- )
- and closing.type == token.RPAR
):
return False
- self.remove_trailing_comma()
- return True
+ if closing.type == token.RBRACE:
+ self.remove_trailing_comma()
+ return True
+
+ if closing.type == token.RSQB:
+ comma = self.leaves[-1]
+ if comma.parent and comma.parent.type == syms.listmaker:
+ self.remove_trailing_comma()
+ return True
+
+ # For parens let's check if it's safe to remove the comma.
+ # Imports are always safe.
+ if self.is_import:
+ self.remove_trailing_comma()
+ return True
+
+ # Otherwise, if the trailing one is the only one, we might mistakenly
+ # change a tuple into a different type by removing the comma.
+ depth = closing.bracket_depth + 1
+ commas = 0
+ opening = closing.opening_bracket
+ for _opening_index, leaf in enumerate(self.leaves):
+ if leaf is opening:
+ break
+
+ else:
+ return False
+
+ for leaf in self.leaves[_opening_index + 1 :]:
+ if leaf is closing:
+ break
+
+ bracket_depth = leaf.bracket_depth
+ if bracket_depth == depth and leaf.type == token.COMMA:
+ commas += 1
+ if leaf.parent and leaf.parent.type in {
+ syms.arglist,
+ syms.typedargslist,
+ }:
+ commas += 1
+ break
+
+ if commas > 1:
+ self.remove_trailing_comma()
+ return True
+
+ return False
def append_comment(self, comment: Leaf) -> bool:
"""Add an inline or standalone comment to the line."""
@@ -2686,7 +2643,6 @@ def init_st(ST: Type[StringTransformer]) -> StringTransformer:
if (
not line.contains_uncollapsable_type_comments()
and not line.should_explode
- and not line.is_collection_with_optional_trailing_comma
and (
is_line_short_enough(line, line_length=mode.line_length, line_str=line_str)
or line.contains_unsplittable_type_ignore()
From 788268bc39a87d37a24d203fa5ee7b3953af3446 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Tue, 29 Oct 2019 00:50:42 +0100
Subject: [PATCH 025/680] Re-implement magic trailing comma handling:
- when a trailing comma is specified in any bracket pair, that signals to Black
that this bracket pair needs to be always exploded, e.g. presented as "one
item per line";
- this causes some changes to previously formatted code that erroneously left
trailing commas embedded into single-line expressions;
- internally, Black needs to be able to identify trailing commas that it put
itself compared to pre-existing trailing commas. We do this by using/abusing
lib2to3's `was_checked` attribute. It's True for internally generated
trailing commas and False for pre-existing ones (in fact, for all
pre-existing leaves and nodes).
Fixes #1288
---
CHANGES.md | 3 +
gallery/gallery.py | 5 +-
src/black/__init__.py | 126 +++++++++++-----------
src/blib2to3/pgen2/driver.py | 2 +-
tests/data/collections.py | 35 ++++--
tests/data/comments2.py | 8 +-
tests/data/comments7.py | 9 +-
tests/data/expression.diff | 27 ++++-
tests/data/expression.py | 22 +++-
tests/data/fmtonoff4.py | 7 +-
tests/data/function.py | 5 +-
tests/data/function2.py | 5 +-
tests/data/function_trailing_comma.py | 58 ++++++++--
tests/data/function_trailing_comma_wip.py | 5 +
tests/data/import_spacing.py | 8 +-
tests/data/long_strings.py | 32 +++++-
tests/data/long_strings__regression.py | 12 ++-
tests/data/long_strings_flag_disabled.py | 18 +++-
tests/test_black.py | 52 ++++++++-
19 files changed, 336 insertions(+), 103 deletions(-)
create mode 100644 tests/data/function_trailing_comma_wip.py
diff --git a/CHANGES.md b/CHANGES.md
index 6d418b9bec8..eb6d1c2ebbf 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -4,6 +4,9 @@
#### _Black_
+- re-implemented support for explicit trailing commas: now it works consistently within
+ any bracket pair, including nested structures (#1288 and duplicates)
+
- reindent docstrings when reindenting code around it (#1053)
- show colored diffs (#1266)
diff --git a/gallery/gallery.py b/gallery/gallery.py
index 2a56b4ed4c0..6b42ec3a6d4 100755
--- a/gallery/gallery.py
+++ b/gallery/gallery.py
@@ -127,7 +127,10 @@ def get_package(
def download_and_extract_top_packages(
- directory: Path, days: Days = 365, workers: int = 8, limit: slice = DEFAULT_SLICE,
+ directory: Path,
+ days: Days = 365,
+ workers: int = 8,
+ limit: slice = DEFAULT_SLICE,
) -> Generator[Path, None, None]:
with ThreadPoolExecutor(max_workers=workers) as executor:
bound_downloader = partial(get_package, version=None, directory=directory)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 2250943665a..8d0c70f06c7 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -1442,7 +1442,8 @@ def append(self, leaf: Leaf, preformatted: bool = False) -> None:
)
if self.inside_brackets or not preformatted:
self.bracket_tracker.mark(leaf)
- self.maybe_remove_trailing_comma(leaf)
+ if self.maybe_should_explode(leaf):
+ self.should_explode = True
if not self.append_comment(leaf):
self.leaves.append(leaf)
@@ -1618,59 +1619,26 @@ def contains_unsplittable_type_ignore(self) -> bool:
def contains_multiline_strings(self) -> bool:
return any(is_multiline_string(leaf) for leaf in self.leaves)
- def maybe_remove_trailing_comma(self, closing: Leaf) -> bool:
- """Remove trailing comma if there is one and it's safe."""
+ def maybe_should_explode(self, closing: Leaf) -> bool:
+ """Return True if this line should explode (always be split), that is when:
+ - there's a pre-existing trailing comma here; and
+ - it's not a one-tuple.
+ """
if not (
- self.leaves
+ closing.type in CLOSING_BRACKETS
+ and self.leaves
and self.leaves[-1].type == token.COMMA
- and closing.type in CLOSING_BRACKETS
+ and not self.leaves[-1].was_checked # pre-existing
):
return False
- if closing.type == token.RBRACE:
- self.remove_trailing_comma()
+ if closing.type in {token.RBRACE, token.RSQB}:
return True
- if closing.type == token.RSQB:
- comma = self.leaves[-1]
- if comma.parent and comma.parent.type == syms.listmaker:
- self.remove_trailing_comma()
- return True
-
- # For parens let's check if it's safe to remove the comma.
- # Imports are always safe.
if self.is_import:
- self.remove_trailing_comma()
return True
- # Otherwise, if the trailing one is the only one, we might mistakenly
- # change a tuple into a different type by removing the comma.
- depth = closing.bracket_depth + 1
- commas = 0
- opening = closing.opening_bracket
- for _opening_index, leaf in enumerate(self.leaves):
- if leaf is opening:
- break
-
- else:
- return False
-
- for leaf in self.leaves[_opening_index + 1 :]:
- if leaf is closing:
- break
-
- bracket_depth = leaf.bracket_depth
- if bracket_depth == depth and leaf.type == token.COMMA:
- commas += 1
- if leaf.parent and leaf.parent.type in {
- syms.arglist,
- syms.typedargslist,
- }:
- commas += 1
- break
-
- if commas > 1:
- self.remove_trailing_comma()
+ if not is_one_tuple_between(closing.opening_bracket, closing, self.leaves):
return True
return False
@@ -2647,7 +2615,7 @@ def init_st(ST: Type[StringTransformer]) -> StringTransformer:
is_line_short_enough(line, line_length=mode.line_length, line_str=line_str)
or line.contains_unsplittable_type_ignore()
)
- and not (line.contains_standalone_comments() and line.inside_brackets)
+ and not (line.inside_brackets and line.contains_standalone_comments())
):
# Only apply basic string preprocessing, since lines shouldn't be split here.
if mode.experimental_string_processing:
@@ -4772,10 +4740,8 @@ def right_hand_split(
tail = bracket_split_build_line(tail_leaves, line, opening_bracket)
bracket_split_succeeded_or_raise(head, body, tail)
if (
- # the body shouldn't be exploded
- not body.should_explode
# the opening bracket is an optional paren
- and opening_bracket.type == token.LPAR
+ opening_bracket.type == token.LPAR
and not opening_bracket.value
# the closing bracket is an optional paren
and closing_bracket.type == token.RPAR
@@ -4872,7 +4838,9 @@ def bracket_split_build_line(
continue
if leaves[i].type != token.COMMA:
- leaves.insert(i + 1, Leaf(token.COMMA, ","))
+ new_comma = Leaf(token.COMMA, ",")
+ new_comma.was_checked = True
+ leaves.insert(i + 1, new_comma)
break
# Populate the line
@@ -4880,8 +4848,8 @@ def bracket_split_build_line(
result.append(leaf, preformatted=True)
for comment_after in original.comments_after(leaf):
result.append(comment_after, preformatted=True)
- if is_body:
- result.should_explode = should_explode(result, opening_bracket)
+ if is_body and should_split_body_explode(result, opening_bracket):
+ result.should_explode = True
return result
@@ -4966,7 +4934,9 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
and current_line.leaves[-1].type != token.COMMA
and current_line.leaves[-1].type != STANDALONE_COMMENT
):
- current_line.append(Leaf(token.COMMA, ","))
+ new_comma = Leaf(token.COMMA, ",")
+ new_comma.was_checked = True
+ current_line.append(new_comma)
yield current_line
@@ -5588,24 +5558,60 @@ def ensure_visible(leaf: Leaf) -> None:
leaf.value = ")"
-def should_explode(line: Line, opening_bracket: Leaf) -> bool:
+def should_split_body_explode(line: Line, opening_bracket: Leaf) -> bool:
"""Should `line` immediately be split with `delimiter_split()` after RHS?"""
- if not (
- opening_bracket.parent
- and opening_bracket.parent.type in {syms.atom, syms.import_from}
- and opening_bracket.value in "[{("
- ):
+ if not (opening_bracket.parent and opening_bracket.value in "[{("):
return False
+ # We're essentially checking if the body is delimited by commas and there's more
+ # than one of them (we're excluding the trailing comma and if the delimiter priority
+ # is still commas, that means there's more).
+ exclude = set()
+ pre_existing_trailing_comma = False
try:
last_leaf = line.leaves[-1]
- exclude = {id(last_leaf)} if last_leaf.type == token.COMMA else set()
+ if last_leaf.type == token.COMMA:
+ pre_existing_trailing_comma = not last_leaf.was_checked
+ exclude.add(id(last_leaf))
max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude)
except (IndexError, ValueError):
return False
- return max_priority == COMMA_PRIORITY
+ return max_priority == COMMA_PRIORITY and (
+ # always explode imports
+ opening_bracket.parent.type in {syms.atom, syms.import_from}
+ or pre_existing_trailing_comma
+ )
+
+
+def is_one_tuple_between(opening: Leaf, closing: Leaf, leaves: List[Leaf]) -> bool:
+ """Return True if content between `opening` and `closing` looks like a one-tuple."""
+ depth = closing.bracket_depth + 1
+ for _opening_index, leaf in enumerate(leaves):
+ if leaf is opening:
+ break
+
+ else:
+ raise LookupError("Opening paren not found in `leaves`")
+
+ commas = 0
+ _opening_index += 1
+ for leaf in leaves[_opening_index:]:
+ if leaf is closing:
+ break
+
+ bracket_depth = leaf.bracket_depth
+ if bracket_depth == depth and leaf.type == token.COMMA:
+ commas += 1
+ if leaf.parent and leaf.parent.type in {
+ syms.arglist,
+ syms.typedargslist,
+ }:
+ commas += 1
+ break
+
+ return commas < 2
def get_features_used(node: Node) -> Set[Feature]:
diff --git a/src/blib2to3/pgen2/driver.py b/src/blib2to3/pgen2/driver.py
index 052c94883cf..81940f78f0f 100644
--- a/src/blib2to3/pgen2/driver.py
+++ b/src/blib2to3/pgen2/driver.py
@@ -128,7 +128,7 @@ def parse_stream(self, stream: IO[Text], debug: bool = False) -> NL:
return self.parse_stream_raw(stream, debug)
def parse_file(
- self, filename: Path, encoding: Optional[Text] = None, debug: bool = False,
+ self, filename: Path, encoding: Optional[Text] = None, debug: bool = False
) -> NL:
"""Parse a file and return the syntax tree."""
with io.open(filename, "r", encoding=encoding) as stream:
diff --git a/tests/data/collections.py b/tests/data/collections.py
index ebe8d3c5200..68431665211 100644
--- a/tests/data/collections.py
+++ b/tests/data/collections.py
@@ -2,18 +2,18 @@
from . import A, B, C
-# unwraps
+# keeps existing trailing comma
from foo import (
bar,
)
-# stays wrapped
+# also keeps existing structure
from foo import (
baz,
qux,
)
-# as doesn't get confusing when unwrapped
+# `as` works as well
from foo import (
xyzzy as magic,
)
@@ -77,17 +77,21 @@
from . import A, B, C
-# unwraps
-from foo import bar
+# keeps existing trailing comma
+from foo import (
+ bar,
+)
-# stays wrapped
+# also keeps existing structure
from foo import (
baz,
qux,
)
-# as doesn't get confusing when unwrapped
-from foo import xyzzy as magic
+# `as` works as well
+from foo import (
+ xyzzy as magic,
+)
a = {
1,
@@ -151,11 +155,20 @@
if True:
ec2client.get_waiter("instance_stopped").wait(
- InstanceIds=[instance.id], WaiterConfig={"Delay": 5,}
+ InstanceIds=[instance.id],
+ WaiterConfig={
+ "Delay": 5,
+ },
)
ec2client.get_waiter("instance_stopped").wait(
- InstanceIds=[instance.id], WaiterConfig={"Delay": 5,},
+ InstanceIds=[instance.id],
+ WaiterConfig={
+ "Delay": 5,
+ },
)
ec2client.get_waiter("instance_stopped").wait(
- InstanceIds=[instance.id], WaiterConfig={"Delay": 5,},
+ InstanceIds=[instance.id],
+ WaiterConfig={
+ "Delay": 5,
+ },
)
diff --git a/tests/data/comments2.py b/tests/data/comments2.py
index 89c29104bd8..221cb3fe143 100644
--- a/tests/data/comments2.py
+++ b/tests/data/comments2.py
@@ -316,7 +316,13 @@ def inline_comments_in_brackets_ruin_everything():
)
-CONFIG_FILES = [CONFIG_FILE,] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final
+CONFIG_FILES = (
+ [
+ CONFIG_FILE,
+ ]
+ + SHARED_CONFIG_FILES
+ + USER_CONFIG_FILES
+) # type: Final
class Test:
diff --git a/tests/data/comments7.py b/tests/data/comments7.py
index 436df1a2a41..a7bd281c91e 100644
--- a/tests/data/comments7.py
+++ b/tests/data/comments7.py
@@ -97,7 +97,14 @@ def func():
def func():
- c = call(0.0123, 0.0456, 0.0789, 0.0123, 0.0789, a[-1],) # type: ignore
+ c = call(
+ 0.0123,
+ 0.0456,
+ 0.0789,
+ 0.0123,
+ 0.0789,
+ a[-1], # type: ignore
+ )
# The type: ignore exception only applies to line length, not
# other types of formatting.
diff --git a/tests/data/expression.diff b/tests/data/expression.diff
index f47ee1c6d2c..684f92cd3b7 100644
--- a/tests/data/expression.diff
+++ b/tests/data/expression.diff
@@ -130,15 +130,21 @@
call(**self.screen_kwargs)
call(b, **self.screen_kwargs)
lukasz.langa.pl
-@@ -94,23 +127,25 @@
+@@ -94,26 +127,29 @@
1.0 .real
....__class__
list[str]
dict[str, int]
tuple[str, ...]
++tuple[str, int, float, dict[str, int]]
+ tuple[
+- str, int, float, dict[str, int]
+-]
-tuple[str, int, float, dict[str, int],]
-+tuple[
-+ str, int, float, dict[str, int],
++ str,
++ int,
++ float,
++ dict[str, int],
+]
very_long_variable_name_filters: t.List[
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
@@ -160,7 +166,7 @@
slice[0:1:2]
slice[:]
slice[:-1]
-@@ -134,112 +169,170 @@
+@@ -137,113 +173,180 @@
numpy[-(c + 1) :, d]
numpy[:, l[-2]]
numpy[:, ::-1]
@@ -200,6 +206,7 @@
g = 1, *"ten"
-what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set(vars_to_remove)
-what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set(vars_to_remove)
+-result = session.query(models.Customer.id).filter(models.Customer.account_id == account_id, models.Customer.email == email_address).order_by(models.Customer.id.asc()).all()
-result = session.query(models.Customer.id).filter(models.Customer.account_id == account_id, models.Customer.email == email_address).order_by(models.Customer.id.asc(),).all()
+what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set(
+ vars_to_remove
@@ -212,7 +219,17 @@
+ .filter(
+ models.Customer.account_id == account_id, models.Customer.email == email_address
+ )
-+ .order_by(models.Customer.id.asc(),)
++ .order_by(models.Customer.id.asc())
++ .all()
++)
++result = (
++ session.query(models.Customer.id)
++ .filter(
++ models.Customer.account_id == account_id, models.Customer.email == email_address
++ )
++ .order_by(
++ models.Customer.id.asc(),
++ )
+ .all()
+)
Ø = set()
diff --git a/tests/data/expression.py b/tests/data/expression.py
index 6a04db8b1c4..8e63bdcdf9b 100644
--- a/tests/data/expression.py
+++ b/tests/data/expression.py
@@ -96,6 +96,9 @@
list[str]
dict[str, int]
tuple[str, ...]
+tuple[
+ str, int, float, dict[str, int]
+]
tuple[str, int, float, dict[str, int],]
very_long_variable_name_filters: t.List[
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
@@ -157,6 +160,7 @@
g = 1, *"ten"
what_is_up_with_those_new_coord_names = (coord_names + set(vars_to_create)) + set(vars_to_remove)
what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set(vars_to_remove)
+result = session.query(models.Customer.id).filter(models.Customer.account_id == account_id, models.Customer.email == email_address).order_by(models.Customer.id.asc()).all()
result = session.query(models.Customer.id).filter(models.Customer.account_id == account_id, models.Customer.email == email_address).order_by(models.Customer.id.asc(),).all()
Ø = set()
authors.łukasz.say_thanks()
@@ -379,8 +383,12 @@ async def f():
list[str]
dict[str, int]
tuple[str, ...]
+tuple[str, int, float, dict[str, int]]
tuple[
- str, int, float, dict[str, int],
+ str,
+ int,
+ float,
+ dict[str, int],
]
very_long_variable_name_filters: t.List[
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
@@ -459,7 +467,17 @@ async def f():
.filter(
models.Customer.account_id == account_id, models.Customer.email == email_address
)
- .order_by(models.Customer.id.asc(),)
+ .order_by(models.Customer.id.asc())
+ .all()
+)
+result = (
+ session.query(models.Customer.id)
+ .filter(
+ models.Customer.account_id == account_id, models.Customer.email == email_address
+ )
+ .order_by(
+ models.Customer.id.asc(),
+ )
.all()
)
Ø = set()
diff --git a/tests/data/fmtonoff4.py b/tests/data/fmtonoff4.py
index 54673c06b2d..4ca707965ad 100644
--- a/tests/data/fmtonoff4.py
+++ b/tests/data/fmtonoff4.py
@@ -25,7 +25,12 @@ def f():
@test(
- [1, 2, 3, 4,]
+ [
+ 1,
+ 2,
+ 3,
+ 4,
+ ]
)
def f():
pass
diff --git a/tests/data/function.py b/tests/data/function.py
index 51234a1e9b4..2d642c8731b 100644
--- a/tests/data/function.py
+++ b/tests/data/function.py
@@ -230,7 +230,10 @@ def trailing_comma():
}
-def f(a, **kwargs,) -> A:
+def f(
+ a,
+ **kwargs,
+) -> A:
return (
yield from A(
very_long_argument_name1=very_long_value_for_the_argument,
diff --git a/tests/data/function2.py b/tests/data/function2.py
index a6773d429cd..cfc259ea7bd 100644
--- a/tests/data/function2.py
+++ b/tests/data/function2.py
@@ -25,7 +25,10 @@ def inner():
# output
-def f(a, **kwargs,) -> A:
+def f(
+ a,
+ **kwargs,
+) -> A:
with cache_dir():
if something:
result = CliRunner().invoke(
diff --git a/tests/data/function_trailing_comma.py b/tests/data/function_trailing_comma.py
index fcd81ad7d96..314a56cf67b 100644
--- a/tests/data/function_trailing_comma.py
+++ b/tests/data/function_trailing_comma.py
@@ -1,25 +1,67 @@
def f(a,):
- ...
+ d = {'key': 'value',}
+ tup = (1,)
+
+def f2(a,b,):
+ d = {'key': 'value', 'key2': 'value2',}
+ tup = (1,2,)
def f(a:int=1,):
- ...
+ call(arg={'explode': 'this',})
+ call2(arg=[1,2,3],)
def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
]:
- pass
+ json = {"k": {"k2": {"k3": [1,]}}}
# output
-def f(a,):
- ...
+def f(
+ a,
+):
+ d = {
+ "key": "value",
+ }
+ tup = (1,)
+
+
+def f2(
+ a,
+ b,
+):
+ d = {
+ "key": "value",
+ "key2": "value2",
+ }
+ tup = (
+ 1,
+ 2,
+ )
-def f(a: int = 1,):
- ...
+def f(
+ a: int = 1,
+):
+ call(
+ arg={
+ "explode": "this",
+ }
+ )
+ call2(
+ arg=[1, 2, 3],
+ )
def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
]:
- pass
+ json = {
+ "k": {
+ "k2": {
+ "k3": [
+ 1,
+ ]
+ }
+ }
+ }
\ No newline at end of file
diff --git a/tests/data/function_trailing_comma_wip.py b/tests/data/function_trailing_comma_wip.py
new file mode 100644
index 00000000000..c41fc709d97
--- /dev/null
+++ b/tests/data/function_trailing_comma_wip.py
@@ -0,0 +1,5 @@
+CONFIG_FILES = [CONFIG_FILE] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final
+
+# output
+
+CONFIG_FILES = [CONFIG_FILE] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final
\ No newline at end of file
diff --git a/tests/data/import_spacing.py b/tests/data/import_spacing.py
index 51cfda23ff3..8e6e23cc348 100644
--- a/tests/data/import_spacing.py
+++ b/tests/data/import_spacing.py
@@ -2,6 +2,9 @@
# flake8: noqa
+from logging import (
+ WARNING
+)
from logging import (
ERROR,
)
@@ -53,7 +56,10 @@
# flake8: noqa
-from logging import ERROR
+from logging import WARNING
+from logging import (
+ ERROR,
+)
import sys
# This relies on each of the submodules having an __all__ variable.
diff --git a/tests/data/long_strings.py b/tests/data/long_strings.py
index 5da460b65c0..e1ed90f22de 100644
--- a/tests/data/long_strings.py
+++ b/tests/data/long_strings.py
@@ -137,6 +137,20 @@
), # comment after comma
)
+func_with_bad_parens_that_wont_fit_in_one_line(
+ ("short string that should have parens stripped"),
+ x,
+ y,
+ z
+)
+
+func_with_bad_parens_that_wont_fit_in_one_line(
+ x,
+ y,
+ ("short string that should have parens stripped"),
+ z
+)
+
func_with_bad_parens(
("short string that should have parens stripped"),
x,
@@ -487,12 +501,26 @@ def foo():
" which should NOT be there.", # comment after comma
)
+func_with_bad_parens_that_wont_fit_in_one_line(
+ "short string that should have parens stripped", x, y, z
+)
+
+func_with_bad_parens_that_wont_fit_in_one_line(
+ x, y, "short string that should have parens stripped", z
+)
+
func_with_bad_parens(
- "short string that should have parens stripped", x, y, z,
+ "short string that should have parens stripped",
+ x,
+ y,
+ z,
)
func_with_bad_parens(
- x, y, "short string that should have parens stripped", z,
+ x,
+ y,
+ "short string that should have parens stripped",
+ z,
)
annotated_variable: Final = (
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
index 8dbc58a4315..044bb4a5deb 100644
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -528,17 +528,23 @@ def xxxx_xxx_xx_xxxxxxxxxx_xxxx_xxxxxxxxx(xxxx):
xxxxxxxx = [
xxxxxxxxxxxxxxxx(
"xxxx",
- xxxxxxxxxxx={"xxxx": 1.0,},
+ xxxxxxxxxxx={
+ "xxxx": 1.0,
+ },
xxxxxx={"xxxxxx 1": xxxxxx(xxxx="xxxxxx 1", xxxxxx=600.0)},
xxxxxxxx_xxxxxxx=0.0,
),
xxxxxxxxxxxxxxxx(
"xxxxxxx",
- xxxxxxxxxxx={"xxxx": 1.0,},
+ xxxxxxxxxxx={
+ "xxxx": 1.0,
+ },
xxxxxx={"xxxxxx 1": xxxxxx(xxxx="xxxxxx 1", xxxxxx=200.0)},
xxxxxxxx_xxxxxxx=0.0,
),
- xxxxxxxxxxxxxxxx("xxxx",),
+ xxxxxxxxxxxxxxxx(
+ "xxxx",
+ ),
]
diff --git a/tests/data/long_strings_flag_disabled.py b/tests/data/long_strings_flag_disabled.py
index 1ea864d4bd5..db3954e3abd 100644
--- a/tests/data/long_strings_flag_disabled.py
+++ b/tests/data/long_strings_flag_disabled.py
@@ -225,12 +225,26 @@
), # comment after comma
)
+func_with_bad_parens_that_wont_fit_in_one_line(
+ ("short string that should have parens stripped"), x, y, z
+)
+
+func_with_bad_parens_that_wont_fit_in_one_line(
+ x, y, ("short string that should have parens stripped"), z
+)
+
func_with_bad_parens(
- ("short string that should have parens stripped"), x, y, z,
+ ("short string that should have parens stripped"),
+ x,
+ y,
+ z,
)
func_with_bad_parens(
- x, y, ("short string that should have parens stripped"), z,
+ x,
+ y,
+ ("short string that should have parens stripped"),
+ z,
)
annotated_variable: Final = (
diff --git a/tests/test_black.py b/tests/test_black.py
index 686232a7f9c..7793b0e1131 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -5,13 +5,25 @@
from contextlib import contextmanager
from dataclasses import replace
from functools import partial
+import inspect
from io import BytesIO, TextIOWrapper
import os
from pathlib import Path
import regex as re
import sys
from tempfile import TemporaryDirectory
-from typing import Any, BinaryIO, Dict, Generator, List, Tuple, Iterator, TypeVar
+import types
+from typing import (
+ Any,
+ BinaryIO,
+ Callable,
+ Dict,
+ Generator,
+ List,
+ Tuple,
+ Iterator,
+ TypeVar,
+)
import unittest
from unittest.mock import patch, MagicMock
@@ -153,6 +165,7 @@ def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None
class BlackTestCase(unittest.TestCase):
maxDiff = None
+ _diffThreshold = 2 ** 20
def assertFormatEqual(self, expected: str, actual: str) -> None:
if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
@@ -171,7 +184,7 @@ def assertFormatEqual(self, expected: str, actual: str) -> None:
list(bdv.visit(exp_node))
except Exception as ve:
black.err(str(ve))
- self.assertEqual(expected, actual)
+ self.assertMultiLineEqual(expected, actual)
def invokeBlack(
self, args: List[str], exit_code: int = 0, ignore_config: bool = True
@@ -332,6 +345,16 @@ def test_function2(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_function_trailing_comma_wip(self) -> None:
+ source, expected = read_data("function_trailing_comma_wip")
+ # sys.settrace(tracefunc)
+ actual = fs(source)
+ # sys.settrace(None)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
@patch("black.dump_to_file", dump_to_stderr)
def test_function_trailing_comma(self) -> None:
source, expected = read_data("function_trailing_comma")
@@ -2039,5 +2062,30 @@ async def test_blackd_response_black_version_header(self) -> None:
self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
+with open(black.__file__, "r") as _bf:
+ black_source_lines = _bf.readlines()
+
+
+def tracefunc(frame: types.FrameType, event: str, arg: Any) -> Callable:
+ """Show function calls `from black/__init__.py` as they happen.
+
+ Register this with `sys.settrace()` in a test you're debugging.
+ """
+ if event != "call":
+ return tracefunc
+
+ stack = len(inspect.stack()) - 19
+ filename = frame.f_code.co_filename
+ lineno = frame.f_lineno
+ func_sig_lineno = lineno - 1
+ funcname = black_source_lines[func_sig_lineno].strip()
+ while funcname.startswith("@"):
+ func_sig_lineno += 1
+ funcname = black_source_lines[func_sig_lineno].strip()
+ if "black/__init__.py" in filename:
+ print(f"{' ' * stack}{lineno}:{funcname}")
+ return tracefunc
+
+
if __name__ == "__main__":
unittest.main(module="test_black")
From 05cc7ede6a6e4f07e621fdd7a3541a5d39f6f156 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Thu, 20 Aug 2020 19:04:56 +0200
Subject: [PATCH 026/680] Reformat docs/conf.py according to the new style
---
docs/conf.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/conf.py b/docs/conf.py
index 575a14011e4..9e6a9bcaa62 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -209,7 +209,7 @@ def process_sections(
version = version.split(sp)[0]
custom_sections = [
- DocSection("the_black_code_style", CURRENT_DIR / "the_black_code_style.md",),
+ DocSection("the_black_code_style", CURRENT_DIR / "the_black_code_style.md"),
DocSection("editor_integration", CURRENT_DIR / "editor_integration.md"),
DocSection("blackd", CURRENT_DIR / "blackd.md"),
DocSection("black_primer", CURRENT_DIR / "black_primer.md"),
From bc71685d229baa28fdee8bb1928b82e9f632e0a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Thu, 20 Aug 2020 19:21:40 +0200
Subject: [PATCH 027/680] Open file explicitly with UTF-8 so it works on
Windows, too
---
tests/test_black.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_black.py b/tests/test_black.py
index 7793b0e1131..f89f1403aba 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -2062,7 +2062,7 @@ async def test_blackd_response_black_version_header(self) -> None:
self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
-with open(black.__file__, "r") as _bf:
+with open(black.__file__, "r", encoding="utf-8") as _bf:
black_source_lines = _bf.readlines()
From f3ab907a964e81c35cc6a8530e911c897f5a895e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Fri, 21 Aug 2020 15:27:08 +0200
Subject: [PATCH 028/680] Mark Primer projects that will change formatting
---
src/black_primer/primer.json | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index 7d8271188a2..83a9cb50161 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -3,21 +3,21 @@
"projects": {
"aioexabgp": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/cooperlees/aioexabgp.git",
"long_checkout": false,
"py_versions": ["all"]
},
"attrs": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/python-attrs/attrs.git",
"long_checkout": false,
"py_versions": ["all"]
},
"bandersnatch": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/pypa/bandersnatch.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -40,14 +40,14 @@
},
"flake8-bugbear": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/PyCQA/flake8-bugbear.git",
"long_checkout": false,
"py_versions": ["all"]
},
"hypothesis": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/HypothesisWorks/hypothesis.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -63,7 +63,7 @@
},
"poetry": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/python-poetry/poetry.git",
"long_checkout": false,
"py_versions": ["all"]
From 2fc1dfca96c5fbbb7c2e13df93d1cde1ea74a40b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Fri, 21 Aug 2020 15:42:58 +0200
Subject: [PATCH 029/680] Require Sphinx 3
---
Pipfile | 4 ++--
Pipfile.lock | 22 +++++++++++-----------
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/Pipfile b/Pipfile
index feefb857132..18e8a545617 100644
--- a/Pipfile
+++ b/Pipfile
@@ -4,13 +4,13 @@ url = "https://pypi.python.org/simple"
verify_ssl = true
[dev-packages]
-Sphinx = "*"
+Sphinx = ">=3.1.2"
coverage = "*"
docutils = "==0.15" # not a direct dependency, see https://github.com/pypa/pipenv/issues/3865
flake8 = "*"
flake8-bugbear = "*"
flake8-mypy = "*"
-mypy = ">=0.740"
+mypy = ">=0.782"
pre-commit = "*"
readme_renderer = "*"
recommonmark = "*"
diff --git a/Pipfile.lock b/Pipfile.lock
index b8dfd6b5422..ddbf9b99520 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "4a6956c7c81b496d3fd7a4e3395b332d4dc9a5bed468e36e729a4039c739ad2d"
+ "sha256": "682054eb4a3d4366e2f76b3ae74286d156a270c0d7b57299a81f8cc1d0a51d19"
},
"pipfile-spec": 6,
"requires": {},
@@ -58,11 +58,11 @@
},
"attrs": {
"hashes": [
- "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
- "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
+ "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a",
+ "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==19.3.0"
+ "version": "==20.1.0"
},
"black": {
"editable": true,
@@ -187,9 +187,9 @@
"toml": {
"hashes": [
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
+ "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
],
"index": "pypi",
"version": "==0.10.1"
@@ -305,11 +305,11 @@
},
"attrs": {
"hashes": [
- "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
- "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
+ "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a",
+ "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==19.3.0"
+ "version": "==20.1.0"
},
"babel": {
"hashes": [
@@ -848,9 +848,9 @@
"toml": {
"hashes": [
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
+ "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
],
"index": "pypi",
"version": "==0.10.1"
From cb6f2198b8fc700f8cc0d36c338d613e509250f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Fri, 21 Aug 2020 15:46:00 +0200
Subject: [PATCH 030/680] Use properly renamed function name in docs
---
docs/reference/reference_functions.rst | 2 +-
src/black/__init__.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/reference/reference_functions.rst b/docs/reference/reference_functions.rst
index 1beecc10325..a7184115c94 100644
--- a/docs/reference/reference_functions.rst
+++ b/docs/reference/reference_functions.rst
@@ -171,7 +171,7 @@ Utilities
.. autofunction:: black.re_compile_maybe_verbose
-.. autofunction:: black.should_explode
+.. autofunction:: black.should_split_body_explode
.. autofunction:: black.shutdown
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 8d0c70f06c7..faa88b38af7 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -5559,7 +5559,7 @@ def ensure_visible(leaf: Leaf) -> None:
def should_split_body_explode(line: Line, opening_bracket: Leaf) -> bool:
- """Should `line` immediately be split with `delimiter_split()` after RHS?"""
+ """Should `line` be immediately split with `delimiter_split()` after RHS?"""
if not (opening_bracket.parent and opening_bracket.value in "[{("):
return False
From 205f3b67fbddc7ba0fa330c0493821e404451b82 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Fri, 21 Aug 2020 15:55:03 +0200
Subject: [PATCH 031/680] Fix dealing with generated files in docs
---
docs/authors.md | 183 +++++++++++
docs/change_log.md | 467 ++++++++++++++++++++++++++++
docs/conf.py | 8 +-
docs/contributing_to_black.md | 70 +++++
docs/github_actions.md | 19 ++
docs/ignoring_unmodified_files.md | 23 ++
docs/installation_and_usage.md | 179 +++++++++++
docs/pyproject_toml.md | 88 ++++++
docs/show_your_style.md | 19 ++
docs/version_control_integration.md | 28 ++
10 files changed, 1079 insertions(+), 5 deletions(-)
create mode 100644 docs/authors.md
create mode 100644 docs/change_log.md
create mode 100644 docs/contributing_to_black.md
create mode 100644 docs/github_actions.md
create mode 100644 docs/ignoring_unmodified_files.md
create mode 100644 docs/installation_and_usage.md
create mode 100644 docs/pyproject_toml.md
create mode 100644 docs/show_your_style.md
create mode 100644 docs/version_control_integration.md
diff --git a/docs/authors.md b/docs/authors.md
new file mode 100644
index 00000000000..6a3a8d63f91
--- /dev/null
+++ b/docs/authors.md
@@ -0,0 +1,183 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md"
+
+# Authors
+
+Glued together by [Łukasz Langa](mailto:lukasz@langa.pl).
+
+Maintained with [Carol Willing](mailto:carolcode@willingconsulting.com),
+[Carl Meyer](mailto:carl@oddbird.net),
+[Jelle Zijlstra](mailto:jelle.zijlstra@gmail.com),
+[Mika Naylor](mailto:mail@autophagy.io),
+[Zsolt Dollenstein](mailto:zsol.zsol@gmail.com), and
+[Cooper Lees](mailto:me@cooperlees.com).
+
+Multiple contributions by:
+
+- [Abdur-Rahmaan Janhangeer](mailto:arj.python@gmail.com)
+- [Adam Johnson](mailto:me@adamj.eu)
+- [Adam Williamson](mailto:adamw@happyassassin.net)
+- [Alexander Huynh](mailto:github@grande.coffee)
+- [Alex Vandiver](mailto:github@chmrr.net)
+- [Allan Simon](mailto:allan.simon@supinfo.com)
+- Anders-Petter Ljungquist
+- [Andrew Thorp](mailto:andrew.thorp.dev@gmail.com)
+- [Andrew Zhou](mailto:andrewfzhou@gmail.com)
+- [Andrey](mailto:dyuuus@yandex.ru)
+- [Andy Freeland](mailto:andy@andyfreeland.net)
+- [Anthony Sottile](mailto:asottile@umich.edu)
+- [Arjaan Buijk](mailto:arjaan.buijk@gmail.com)
+- [Arnav Borbornah](mailto:arnavborborah11@gmail.com)
+- [Artem Malyshev](mailto:proofit404@gmail.com)
+- [Asger Hautop Drewsen](mailto:asgerdrewsen@gmail.com)
+- [Augie Fackler](mailto:raf@durin42.com)
+- [Aviskar KC](mailto:aviskarkc10@gmail.com)
+- Batuhan Taşkaya
+- [Benjamin Wohlwend](mailto:bw@piquadrat.ch)
+- [Benjamin Woodruff](mailto:github@benjam.info)
+- [Bharat Raghunathan](mailto:bharatraghunthan9767@gmail.com)
+- [Brandt Bucher](mailto:brandtbucher@gmail.com)
+- [Brett Cannon](mailto:brett@python.org)
+- [Bryan Bugyi](mailto:bryan.bugyi@rutgers.edu)
+- [Bryan Forbes](mailto:bryan@reigndropsfall.net)
+- [Calum Lind](mailto:calumlind@gmail.com)
+- [Charles](mailto:peacech@gmail.com)
+- Charles Reid
+- [Christian Clauss](mailto:cclauss@bluewin.ch)
+- [Christian Heimes](mailto:christian@python.org)
+- [Chuck Wooters](mailto:chuck.wooters@microsoft.com)
+- [Chris Rose](mailto:offline@offby1.net)
+- Codey Oxley
+- [Cong](mailto:congusbongus@gmail.com)
+- [Cooper Ry Lees](mailto:me@cooperlees.com)
+- [Dan Davison](mailto:dandavison7@gmail.com)
+- [Daniel Hahler](mailto:github@thequod.de)
+- [Daniel M. Capella](mailto:polycitizen@gmail.com)
+- Daniele Esposti
+- [David Hotham](mailto:david.hotham@metaswitch.com)
+- [David Lukes](mailto:dafydd.lukes@gmail.com)
+- [David Szotten](mailto:davidszotten@gmail.com)
+- [Denis Laxalde](mailto:denis@laxalde.org)
+- [Douglas Thor](mailto:dthor@transphormusa.com)
+- dylanjblack
+- [Eli Treuherz](mailto:eli@treuherz.com)
+- [Emil Hessman](mailto:emil@hessman.se)
+- [Felix Kohlgrüber](mailto:felix.kohlgrueber@gmail.com)
+- [Florent Thiery](mailto:fthiery@gmail.com)
+- Francisco
+- [Giacomo Tagliabue](mailto:giacomo.tag@gmail.com)
+- [Greg Gandenberger](mailto:ggandenberger@shoprunner.com)
+- [Gregory P. Smith](mailto:greg@krypto.org)
+- Gustavo Camargo
+- hauntsaninja
+- [Heaford](mailto:dan@heaford.com)
+- [Hugo Barrera](mailto::hugo@barrera.io)
+- Hugo van Kemenade
+- [Hynek Schlawack](mailto:hs@ox.cx)
+- [Ivan Katanić](mailto:ivan.katanic@gmail.com)
+- [Jakub Kadlubiec](mailto:jakub.kadlubiec@skyscanner.net)
+- [Jakub Warczarek](mailto:jakub.warczarek@gmail.com)
+- [Jan Hnátek](mailto:jan.hnatek@gmail.com)
+- [Jason Fried](mailto:me@jasonfried.info)
+- [Jason Friedland](mailto:jason@friedland.id.au)
+- [jgirardet](mailto:ijkl@netc.fr)
+- Jim Brännlund
+- [Jimmy Jia](mailto:tesrin@gmail.com)
+- [Joe Antonakakis](mailto:jma353@cornell.edu)
+- [Jon Dufresne](mailto:jon.dufresne@gmail.com)
+- [Jonas Obrist](mailto:ojiidotch@gmail.com)
+- [Jonty Wareing](mailto:jonty@jonty.co.uk)
+- [Jose Nazario](mailto:jose.monkey.org@gmail.com)
+- [Joseph Larson](mailto:larson.joseph@gmail.com)
+- [Josh Bode](mailto:joshbode@fastmail.com)
+- [Josh Holland](mailto:anowlcalledjosh@gmail.com)
+- [José Padilla](mailto:jpadilla@webapplicate.com)
+- [Juan Luis Cano Rodríguez](mailto:hello@juanlu.space)
+- [kaiix](mailto:kvn.hou@gmail.com)
+- [Katie McLaughlin](mailto:katie@glasnt.com)
+- Katrin Leinweber
+- [Keith Smiley](mailto:keithbsmiley@gmail.com)
+- [Kenyon Ralph](mailto:kenyon@kenyonralph.com)
+- [Kevin Kirsche](mailto:Kev.Kirsche+GitHub@gmail.com)
+- [Kyle Hausmann](mailto:kyle.hausmann@gmail.com)
+- [Kyle Sunden](mailto:sunden@wisc.edu)
+- Lawrence Chan
+- [Linus Groh](mailto:mail@linusgroh.de)
+- [Loren Carvalho](mailto:comradeloren@gmail.com)
+- [Luka Sterbic](mailto:luka.sterbic@gmail.com)
+- [LukasDrude](mailto:mail@lukas-drude.de)
+- Mahmoud Hossam
+- Mariatta
+- [Matt VanEseltine](mailto:vaneseltine@gmail.com)
+- [Matthew Clapp](mailto:itsayellow+dev@gmail.com)
+- [Matthew Walster](mailto:matthew@walster.org)
+- Max Smolens
+- [Michael Aquilina](mailto:michaelaquilina@gmail.com)
+- [Michael Flaxman](mailto:michael.flaxman@gmail.com)
+- [Michael J. Sullivan](mailto:sully@msully.net)
+- [Michael McClimon](mailto:michael@mcclimon.org)
+- [Miguel Gaiowski](mailto:miggaiowski@gmail.com)
+- [Mike](mailto:roshi@fedoraproject.org)
+- [mikehoyio](mailto:mikehoy@gmail.com)
+- [Min ho Kim](mailto:minho42@gmail.com)
+- [Miroslav Shubernetskiy](mailto:miroslav@miki725.com)
+- MomIsBestFriend
+- [Nathan Goldbaum](mailto:ngoldbau@illinois.edu)
+- [Nathan Hunt](mailto:neighthan.hunt@gmail.com)
+- [Neraste](mailto:neraste.herr10@gmail.com)
+- [Nikolaus Waxweiler](mailto:madigens@gmail.com)
+- [Ofek Lev](mailto:ofekmeister@gmail.com)
+- [Osaetin Daniel](mailto:osaetindaniel@gmail.com)
+- [otstrel](mailto:otstrel@gmail.com)
+- [Pablo Galindo](mailto:Pablogsal@gmail.com)
+- [Paul Ganssle](mailto:p.ganssle@gmail.com)
+- [Paul Meinhardt](mailto:mnhrdt@gmail.com)
+- [Peter Bengtsson](mailto:mail@peterbe.com)
+- [Peter Stensmyr](mailto:peter.stensmyr@gmail.com)
+- pmacosta
+- [Quentin Pradet](mailto:quentin@pradet.me)
+- [Ralf Schmitt](mailto:ralf@systemexit.de)
+- [Ramón Valles](mailto:mroutis@protonmail.com)
+- [Richard Fearn](mailto:richardfearn@gmail.com)
+- Richard Si
+- [Rishikesh Jha](mailto:rishijha424@gmail.com)
+- [Rupert Bedford](mailto:rupert@rupertb.com)
+- Russell Davis
+- [Rémi Verschelde](mailto:rverschelde@gmail.com)
+- [Sami Salonen](mailto:sakki@iki.fi)
+- [Samuel Cormier-Iijima](mailto:samuel@cormier-iijima.com)
+- [Sanket Dasgupta](mailto:sanketdasgupta@gmail.com)
+- Sergi
+- [Scott Stevenson](mailto:scott@stevenson.io)
+- Shantanu
+- [shaoran](mailto:shaoran@sakuranohana.org)
+- [Shinya Fujino](mailto:shf0811@gmail.com)
+- springstan
+- [Stavros Korokithakis](mailto:hi@stavros.io)
+- [Stephen Rosen](mailto:sirosen@globus.org)
+- [Steven M. Vascellaro](mailto:S.Vascellaro@gmail.com)
+- [Sunil Kapil](mailto:snlkapil@gmail.com)
+- [Sébastien Eustace](mailto:sebastien.eustace@gmail.com)
+- [Tal Amuyal](mailto:TalAmuyal@gmail.com)
+- [Terrance](mailto:git@terrance.allofti.me)
+- [Thom Lu](mailto:thomas.c.lu@gmail.com)
+- [Thomas Grainger](mailto:tagrain@gmail.com)
+- [Tim Gates](mailto:tim.gates@iress.com)
+- [Tim Swast](mailto:swast@google.com)
+- [Timo](mailto:timo_tk@hotmail.com)
+- Toby Fleming
+- [Tom Christie](mailto:tom@tomchristie.com)
+- [Tony Narlock](mailto:tony@git-pull.com)
+- [Tsuyoshi Hombashi](mailto:tsuyoshi.hombashi@gmail.com)
+- [Tushar Chandra](mailto:tusharchandra2018@u.northwestern.edu)
+- [Tzu-ping Chung](mailto:uranusjr@gmail.com)
+- [Utsav Shah](mailto:ukshah2@illinois.edu)
+- utsav-dbx
+- vezeli
+- [Ville Skyttä](mailto:ville.skytta@iki.fi)
+- [Vishwas B Sharma](mailto:sharma.vishwas88@gmail.com)
+- [Vlad Emelianov](mailto:volshebnyi@gmail.com)
+- [williamfzc](mailto:178894043@qq.com)
+- [wouter bolsterlee](mailto:wouter@bolsterl.ee)
+- Yazdan
+- [Yngve Høiseth](mailto:yngve@hoiseth.net)
+- [Yurii Karabas](mailto:1998uriyyo@gmail.com)
diff --git a/docs/change_log.md b/docs/change_log.md
new file mode 100644
index 00000000000..3cc8c40d8c6
--- /dev/null
+++ b/docs/change_log.md
@@ -0,0 +1,467 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM CHANGES.md"
+
+## Change Log
+
+### Unreleased
+
+#### _Black_
+
+- re-implemented support for explicit trailing commas: now it works consistently within
+ any bracket pair, including nested structures (#1288 and duplicates)
+
+- reindent docstrings when reindenting code around it (#1053)
+
+- show colored diffs (#1266)
+
+- move to 'py3' tagged wheels (#1388)
+
+- remove deprecated `--py36` option (#1236)
+
+- add `--force-exclude` argument (#1032)
+
+#### Vim plugin
+
+- prefer virtualenv packages over global packages (#1383)
+
+### 19.10b0
+
+- added support for PEP 572 assignment expressions (#711)
+
+- added support for PEP 570 positional-only arguments (#943)
+
+- added support for async generators (#593)
+
+- added support for pre-splitting collections by putting an explicit trailing comma
+ inside (#826)
+
+- added `black -c` as a way to format code passed from the command line (#761)
+
+- --safe now works with Python 2 code (#840)
+
+- fixed grammar selection for Python 2-specific code (#765)
+
+- fixed feature detection for trailing commas in function definitions and call sites
+ (#763)
+
+- `# fmt: off`/`# fmt: on` comment pairs placed multiple times within the same block of
+ code now behave correctly (#1005)
+
+- _Black_ no longer crashes on Windows machines with more than 61 cores (#838)
+
+- _Black_ no longer crashes on standalone comments prepended with a backslash (#767)
+
+- _Black_ no longer crashes on `from` ... `import` blocks with comments (#829)
+
+- _Black_ no longer crashes on Python 3.7 on some platform configurations (#494)
+
+- _Black_ no longer fails on comments in from-imports (#671)
+
+- _Black_ no longer fails when the file starts with a backslash (#922)
+
+- _Black_ no longer merges regular comments with type comments (#1027)
+
+- _Black_ no longer splits long lines that contain type comments (#997)
+
+- removed unnecessary parentheses around `yield` expressions (#834)
+
+- added parentheses around long tuples in unpacking assignments (#832)
+
+- added parentheses around complex powers when they are prefixed by a unary operator
+ (#646)
+
+- fixed bug that led _Black_ format some code with a line length target of 1 (#762)
+
+- _Black_ no longer introduces quotes in f-string subexpressions on string boundaries
+ (#863)
+
+- if _Black_ puts parenthesis around a single expression, it moves comments to the
+ wrapped expression instead of after the brackets (#872)
+
+- `blackd` now returns the version of _Black_ in the response headers (#1013)
+
+- `blackd` can now output the diff of formats on source code when the `X-Diff` header is
+ provided (#969)
+
+### 19.3b0
+
+- new option `--target-version` to control which Python versions _Black_-formatted code
+ should target (#618)
+
+- deprecated `--py36` (use `--target-version=py36` instead) (#724)
+
+- _Black_ no longer normalizes numeric literals to include `_` separators (#696)
+
+- long `del` statements are now split into multiple lines (#698)
+
+- type comments are no longer mangled in function signatures
+
+- improved performance of formatting deeply nested data structures (#509)
+
+- _Black_ now properly formats multiple files in parallel on Windows (#632)
+
+- _Black_ now creates cache files atomically which allows it to be used in parallel
+ pipelines (like `xargs -P8`) (#673)
+
+- _Black_ now correctly indents comments in files that were previously formatted with
+ tabs (#262)
+
+- `blackd` now supports CORS (#622)
+
+### 18.9b0
+
+- numeric literals are now formatted by _Black_ (#452, #461, #464, #469):
+
+ - numeric literals are normalized to include `_` separators on Python 3.6+ code
+
+ - added `--skip-numeric-underscore-normalization` to disable the above behavior and
+ leave numeric underscores as they were in the input
+
+ - code with `_` in numeric literals is recognized as Python 3.6+
+
+ - most letters in numeric literals are lowercased (e.g., in `1e10`, `0x01`)
+
+ - hexadecimal digits are always uppercased (e.g. `0xBADC0DE`)
+
+- added `blackd`, see [its documentation](#blackd) for more info (#349)
+
+- adjacent string literals are now correctly split into multiple lines (#463)
+
+- trailing comma is now added to single imports that don't fit on a line (#250)
+
+- cache is now populated when `--check` is successful for a file which speeds up
+ consecutive checks of properly formatted unmodified files (#448)
+
+- whitespace at the beginning of the file is now removed (#399)
+
+- fixed mangling [pweave](http://mpastell.com/pweave/) and
+ [Spyder IDE](https://www.spyder-ide.org/) special comments (#532)
+
+- fixed unstable formatting when unpacking big tuples (#267)
+
+- fixed parsing of `__future__` imports with renames (#389)
+
+- fixed scope of `# fmt: off` when directly preceding `yield` and other nodes (#385)
+
+- fixed formatting of lambda expressions with default arguments (#468)
+
+- fixed `async for` statements: _Black_ no longer breaks them into separate lines (#372)
+
+- note: the Vim plugin stopped registering `,=` as a default chord as it turned out to
+ be a bad idea (#415)
+
+### 18.6b4
+
+- hotfix: don't freeze when multiple comments directly precede `# fmt: off` (#371)
+
+### 18.6b3
+
+- typing stub files (`.pyi`) now have blank lines added after constants (#340)
+
+- `# fmt: off` and `# fmt: on` are now much more dependable:
+
+ - they now work also within bracket pairs (#329)
+
+ - they now correctly work across function/class boundaries (#335)
+
+ - they now work when an indentation block starts with empty lines or misaligned
+ comments (#334)
+
+- made Click not fail on invalid environments; note that Click is right but the
+ likelihood we'll need to access non-ASCII file paths when dealing with Python source
+ code is low (#277)
+
+- fixed improper formatting of f-strings with quotes inside interpolated expressions
+ (#322)
+
+- fixed unnecessary slowdown when long list literals where found in a file
+
+- fixed unnecessary slowdown on AST nodes with very many siblings
+
+- fixed cannibalizing backslashes during string normalization
+
+- fixed a crash due to symbolic links pointing outside of the project directory (#338)
+
+### 18.6b2
+
+- added `--config` (#65)
+
+- added `-h` equivalent to `--help` (#316)
+
+- fixed improper unmodified file caching when `-S` was used
+
+- fixed extra space in string unpacking (#305)
+
+- fixed formatting of empty triple quoted strings (#313)
+
+- fixed unnecessary slowdown in comment placement calculation on lines without comments
+
+### 18.6b1
+
+- hotfix: don't output human-facing information on stdout (#299)
+
+- hotfix: don't output cake emoji on non-zero return code (#300)
+
+### 18.6b0
+
+- added `--include` and `--exclude` (#270)
+
+- added `--skip-string-normalization` (#118)
+
+- added `--verbose` (#283)
+
+- the header output in `--diff` now actually conforms to the unified diff spec
+
+- fixed long trivial assignments being wrapped in unnecessary parentheses (#273)
+
+- fixed unnecessary parentheses when a line contained multiline strings (#232)
+
+- fixed stdin handling not working correctly if an old version of Click was used (#276)
+
+- _Black_ now preserves line endings when formatting a file in place (#258)
+
+### 18.5b1
+
+- added `--pyi` (#249)
+
+- added `--py36` (#249)
+
+- Python grammar pickle caches are stored with the formatting caches, making _Black_
+ work in environments where site-packages is not user-writable (#192)
+
+- _Black_ now enforces a PEP 257 empty line after a class-level docstring (and/or
+ fields) and the first method
+
+- fixed invalid code produced when standalone comments were present in a trailer that
+ was omitted from line splitting on a large expression (#237)
+
+- fixed optional parentheses being removed within `# fmt: off` sections (#224)
+
+- fixed invalid code produced when stars in very long imports were incorrectly wrapped
+ in optional parentheses (#234)
+
+- fixed unstable formatting when inline comments were moved around in a trailer that was
+ omitted from line splitting on a large expression (#238)
+
+- fixed extra empty line between a class declaration and the first method if no class
+ docstring or fields are present (#219)
+
+- fixed extra empty line between a function signature and an inner function or inner
+ class (#196)
+
+### 18.5b0
+
+- call chains are now formatted according to the
+ [fluent interfaces](https://en.wikipedia.org/wiki/Fluent_interface) style (#67)
+
+- data structure literals (tuples, lists, dictionaries, and sets) are now also always
+ exploded like imports when they don't fit in a single line (#152)
+
+- slices are now formatted according to PEP 8 (#178)
+
+- parentheses are now also managed automatically on the right-hand side of assignments
+ and return statements (#140)
+
+- math operators now use their respective priorities for delimiting multiline
+ expressions (#148)
+
+- optional parentheses are now omitted on expressions that start or end with a bracket
+ and only contain a single operator (#177)
+
+- empty parentheses in a class definition are now removed (#145, #180)
+
+- string prefixes are now standardized to lowercase and `u` is removed on Python 3.6+
+ only code and Python 2.7+ code with the `unicode_literals` future import (#188, #198,
+ #199)
+
+- typing stub files (`.pyi`) are now formatted in a style that is consistent with PEP
+ 484 (#207, #210)
+
+- progress when reformatting many files is now reported incrementally
+
+- fixed trailers (content with brackets) being unnecessarily exploded into their own
+ lines after a dedented closing bracket (#119)
+
+- fixed an invalid trailing comma sometimes left in imports (#185)
+
+- fixed non-deterministic formatting when multiple pairs of removable parentheses were
+ used (#183)
+
+- fixed multiline strings being unnecessarily wrapped in optional parentheses in long
+ assignments (#215)
+
+- fixed not splitting long from-imports with only a single name
+
+- fixed Python 3.6+ file discovery by also looking at function calls with unpacking.
+ This fixed non-deterministic formatting if trailing commas where used both in function
+ signatures with stars and function calls with stars but the former would be
+ reformatted to a single line.
+
+- fixed crash on dealing with optional parentheses (#193)
+
+- fixed "is", "is not", "in", and "not in" not considered operators for splitting
+ purposes
+
+- fixed crash when dead symlinks where encountered
+
+### 18.4a4
+
+- don't populate the cache on `--check` (#175)
+
+### 18.4a3
+
+- added a "cache"; files already reformatted that haven't changed on disk won't be
+ reformatted again (#109)
+
+- `--check` and `--diff` are no longer mutually exclusive (#149)
+
+- generalized star expression handling, including double stars; this fixes
+ multiplication making expressions "unsafe" for trailing commas (#132)
+
+- _Black_ no longer enforces putting empty lines behind control flow statements (#90)
+
+- _Black_ now splits imports like "Mode 3 + trailing comma" of isort (#127)
+
+- fixed comment indentation when a standalone comment closes a block (#16, #32)
+
+- fixed standalone comments receiving extra empty lines if immediately preceding a
+ class, def, or decorator (#56, #154)
+
+- fixed `--diff` not showing entire path (#130)
+
+- fixed parsing of complex expressions after star and double stars in function calls
+ (#2)
+
+- fixed invalid splitting on comma in lambda arguments (#133)
+
+- fixed missing splits of ternary expressions (#141)
+
+### 18.4a2
+
+- fixed parsing of unaligned standalone comments (#99, #112)
+
+- fixed placement of dictionary unpacking inside dictionary literals (#111)
+
+- Vim plugin now works on Windows, too
+
+- fixed unstable formatting when encountering unnecessarily escaped quotes in a string
+ (#120)
+
+### 18.4a1
+
+- added `--quiet` (#78)
+
+- added automatic parentheses management (#4)
+
+- added [pre-commit](https://pre-commit.com) integration (#103, #104)
+
+- fixed reporting on `--check` with multiple files (#101, #102)
+
+- fixed removing backslash escapes from raw strings (#100, #105)
+
+### 18.4a0
+
+- added `--diff` (#87)
+
+- add line breaks before all delimiters, except in cases like commas, to better comply
+ with PEP 8 (#73)
+
+- standardize string literals to use double quotes (almost) everywhere (#75)
+
+- fixed handling of standalone comments within nested bracketed expressions; _Black_
+ will no longer produce super long lines or put all standalone comments at the end of
+ the expression (#22)
+
+- fixed 18.3a4 regression: don't crash and burn on empty lines with trailing whitespace
+ (#80)
+
+- fixed 18.3a4 regression: `# yapf: disable` usage as trailing comment would cause
+ _Black_ to not emit the rest of the file (#95)
+
+- when CTRL+C is pressed while formatting many files, _Black_ no longer freaks out with
+ a flurry of asyncio-related exceptions
+
+- only allow up to two empty lines on module level and only single empty lines within
+ functions (#74)
+
+### 18.3a4
+
+- `# fmt: off` and `# fmt: on` are implemented (#5)
+
+- automatic detection of deprecated Python 2 forms of print statements and exec
+ statements in the formatted file (#49)
+
+- use proper spaces for complex expressions in default values of typed function
+ arguments (#60)
+
+- only return exit code 1 when --check is used (#50)
+
+- don't remove single trailing commas from square bracket indexing (#59)
+
+- don't omit whitespace if the previous factor leaf wasn't a math operator (#55)
+
+- omit extra space in kwarg unpacking if it's the first argument (#46)
+
+- omit extra space in
+ [Sphinx auto-attribute comments](http://www.sphinx-doc.org/en/stable/ext/autodoc.html#directive-autoattribute)
+ (#68)
+
+### 18.3a3
+
+- don't remove single empty lines outside of bracketed expressions (#19)
+
+- added ability to pipe formatting from stdin to stdin (#25)
+
+- restored ability to format code with legacy usage of `async` as a name (#20, #42)
+
+- even better handling of numpy-style array indexing (#33, again)
+
+### 18.3a2
+
+- changed positioning of binary operators to occur at beginning of lines instead of at
+ the end, following
+ [a recent change to PEP 8](https://github.com/python/peps/commit/c59c4376ad233a62ca4b3a6060c81368bd21e85b)
+ (#21)
+
+- ignore empty bracket pairs while splitting. This avoids very weirdly looking
+ formattings (#34, #35)
+
+- remove a trailing comma if there is a single argument to a call
+
+- if top level functions were separated by a comment, don't put four empty lines after
+ the upper function
+
+- fixed unstable formatting of newlines with imports
+
+- fixed unintentional folding of post scriptum standalone comments into last statement
+ if it was a simple statement (#18, #28)
+
+- fixed missing space in numpy-style array indexing (#33)
+
+- fixed spurious space after star-based unary expressions (#31)
+
+### 18.3a1
+
+- added `--check`
+
+- only put trailing commas in function signatures and calls if it's safe to do so. If
+ the file is Python 3.6+ it's always safe, otherwise only safe if there are no `*args`
+ or `**kwargs` used in the signature or call. (#8)
+
+- fixed invalid spacing of dots in relative imports (#6, #13)
+
+- fixed invalid splitting after comma on unpacked variables in for-loops (#23)
+
+- fixed spurious space in parenthesized set expressions (#7)
+
+- fixed spurious space after opening parentheses and in default arguments (#14, #17)
+
+- fixed spurious space after unary operators when the operand was a complex expression
+ (#15)
+
+### 18.3a0
+
+- first published version, Happy 🍰 Day 2018!
+
+- alpha quality
+
+- date-versioned (see: https://calver.org/)
diff --git a/docs/conf.py b/docs/conf.py
index 9e6a9bcaa62..8dd77b41d61 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -184,11 +184,9 @@ def process_sections(
contents = fix_headers(contents)
with open(target_path, "w", encoding="utf-8") as f:
- if section.src.suffix == ".md":
- f.write(
- "[//]: # (NOTE: THIS FILE WAS AUTOGENERATED FROM"
- f" {section.src})\n\n"
- )
+ if section.src.suffix == ".md" and section.src != target_path:
+ rel = section.src.resolve().relative_to(CURRENT_DIR.parent)
+ f.write(f'[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM {rel}"\n\n')
f.write(contents)
processed_sections.add(section.name)
modified_files.add(target_path)
diff --git a/docs/contributing_to_black.md b/docs/contributing_to_black.md
new file mode 100644
index 00000000000..e5307adb5d0
--- /dev/null
+++ b/docs/contributing_to_black.md
@@ -0,0 +1,70 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM CONTRIBUTING.md"
+
+# Contributing to _Black_
+
+Welcome! Happy to see you willing to make the project better. Have you read the entire
+[user documentation](https://black.readthedocs.io/en/latest/) yet?
+
+## Bird's eye view
+
+In terms of inspiration, _Black_ is about as configurable as _gofmt_. This is
+deliberate.
+
+Bug reports and fixes are always welcome! Please follow the
+[issue template on GitHub](https://github.com/psf/black/issues/new) for best results.
+
+Before you suggest a new feature or configuration knob, ask yourself why you want it. If
+it enables better integration with some workflow, fixes an inconsistency, speeds things
+up, and so on - go for it! On the other hand, if your answer is "because I don't like a
+particular formatting" then you're not ready to embrace _Black_ yet. Such changes are
+unlikely to get accepted. You can still try but prepare to be disappointed.
+
+## Technicalities
+
+Development on the latest version of Python is preferred. As of this writing it's 3.8.
+You can use any operating system. I am using macOS myself and CentOS at work.
+
+Install all development dependencies using:
+
+```console
+$ pipenv install --dev
+$ pipenv shell
+$ pre-commit install
+```
+
+If you haven't used `pipenv` before but are comfortable with virtualenvs, just run
+`pip install pipenv` in the virtualenv you're already using and invoke the command above
+from the cloned _Black_ repo. It will do the correct thing.
+
+Before submitting pull requests, run lints and tests with:
+
+```console
+$ pre-commit run -a
+$ python -m unittest
+$ black-primer [-k -w /tmp/black_test_repos]
+```
+
+## black-primer
+
+`black-primer` is used by CI to pull down well-known _Black_ formatted projects and see
+if we get source code changes. It will error on formatting changes or errors. Please run
+before pushing your PR to see if you get the actions you would expect from _Black_ with
+your PR. You may need to change
+[primer.json](https://github.com/psf/black/blob/master/src/black_primer/primer.json)
+configuration for it to pass.
+
+For more `black-primer` information visit the
+[documentation](https://github.com/psf/black/blob/master/docs/black_primer.md).
+
+## Hygiene
+
+If you're fixing a bug, add a test. Run it first to confirm it fails, then fix the bug,
+run it again to confirm it's really fixed.
+
+If adding a new feature, add a test. In fact, always add a test. But wait, before adding
+any large feature, first open an issue for us to discuss the idea first.
+
+## Finally
+
+Thanks again for your interest in improving the project! You're taking action when most
+people decide to sit and watch.
diff --git a/docs/github_actions.md b/docs/github_actions.md
new file mode 100644
index 00000000000..7ff87540242
--- /dev/null
+++ b/docs/github_actions.md
@@ -0,0 +1,19 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md"
+
+# GitHub Actions
+
+Create a file named `.github/workflows/black.yml` inside your repository with:
+
+```yaml
+name: Lint
+
+on: [push, pull_request]
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-python@v2
+ - uses: psf/black@stable
+```
diff --git a/docs/ignoring_unmodified_files.md b/docs/ignoring_unmodified_files.md
new file mode 100644
index 00000000000..a915f4e8678
--- /dev/null
+++ b/docs/ignoring_unmodified_files.md
@@ -0,0 +1,23 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md"
+
+# Ignoring unmodified files
+
+_Black_ remembers files it has already formatted, unless the `--diff` flag is used or
+code is passed via standard input. This information is stored per-user. The exact
+location of the file depends on the _Black_ version and the system on which _Black_ is
+run. The file is non-portable. The standard location on common operating systems is:
+
+- Windows:
+ `C:\\Users\\AppData\Local\black\black\Cache\\cache...pickle`
+- macOS:
+ `/Users//Library/Caches/black//cache...pickle`
+- Linux:
+ `/home//.cache/black//cache...pickle`
+
+`file-mode` is an int flag that determines whether the file was formatted as 3.6+ only,
+as .pyi, and whether string normalization was omitted.
+
+To override the location of these files on macOS or Linux, set the environment variable
+`XDG_CACHE_HOME` to your preferred location. For example, if you want to put the cache
+in the directory you're running _Black_ from, set `XDG_CACHE_HOME=.cache`. _Black_ will
+then write the above files to `.cache/black//`.
diff --git a/docs/installation_and_usage.md b/docs/installation_and_usage.md
new file mode 100644
index 00000000000..cc0269198a2
--- /dev/null
+++ b/docs/installation_and_usage.md
@@ -0,0 +1,179 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md"
+
+# Installation and usage
+
+## Installation
+
+_Black_ can be installed by running `pip install black`. It requires Python 3.6.0+ to
+run but you can reformat Python 2 code with it, too.
+
+### Install from GitHub
+
+If you can't wait for the latest _hotness_ and want to install from GitHub, use:
+
+`pip install git+git://github.com/psf/black`
+
+## Usage
+
+To get started right away with sensible defaults:
+
+```sh
+black {source_file_or_directory}
+```
+
+You can run _Black_ as a package if running it as a script doesn't work:
+
+```sh
+python -m black {source_file_or_directory}
+```
+
+## Command line options
+
+_Black_ doesn't provide many options. You can list them by running `black --help`:
+
+```text
+Usage: black [OPTIONS] [SRC]...
+
+ The uncompromising code formatter.
+
+Options:
+ -c, --code TEXT Format the code passed in as a string.
+ -l, --line-length INTEGER How many characters per line to allow.
+ [default: 88]
+
+ -t, --target-version [py27|py33|py34|py35|py36|py37|py38]
+ Python versions that should be supported by
+ Black's output. [default: per-file auto-
+ detection]
+
+ --pyi Format all input files like typing stubs
+ regardless of file extension (useful when
+ piping source on standard input).
+
+ -S, --skip-string-normalization
+ Don't normalize string quotes or prefixes.
+ --check Don't write the files back, just return the
+ status. Return code 0 means nothing would
+ change. Return code 1 means some files
+ would be reformatted. Return code 123 means
+ there was an internal error.
+
+ --diff Don't write the files back, just output a
+ diff for each file on stdout.
+
+ --color / --no-color Show colored diff. Only applies when
+ `--diff` is given.
+
+ --fast / --safe If --fast given, skip temporary sanity
+ checks. [default: --safe]
+
+ --include TEXT A regular expression that matches files and
+ directories that should be included on
+ recursive searches. An empty value means
+ all files are included regardless of the
+ name. Use forward slashes for directories
+ on all platforms (Windows, too). Exclusions
+ are calculated first, inclusions later.
+ [default: \.pyi?$]
+
+ --exclude TEXT A regular expression that matches files and
+ directories that should be excluded on
+ recursive searches. An empty value means no
+ paths are excluded. Use forward slashes for
+ directories on all platforms (Windows, too).
+ Exclusions are calculated first, inclusions
+ later. [default: /(\.eggs|\.git|\.hg|\.mypy
+ _cache|\.nox|\.tox|\.venv|\.svn|_build|buck-
+ out|build|dist)/]
+
+ --force-exclude TEXT Like --exclude, but files and directories
+ matching this regex will be excluded even
+ when they are passed explicitly as arguments
+
+ -q, --quiet Don't emit non-error messages to stderr.
+ Errors are still emitted; silence those with
+ 2>/dev/null.
+
+ -v, --verbose Also emit messages to stderr about files
+ that were not changed or were ignored due to
+ --exclude=.
+
+ --version Show the version and exit.
+ --config FILE Read configuration from FILE path.
+ -h, --help Show this message and exit.
+```
+
+_Black_ is a well-behaved Unix-style command-line tool:
+
+- it does nothing if no sources are passed to it;
+- it will read from standard input and write to standard output if `-` is used as the
+ filename;
+- it only outputs messages to users on standard error;
+- exits with code 0 unless an internal error occurred (or `--check` was used).
+
+## Using _Black_ with other tools
+
+While _Black_ enforces formatting that conforms to PEP 8, other tools may raise warnings
+about _Black_'s changes or will overwrite _Black_'s changes. A good example of this is
+[isort](https://pypi.org/p/isort). Since _Black_ is barely configurable, these tools
+should be configured to neither warn about nor overwrite _Black_'s changes.
+
+Actual details on _Black_ compatible configurations for various tools can be found in
+[compatible_configs](https://github.com/psf/black/blob/master/docs/compatible_configs.md).
+
+## Migrating your code style without ruining git blame
+
+A long-standing argument against moving to automated code formatters like _Black_ is
+that the migration will clutter up the output of `git blame`. This was a valid argument,
+but since Git version 2.23, Git natively supports
+[ignoring revisions in blame](https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revltrevgt)
+with the `--ignore-rev` option. You can also pass a file listing the revisions to ignore
+using the `--ignore-revs-file` option. The changes made by the revision will be ignored
+when assigning blame. Lines modified by an ignored revision will be blamed on the
+previous revision that modified those lines.
+
+So when migrating your project's code style to _Black_, reformat everything and commit
+the changes (preferably in one massive commit). Then put the full 40 characters commit
+identifier(s) into a file.
+
+```
+# Migrate code style to Black
+5b4ab991dede475d393e9d69ec388fd6bd949699
+```
+
+Afterwards, you can pass that file to `git blame` and see clean and meaningful blame
+information.
+
+```console
+$ git blame important.py --ignore-revs-file .git-blame-ignore-revs
+7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 1) def very_important_function(text, file):
+abdfd8b0 (Alice Doe 2019-09-23 11:39:32 -0400 2) text = text.lstrip()
+7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 3) with open(file, "r+") as f:
+7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 4) f.write(formatted)
+```
+
+You can even configure `git` to automatically ignore revisions listed in a file on every
+call to `git blame`.
+
+```console
+$ git config blame.ignoreRevsFile .git-blame-ignore-revs
+```
+
+**The one caveat is that GitHub and GitLab do not yet support ignoring revisions using
+their native UI of blame.** So blame information will be cluttered with a reformatting
+commit on those platforms. (If you'd like this feature, there's an open issue for
+[GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/31423) and please let GitHub
+know!)
+
+## NOTE: This is a beta product
+
+_Black_ is already [successfully used](#used-by) by many projects, small and big. It
+also sports a decent test suite. However, it is still very new. Things will probably be
+wonky for a while. This is made explicit by the "Beta" trove classifier, as well as by
+the "b" in the version number. What this means for you is that **until the formatter
+becomes stable, you should expect some formatting to change in the future**. That being
+said, no drastic stylistic changes are planned, mostly responses to bug reports.
+
+Also, as a temporary safety measure, _Black_ will check that the reformatted code still
+produces a valid AST that is equivalent to the original. This slows it down. If you're
+feeling confident, use `--fast`.
diff --git a/docs/pyproject_toml.md b/docs/pyproject_toml.md
new file mode 100644
index 00000000000..cd313452b1e
--- /dev/null
+++ b/docs/pyproject_toml.md
@@ -0,0 +1,88 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md"
+
+# pyproject.toml
+
+_Black_ is able to read project-specific default values for its command line options
+from a `pyproject.toml` file. This is especially useful for specifying custom
+`--include` and `--exclude` patterns for your project.
+
+**Pro-tip**: If you're asking yourself "Do I need to configure anything?" the answer is
+"No". _Black_ is all about sensible defaults.
+
+## What on Earth is a `pyproject.toml` file?
+
+[PEP 518](https://www.python.org/dev/peps/pep-0518/) defines `pyproject.toml` as a
+configuration file to store build system requirements for Python projects. With the help
+of tools like [Poetry](https://python-poetry.org/) or
+[Flit](https://flit.readthedocs.io/en/latest/) it can fully replace the need for
+`setup.py` and `setup.cfg` files.
+
+## Where _Black_ looks for the file
+
+By default _Black_ looks for `pyproject.toml` starting from the common base directory of
+all files and directories passed on the command line. If it's not there, it looks in
+parent directories. It stops looking when it finds the file, or a `.git` directory, or a
+`.hg` directory, or the root of the file system, whichever comes first.
+
+If you're formatting standard input, _Black_ will look for configuration starting from
+the current working directory.
+
+You can also explicitly specify the path to a particular file that you want with
+`--config`. In this situation _Black_ will not look for any other file.
+
+If you're running with `--verbose`, you will see a blue message if a file was found and
+used.
+
+Please note `blackd` will not use `pyproject.toml` configuration.
+
+## Configuration format
+
+As the file extension suggests, `pyproject.toml` is a
+[TOML](https://github.com/toml-lang/toml) file. It contains separate sections for
+different tools. _Black_ is using the `[tool.black]` section. The option keys are the
+same as long names of options on the command line.
+
+Note that you have to use single-quoted strings in TOML for regular expressions. It's
+the equivalent of r-strings in Python. Multiline strings are treated as verbose regular
+expressions by Black. Use `[ ]` to denote a significant space character.
+
+
+Example pyproject.toml
+
+```toml
+[tool.black]
+line-length = 88
+target-version = ['py37']
+include = '\.pyi?$'
+exclude = '''
+
+(
+ /(
+ \.eggs # exclude a few common directories in the
+ | \.git # root of the project
+ | \.hg
+ | \.mypy_cache
+ | \.tox
+ | \.venv
+ | _build
+ | buck-out
+ | build
+ | dist
+ )/
+ | foo.py # also separately exclude a file named foo.py in
+ # the root of the project
+)
+'''
+```
+
+
+
+## Lookup hierarchy
+
+Command-line options have defaults that you can see in `--help`. A `pyproject.toml` can
+override those defaults. Finally, options provided by the user on the command line
+override both.
+
+_Black_ will only ever use one `pyproject.toml` file during an entire run. It doesn't
+look for multiple files, and doesn't compose configuration from different levels of the
+file hierarchy.
diff --git a/docs/show_your_style.md b/docs/show_your_style.md
new file mode 100644
index 00000000000..67b213c3965
--- /dev/null
+++ b/docs/show_your_style.md
@@ -0,0 +1,19 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md"
+
+# Show your style
+
+Use the badge in your project's README.md:
+
+```md
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
+```
+
+Using the badge in README.rst:
+
+```
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+```
+
+Looks like this:
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
diff --git a/docs/version_control_integration.md b/docs/version_control_integration.md
new file mode 100644
index 00000000000..25dac308c47
--- /dev/null
+++ b/docs/version_control_integration.md
@@ -0,0 +1,28 @@
+[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md"
+
+# Version control integration
+
+Use [pre-commit](https://pre-commit.com/). Once you
+[have it installed](https://pre-commit.com/#install), add this to the
+`.pre-commit-config.yaml` in your repository:
+
+```yaml
+repos:
+ - repo: https://github.com/psf/black
+ rev: 19.10b0 # Replace by any tag/version: https://github.com/psf/black/tags
+ hooks:
+ - id: black
+ language_version: python3 # Should be a command that runs python3.6+
+```
+
+Then run `pre-commit install` and you're ready to go.
+
+Avoid using `args` in the hook. Instead, store necessary configuration in
+`pyproject.toml` so that editors and command-line usage of Black all behave consistently
+for your project. See _Black_'s own
+[pyproject.toml](https://github.com/psf/black/blob/master/pyproject.toml) for an
+example.
+
+If you're already using Python 3.7, switch the `language_version` accordingly. Finally,
+`stable` is a branch that tracks the latest release on PyPI. If you'd rather run on
+master, this is also an option.
From cd3a93a14689f046468ece2a5b1f78863c3c4cd2 Mon Sep 17 00:00:00 2001
From: Zac-HD
Date: Fri, 21 Aug 2020 20:58:58 +1000
Subject: [PATCH 032/680] Property-based fuzz test
---
.github/workflows/fuzz.yml | 31 ++++++++++++++++++++
.gitignore | 1 +
README.md | 1 +
fuzz.py | 59 ++++++++++++++++++++++++++++++++++++++
4 files changed, 92 insertions(+)
create mode 100644 .github/workflows/fuzz.yml
create mode 100644 fuzz.py
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
new file mode 100644
index 00000000000..92caa0fd5c1
--- /dev/null
+++ b/.github/workflows/fuzz.yml
@@ -0,0 +1,31 @@
+name: Fuzz
+
+on: [push, pull_request]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: [3.6, 3.7, 3.8]
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install --upgrade coverage
+ python -m pip install --upgrade hypothesmith
+ python -m pip install -e ".[d]"
+
+ - name: Run fuzz tests
+ run: |
+ coverage run fuzz.py
+ coverage report
diff --git a/.gitignore b/.gitignore
index 509797e65c4..6b94cacd183 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ src/_black_version.py
.eggs
.dmypy.json
*.swp
+.hypothesis/
diff --git a/README.md b/README.md
index 44f2d207c8b..20f6fa420b2 100644
--- a/README.md
+++ b/README.md
@@ -684,3 +684,4 @@ Multiple contributions by:
- Yazdan
- [Yngve Høiseth](mailto:yngve@hoiseth.net)
- [Yurii Karabas](mailto:1998uriyyo@gmail.com)
+- [Zac Hatfield-Dodds](mailto:zac@zhd.dev)
diff --git a/fuzz.py b/fuzz.py
new file mode 100644
index 00000000000..fdd4917f2ec
--- /dev/null
+++ b/fuzz.py
@@ -0,0 +1,59 @@
+"""Property-based tests for Black.
+
+By Zac Hatfield-Dodds, based on my Hypothesmith tool for source code
+generation. You can run this file with `python`, `pytest`, or (soon)
+a coverage-guided fuzzer I'm working on.
+"""
+
+import hypothesmith
+from hypothesis import HealthCheck, given, settings, strategies as st
+
+import black
+
+
+# This test uses the Hypothesis and Hypothesmith libraries to generate random
+# syntatically-valid Python source code and run Black in odd modes.
+@settings(
+ max_examples=1000, # roughly 1k tests/minute, or half that under coverage
+ derandomize=True, # deterministic mode to avoid CI flakiness
+ deadline=None, # ignore Hypothesis' health checks; we already know that
+ suppress_health_check=HealthCheck.all(), # this is slow and filter-heavy.
+)
+@given(
+ # Note that while Hypothesmith might generate code unlike that written by
+ # humans, it's a general test that should pass for any *valid* source code.
+ # (so e.g. running it against code scraped of the internet might also help)
+ src_contents=hypothesmith.from_grammar() | hypothesmith.from_node(),
+ # Using randomly-varied modes helps us to exercise less common code paths.
+ mode=st.builds(
+ black.FileMode,
+ line_length=st.just(88) | st.integers(0, 200),
+ string_normalization=st.booleans(),
+ is_pyi=st.booleans(),
+ ),
+)
+def test_idempotent_any_syntatically_valid_python(
+ src_contents: str, mode: black.FileMode
+) -> None:
+ # Before starting, let's confirm that the input string is valid Python:
+ compile(src_contents, "", "exec") # else the bug is in hypothesmith
+
+ # Then format the code...
+ try:
+ dst_contents = black.format_str(src_contents, mode=mode)
+ except black.InvalidInput:
+ # This is a bug - if it's valid Python code, as above, black should be
+ # able to code with it. See issues #970, #1012, #1358, and #1557.
+ # TODO: remove this try-except block when issues are resolved.
+ return
+
+ # And check that we got equivalent and stable output.
+ black.assert_equivalent(src_contents, dst_contents)
+ black.assert_stable(src_contents, dst_contents, mode=mode)
+
+ # Future test: check that pure-python and mypyc versions of black
+ # give identical output for identical input?
+
+
+if __name__ == "__main__":
+ test_idempotent_any_syntatically_valid_python()
From fd5928c589267b5df5fffbe408ec0b97a558f70f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Mon, 24 Aug 2020 14:11:18 +0200
Subject: [PATCH 033/680] Update the changelog
---
CHANGES.md | 39 ++++++++++++++++++++++++++++++++++-----
1 file changed, 34 insertions(+), 5 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index eb6d1c2ebbf..d36d8463a45 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,15 +7,44 @@
- re-implemented support for explicit trailing commas: now it works consistently within
any bracket pair, including nested structures (#1288 and duplicates)
-- reindent docstrings when reindenting code around it (#1053)
+- `Black` now reindents docstrings when reindenting code around it (#1053)
-- show colored diffs (#1266)
+- `Black` now shows colored diffs (#1266)
-- move to 'py3' tagged wheels (#1388)
+- `Black` is now packaged using 'py3' tagged wheels (#1388)
-- remove deprecated `--py36` option (#1236)
+- `Black` now supports Python 3.8 code, e.g. star expressions in return statements
+ (#1121)
-- add `--force-exclude` argument (#1032)
+- `Black` no longer normalizes capital R-string prefixes as those have a
+ community-accepted meaning (#1244)
+
+- `Black` now uses exit code 2 when specified configuration file doesn't exit (#1361)
+
+- `Black` now works on AWS Lambda (#1141)
+
+- added `--force-exclude` argument (#1032)
+
+- removed deprecated `--py36` option (#1236)
+
+- fixed `--diff` output when EOF is encountered (#526)
+
+- fixed `# fmt: off` handling around decorators (#560)
+
+- fixed unstable formatting with some `# type: ignore` comments (#1113)
+
+- fixed invalid removal on organizing brackets followed by indexing (#1575)
+
+- introduced `black-primer`, a CI tool that allows us to run regression tests against
+ existing open source users of Black (#1402)
+
+- introduced property-based fuzzing to our test suite based on Hypothesis and
+ Hypothersmith (#1566)
+
+- implemented experimental and disabled by default long string rewrapping (#1132),
+ hidden under a `--experimental-string-processing` flag while it's being worked on;
+ this is an undocumented and unsupported feature, you lose Internet points for
+ depending on it (#1609)
#### Vim plugin
From 5faabb56160599c5303e53cf6113f1becc69756d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Mon, 24 Aug 2020 14:33:16 +0200
Subject: [PATCH 034/680] Make doc generation a little smarter, update doc
sections
---
docs/authors.md | 1 +
docs/change_log.md | 39 ++++++++++++++++++++++++++++++++++-----
docs/conf.py | 19 +++++++++++++------
3 files changed, 48 insertions(+), 11 deletions(-)
diff --git a/docs/authors.md b/docs/authors.md
index 6a3a8d63f91..a5349b4b9df 100644
--- a/docs/authors.md
+++ b/docs/authors.md
@@ -181,3 +181,4 @@ Multiple contributions by:
- Yazdan
- [Yngve Høiseth](mailto:yngve@hoiseth.net)
- [Yurii Karabas](mailto:1998uriyyo@gmail.com)
+- [Zac Hatfield-Dodds](mailto:zac@zhd.dev)
diff --git a/docs/change_log.md b/docs/change_log.md
index 3cc8c40d8c6..5b7f08e101d 100644
--- a/docs/change_log.md
+++ b/docs/change_log.md
@@ -9,15 +9,44 @@
- re-implemented support for explicit trailing commas: now it works consistently within
any bracket pair, including nested structures (#1288 and duplicates)
-- reindent docstrings when reindenting code around it (#1053)
+- `Black` now reindents docstrings when reindenting code around it (#1053)
-- show colored diffs (#1266)
+- `Black` now shows colored diffs (#1266)
-- move to 'py3' tagged wheels (#1388)
+- `Black` is now packaged using 'py3' tagged wheels (#1388)
-- remove deprecated `--py36` option (#1236)
+- `Black` now supports Python 3.8 code, e.g. star expressions in return statements
+ (#1121)
-- add `--force-exclude` argument (#1032)
+- `Black` no longer normalizes capital R-string prefixes as those have a
+ community-accepted meaning (#1244)
+
+- `Black` now uses exit code 2 when specified configuration file doesn't exit (#1361)
+
+- `Black` now works on AWS Lambda (#1141)
+
+- added `--force-exclude` argument (#1032)
+
+- removed deprecated `--py36` option (#1236)
+
+- fixed `--diff` output when EOF is encountered (#526)
+
+- fixed `# fmt: off` handling around decorators (#560)
+
+- fixed unstable formatting with some `# type: ignore` comments (#1113)
+
+- fixed invalid removal on organizing brackets followed by indexing (#1575)
+
+- introduced `black-primer`, a CI tool that allows us to run regression tests against
+ existing open source users of Black (#1402)
+
+- introduced property-based fuzzing to our test suite based on Hypothesis and
+ Hypothersmith (#1566)
+
+- implemented experimental and disabled by default long string rewrapping (#1132),
+ hidden under a `--experimental-string-processing` flag while it's being worked on;
+ this is an undocumented and unsupported feature, you lose Internet points for
+ depending on it (#1609)
#### Vim plugin
diff --git a/docs/conf.py b/docs/conf.py
index 8dd77b41d61..7381c9d6423 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -15,7 +15,7 @@
from pathlib import Path
import re
import string
-from typing import Callable, List, Optional, Pattern, Tuple, Set
+from typing import Callable, Dict, List, Optional, Pattern, Tuple, Set
from dataclasses import dataclass
import logging
@@ -99,7 +99,13 @@ def get_contents(section: DocSection) -> str:
for lineno, line in enumerate(f, start=1):
if lineno >= start_line and lineno < end_line:
contents.append(line)
- return "".join(contents)
+ result = "".join(contents)
+ # Let's make Prettier happy with the amount of trailing newlines in the sections.
+ if result.endswith("\n\n"):
+ result = result[:-1]
+ if not result.endswith("\n"):
+ result = result + "\n"
+ return result
def get_sections_from_readme() -> List[DocSection]:
@@ -159,18 +165,19 @@ def process_sections(
It processes custom sections before the README generated sections so sections in the
README can be overwritten with custom options.
"""
- processed_sections: Set[str] = set()
+ processed_sections: Dict[str, DocSection] = {}
modified_files: Set[Path] = set()
sections: List[DocSection] = custom_sections
sections.extend(readme_sections)
for section in sections:
- LOG.info(f"Processing '{section.name}' from {section.src}")
if section.name in processed_sections:
- LOG.info(
+ LOG.warning(
f"Skipping '{section.name}' from '{section.src}' as it is a duplicate"
+ f" of a custom section from '{processed_sections[section.name].src}'"
)
continue
+ LOG.info(f"Processing '{section.name}' from '{section.src}'")
target_path: Path = CURRENT_DIR / section.get_out_filename()
if target_path in modified_files:
LOG.warning(
@@ -188,7 +195,7 @@ def process_sections(
rel = section.src.resolve().relative_to(CURRENT_DIR.parent)
f.write(f'[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM {rel}"\n\n')
f.write(contents)
- processed_sections.add(section.name)
+ processed_sections[section.name] = section
modified_files.add(target_path)
From 292bceb9fd2d7a642351d06c02d2e751933baa5c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Mon, 24 Aug 2020 18:48:11 +0200
Subject: [PATCH 035/680] Add more trailing comma test variants
---
tests/data/comments7.py | 142 ++++++++
tests/data/composition_no_trailing_comma.py | 367 ++++++++++++++++++++
tests/test_black.py | 8 +
3 files changed, 517 insertions(+)
create mode 100644 tests/data/composition_no_trailing_comma.py
diff --git a/tests/data/comments7.py b/tests/data/comments7.py
index a7bd281c91e..0e2bd35bf55 100644
--- a/tests/data/comments7.py
+++ b/tests/data/comments7.py
@@ -22,6 +22,12 @@
# resolve_to_config_type,
# DEFAULT_TYPE_ATTRIBUTES,
)
+from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import (
+ MyLovelyCompanyTeamProjectComponent # NOT DRY
+)
+from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import (
+ MyLovelyCompanyTeamProjectComponent as component # DRY
+)
result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@@ -46,6 +52,26 @@ def func():
0.0789,
a[-1], # type: ignore
)
+ c = call(
+ 0.0123,
+ 0.0456,
+ 0.0789,
+ 0.0123,
+ 0.0789,
+ a[-1] # type: ignore
+ )
+ c = call(
+ 0.0123,
+ 0.0456,
+ 0.0789,
+ 0.0123,
+ 0.0456,
+ 0.0789,
+ 0.0123,
+ 0.0456,
+ 0.0789,
+ a[-1] # type: ignore
+ )
# The type: ignore exception only applies to line length, not
# other types of formatting.
@@ -55,6 +81,54 @@ def func():
)
+class C:
+ @pytest.mark.parametrize(
+ ("post_data", "message"),
+ [
+ # metadata_version errors.
+ (
+ {},
+ "None is an invalid value for Metadata-Version. Error: This field is"
+ " required. see"
+ " https://packaging.python.org/specifications/core-metadata"
+ ),
+ (
+ {"metadata_version": "-1"},
+ "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata"
+ " Version see"
+ " https://packaging.python.org/specifications/core-metadata"
+ ),
+ # name errors.
+ (
+ {"metadata_version": "1.2"},
+ "'' is an invalid value for Name. Error: This field is required. see"
+ " https://packaging.python.org/specifications/core-metadata"
+ ),
+ (
+ {"metadata_version": "1.2", "name": "foo-"},
+ "'foo-' is an invalid value for Name. Error: Must start and end with a"
+ " letter or numeral and contain only ascii numeric and '.', '_' and"
+ " '-'. see https://packaging.python.org/specifications/core-metadata"
+ ),
+ # version errors.
+ (
+ {"metadata_version": "1.2", "name": "example"},
+ "'' is an invalid value for Version. Error: This field is required. see"
+ " https://packaging.python.org/specifications/core-metadata"
+ ),
+ (
+ {"metadata_version": "1.2", "name": "example", "version": "dog"},
+ "'dog' is an invalid value for Version. Error: Must start and end with"
+ " a letter or numeral and contain only ascii numeric and '.', '_' and"
+ " '-'. see https://packaging.python.org/specifications/core-metadata"
+ )
+ ]
+ )
+ def test_fails_invalid_post_data(
+ self, pyramid_config, db_request, post_data, message
+ ):
+ ...
+
# output
from .config import (
@@ -81,6 +155,12 @@ def func():
# resolve_to_config_type,
# DEFAULT_TYPE_ATTRIBUTES,
)
+from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import (
+ MyLovelyCompanyTeamProjectComponent, # NOT DRY
+)
+from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import (
+ MyLovelyCompanyTeamProjectComponent as component, # DRY
+)
result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@@ -105,6 +185,19 @@ def func():
0.0789,
a[-1], # type: ignore
)
+ c = call(0.0123, 0.0456, 0.0789, 0.0123, 0.0789, a[-1]) # type: ignore
+ c = call(
+ 0.0123,
+ 0.0456,
+ 0.0789,
+ 0.0123,
+ 0.0456,
+ 0.0789,
+ 0.0123,
+ 0.0456,
+ 0.0789,
+ a[-1], # type: ignore
+ )
# The type: ignore exception only applies to line length, not
# other types of formatting.
@@ -122,3 +215,52 @@ def func():
"aaaaaaaa",
"aaaaaaaa",
)
+
+
+class C:
+ @pytest.mark.parametrize(
+ ("post_data", "message"),
+ [
+ # metadata_version errors.
+ (
+ {},
+ "None is an invalid value for Metadata-Version. Error: This field is"
+ " required. see"
+ " https://packaging.python.org/specifications/core-metadata",
+ ),
+ (
+ {"metadata_version": "-1"},
+ "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata"
+ " Version see"
+ " https://packaging.python.org/specifications/core-metadata",
+ ),
+ # name errors.
+ (
+ {"metadata_version": "1.2"},
+ "'' is an invalid value for Name. Error: This field is required. see"
+ " https://packaging.python.org/specifications/core-metadata",
+ ),
+ (
+ {"metadata_version": "1.2", "name": "foo-"},
+ "'foo-' is an invalid value for Name. Error: Must start and end with a"
+ " letter or numeral and contain only ascii numeric and '.', '_' and"
+ " '-'. see https://packaging.python.org/specifications/core-metadata",
+ ),
+ # version errors.
+ (
+ {"metadata_version": "1.2", "name": "example"},
+ "'' is an invalid value for Version. Error: This field is required. see"
+ " https://packaging.python.org/specifications/core-metadata",
+ ),
+ (
+ {"metadata_version": "1.2", "name": "example", "version": "dog"},
+ "'dog' is an invalid value for Version. Error: Must start and end with"
+ " a letter or numeral and contain only ascii numeric and '.', '_' and"
+ " '-'. see https://packaging.python.org/specifications/core-metadata",
+ ),
+ ],
+ )
+ def test_fails_invalid_post_data(
+ self, pyramid_config, db_request, post_data, message
+ ):
+ ...
\ No newline at end of file
diff --git a/tests/data/composition_no_trailing_comma.py b/tests/data/composition_no_trailing_comma.py
new file mode 100644
index 00000000000..f17b89dea8d
--- /dev/null
+++ b/tests/data/composition_no_trailing_comma.py
@@ -0,0 +1,367 @@
+class C:
+ def test(self) -> None:
+ with patch("black.out", print):
+ self.assertEqual(
+ unstyle(str(report)), "1 file reformatted, 1 file failed to reformat."
+ )
+ self.assertEqual(
+ unstyle(str(report)),
+ "1 file reformatted, 1 file left unchanged, 1 file failed to reformat.",
+ )
+ self.assertEqual(
+ unstyle(str(report)),
+ "2 files reformatted, 1 file left unchanged, 1 file failed to"
+ " reformat.",
+ )
+ self.assertEqual(
+ unstyle(str(report)),
+ "2 files reformatted, 2 files left unchanged, 2 files failed to"
+ " reformat.",
+ )
+ for i in (a,):
+ if (
+ # Rule 1
+ i % 2 == 0
+ # Rule 2
+ and i % 3 == 0
+ ):
+ while (
+ # Just a comment
+ call()
+ # Another
+ ):
+ print(i)
+ xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy(
+ push_manager=context.request.resource_manager,
+ max_items_to_push=num_items,
+ batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE
+ ).push(
+ # Only send the first n items.
+ items=items[:num_items]
+ )
+ return (
+ 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s'
+ % (test.name, test.filename, lineno, lname, err)
+ )
+
+ def omitting_trailers(self) -> None:
+ get_collection(
+ hey_this_is_a_very_long_call, it_has_funny_attributes, really=True
+ )[OneLevelIndex]
+ get_collection(
+ hey_this_is_a_very_long_call, it_has_funny_attributes, really=True
+ )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex]
+ d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][
+ 22
+ ]
+ assignment = (
+ some.rather.elaborate.rule() and another.rule.ending_with.index[123]
+ )
+
+ def easy_asserts(self) -> None:
+ assert {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9
+ } == expected, "Not what we expected"
+
+ assert expected == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9
+ }, "Not what we expected"
+
+ assert expected == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9
+ }
+
+ def tricky_asserts(self) -> None:
+ assert {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9
+ } == expected(
+ value, is_going_to_be="too long to fit in a single line", srsly=True
+ ), "Not what we expected"
+
+ assert {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9
+ } == expected, (
+ "Not what we expected and the message is too long to fit in one line"
+ )
+
+ assert expected(
+ value, is_going_to_be="too long to fit in a single line", srsly=True
+ ) == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9
+ }, "Not what we expected"
+
+ assert expected == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9
+ }, (
+ "Not what we expected and the message is too long to fit in one line"
+ " because it's too long"
+ )
+
+ dis_c_instance_method = """\
+ %3d 0 LOAD_FAST 1 (x)
+ 2 LOAD_CONST 1 (1)
+ 4 COMPARE_OP 2 (==)
+ 6 LOAD_FAST 0 (self)
+ 8 STORE_ATTR 0 (x)
+ 10 LOAD_CONST 0 (None)
+ 12 RETURN_VALUE
+ """ % (
+ _C.__init__.__code__.co_firstlineno + 1,
+ )
+
+ assert (
+ expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect
+ == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9
+ }
+ )
+
+
+
+# output
+
+class C:
+ def test(self) -> None:
+ with patch("black.out", print):
+ self.assertEqual(
+ unstyle(str(report)), "1 file reformatted, 1 file failed to reformat."
+ )
+ self.assertEqual(
+ unstyle(str(report)),
+ "1 file reformatted, 1 file left unchanged, 1 file failed to reformat.",
+ )
+ self.assertEqual(
+ unstyle(str(report)),
+ "2 files reformatted, 1 file left unchanged, 1 file failed to"
+ " reformat.",
+ )
+ self.assertEqual(
+ unstyle(str(report)),
+ "2 files reformatted, 2 files left unchanged, 2 files failed to"
+ " reformat.",
+ )
+ for i in (a,):
+ if (
+ # Rule 1
+ i % 2 == 0
+ # Rule 2
+ and i % 3 == 0
+ ):
+ while (
+ # Just a comment
+ call()
+ # Another
+ ):
+ print(i)
+ xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy(
+ push_manager=context.request.resource_manager,
+ max_items_to_push=num_items,
+ batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE,
+ ).push(
+ # Only send the first n items.
+ items=items[:num_items]
+ )
+ return (
+ 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s'
+ % (test.name, test.filename, lineno, lname, err)
+ )
+
+ def omitting_trailers(self) -> None:
+ get_collection(
+ hey_this_is_a_very_long_call, it_has_funny_attributes, really=True
+ )[OneLevelIndex]
+ get_collection(
+ hey_this_is_a_very_long_call, it_has_funny_attributes, really=True
+ )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex]
+ d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][
+ 22
+ ]
+ assignment = (
+ some.rather.elaborate.rule() and another.rule.ending_with.index[123]
+ )
+
+ def easy_asserts(self) -> None:
+ assert {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9,
+ } == expected, "Not what we expected"
+
+ assert expected == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9,
+ }, "Not what we expected"
+
+ assert expected == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9,
+ }
+
+ def tricky_asserts(self) -> None:
+ assert {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9,
+ } == expected(
+ value, is_going_to_be="too long to fit in a single line", srsly=True
+ ), "Not what we expected"
+
+ assert {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9,
+ } == expected, (
+ "Not what we expected and the message is too long to fit in one line"
+ )
+
+ assert expected(
+ value, is_going_to_be="too long to fit in a single line", srsly=True
+ ) == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9,
+ }, "Not what we expected"
+
+ assert expected == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9,
+ }, (
+ "Not what we expected and the message is too long to fit in one line"
+ " because it's too long"
+ )
+
+ dis_c_instance_method = """\
+ %3d 0 LOAD_FAST 1 (x)
+ 2 LOAD_CONST 1 (1)
+ 4 COMPARE_OP 2 (==)
+ 6 LOAD_FAST 0 (self)
+ 8 STORE_ATTR 0 (x)
+ 10 LOAD_CONST 0 (None)
+ 12 RETURN_VALUE
+ """ % (
+ _C.__init__.__code__.co_firstlineno + 1,
+ )
+
+ assert (
+ expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect
+ == {
+ key1: value1,
+ key2: value2,
+ key3: value3,
+ key4: value4,
+ key5: value5,
+ key6: value6,
+ key7: value7,
+ key8: value8,
+ key9: value9,
+ }
+ )
diff --git a/tests/test_black.py b/tests/test_black.py
index f89f1403aba..bb1929ebb31 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -630,6 +630,14 @@ def test_composition(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_composition_no_trailing_comma(self) -> None:
+ source, expected = read_data("composition_no_trailing_comma")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
@patch("black.dump_to_file", dump_to_stderr)
def test_empty_lines(self) -> None:
source, expected = read_data("empty_lines")
From d46268cd671760d4c370476006795919951b1076 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Mon, 24 Aug 2020 21:33:51 +0200
Subject: [PATCH 036/680] Run trailing comma tests with TargetVersion.PY38
---
tests/test_black.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/tests/test_black.py b/tests/test_black.py
index bb1929ebb31..16002c0b728 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -593,7 +593,8 @@ def test_comments6(self) -> None:
@patch("black.dump_to_file", dump_to_stderr)
def test_comments7(self) -> None:
source, expected = read_data("comments7")
- actual = fs(source)
+ mode = replace(DEFAULT_MODE, target_versions={black.TargetVersion.PY38})
+ actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
@@ -633,7 +634,8 @@ def test_composition(self) -> None:
@patch("black.dump_to_file", dump_to_stderr)
def test_composition_no_trailing_comma(self) -> None:
source, expected = read_data("composition_no_trailing_comma")
- actual = fs(source)
+ mode = replace(DEFAULT_MODE, target_versions={black.TargetVersion.PY38})
+ actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
From 586d24236e6b57bc3b5da85fdbe2563835021076 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Mon, 24 Aug 2020 18:29:59 +0200
Subject: [PATCH 037/680] Address pre-existing trailing commas when not in the
rightmost bracket pair
This required some hackery. Long story short, we need to reuse the ability to
omit rightmost bracket pairs (which glues them together and splits on something
else instead), for use with pre-existing trailing commas.
This form of user-controlled formatting is brittle so we have to be careful not
to cause a scenario where Black first formats code without trailing commas in
one way, and then looks at the same file with pre-existing trailing commas
(that it itself put on the previous run) and decides to format the code again.
One particular ugly edge case here is handling of optional parentheses. In
particular, the long-standing `line_length=1` hack got in the way of
pre-existing trailing commas and had to be removed. Instead, a more
intelligent but costly solution was put in place: a "second opinion" if the
formatting that omits optional parentheses ended up causing lines to be too
long. Again, for efficiency purposes, Black reuses Leaf objects from blib2to3
and modifies them in place, which was invalid for having two separate
formattings. Line cloning was used to mitigate this.
Fixes #1619
---
src/black/__init__.py | 243 +++++++++++++++++-----
tests/data/cantfit.py | 12 +-
tests/data/function_trailing_comma.py | 21 ++
tests/data/function_trailing_comma_wip.py | 5 -
tests/data/long_strings_flag_disabled.py | 13 +-
tests/test_black.py | 16 +-
6 files changed, 235 insertions(+), 75 deletions(-)
delete mode 100644 tests/data/function_trailing_comma_wip.py
diff --git a/src/black/__init__.py b/src/black/__init__.py
index faa88b38af7..e37caa98a2c 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -195,6 +195,7 @@ class Feature(Enum):
ASYNC_KEYWORDS = 7
ASSIGNMENT_EXPRESSIONS = 8
POS_ONLY_ARGUMENTS = 9
+ FORCE_OPTIONAL_PARENTHESES = 50
VERSION_TO_FEATURES: Dict[TargetVersion, Set[Feature]] = {
@@ -1284,6 +1285,7 @@ class BracketTracker:
previous: Optional[Leaf] = None
_for_loop_depths: List[int] = field(default_factory=list)
_lambda_argument_depths: List[int] = field(default_factory=list)
+ invisible: List[Leaf] = field(default_factory=list)
def mark(self, leaf: Leaf) -> None:
"""Mark `leaf` with bracket-related metadata. Keep track of delimiters.
@@ -1309,6 +1311,8 @@ def mark(self, leaf: Leaf) -> None:
self.depth -= 1
opening_bracket = self.bracket_match.pop((self.depth, leaf.type))
leaf.opening_bracket = opening_bracket
+ if not leaf.value:
+ self.invisible.append(leaf)
leaf.bracket_depth = self.depth
if self.depth == 0:
delim = is_split_before_delimiter(leaf, self.previous)
@@ -1321,6 +1325,8 @@ def mark(self, leaf: Leaf) -> None:
if leaf.type in OPENING_BRACKETS:
self.bracket_match[self.depth, BRACKET[leaf.type]] = leaf
self.depth += 1
+ if not leaf.value:
+ self.invisible.append(leaf)
self.previous = leaf
self.maybe_increment_lambda_arguments(leaf)
self.maybe_increment_for_loop_variable(leaf)
@@ -2627,20 +2633,31 @@ def init_st(ST: Type[StringTransformer]) -> StringTransformer:
else:
def rhs(line: Line, features: Collection[Feature]) -> Iterator[Line]:
+ """Wraps calls to `right_hand_split`.
+
+ The calls increasingly `omit` right-hand trailers (bracket pairs with
+ content), meaning the trailers get glued together to split on another
+ bracket pair instead.
+ """
for omit in generate_trailers_to_omit(line, mode.line_length):
lines = list(
right_hand_split(line, mode.line_length, features, omit=omit)
)
+ # Note: this check is only able to figure out if the first line of the
+ # *current* transformation fits in the line length. This is true only
+ # for simple cases. All others require running more transforms via
+ # `transform_line()`. This check doesn't know if those would succeed.
if is_line_short_enough(lines[0], line_length=mode.line_length):
yield from lines
return
# All splits failed, best effort split with no omits.
# This mostly happens to multiline strings that are by definition
- # reported as not fitting a single line.
- # line_length=1 here was historically a bug that somehow became a feature.
- # See #762 and #781 for the full story.
- yield from right_hand_split(line, line_length=1, features=features)
+ # reported as not fitting a single line, as well as lines that contain
+ # pre-existing trailing commas (those have to be exploded).
+ yield from right_hand_split(
+ line, line_length=mode.line_length, features=features
+ )
if mode.experimental_string_processing:
if line.inside_brackets:
@@ -2671,17 +2688,8 @@ def rhs(line: Line, features: Collection[Feature]) -> Iterator[Line]:
# We are accumulating lines in `result` because we might want to abort
# mission and return the original line in the end, or attempt a different
# split altogether.
- result: List[Line] = []
try:
- for transformed_line in transform(line, features):
- if str(transformed_line).strip("\n") == line_str:
- raise CannotTransform(
- "Line transformer returned an unchanged result"
- )
-
- result.extend(
- transform_line(transformed_line, mode=mode, features=features)
- )
+ result = run_transformer(line, transform, mode, features, line_str=line_str)
except CannotTransform:
continue
else:
@@ -2722,6 +2730,7 @@ class StringTransformer(ABC):
line_length: int
normalize_strings: bool
+ __name__ = "StringTransformer"
@abstractmethod
def do_match(self, line: Line) -> TMatchResult:
@@ -2968,7 +2977,7 @@ def __remove_backslash_line_continuation_chars(
)
new_line = line.clone()
- new_line.comments = line.comments
+ new_line.comments = line.comments.copy()
append_leaves(new_line, line, LL)
new_string_leaf = new_line.leaves[string_idx]
@@ -3296,7 +3305,6 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]:
new_line = line.clone()
new_line.comments = line.comments.copy()
-
append_leaves(new_line, line, LL[: string_idx - 1])
string_leaf = Leaf(token.STRING, LL[string_idx].value)
@@ -4740,8 +4748,9 @@ def right_hand_split(
tail = bracket_split_build_line(tail_leaves, line, opening_bracket)
bracket_split_succeeded_or_raise(head, body, tail)
if (
+ Feature.FORCE_OPTIONAL_PARENTHESES not in features
# the opening bracket is an optional paren
- opening_bracket.type == token.LPAR
+ and opening_bracket.type == token.LPAR
and not opening_bracket.value
# the closing bracket is an optional paren
and closing_bracket.type == token.RPAR
@@ -4752,7 +4761,7 @@ def right_hand_split(
# there are no standalone comments in the body
and not body.contains_standalone_comments(0)
# and we can actually remove the parens
- and can_omit_invisible_parens(body, line_length)
+ and can_omit_invisible_parens(body, line_length, omit_on_explode=omit)
):
omit = {id(closing_bracket), *omit}
try:
@@ -5587,6 +5596,9 @@ def should_split_body_explode(line: Line, opening_bracket: Leaf) -> bool:
def is_one_tuple_between(opening: Leaf, closing: Leaf, leaves: List[Leaf]) -> bool:
"""Return True if content between `opening` and `closing` looks like a one-tuple."""
+ if opening.type != token.LPAR and closing.type != token.RPAR:
+ return False
+
depth = closing.bracket_depth + 1
for _opening_index, leaf in enumerate(leaves):
if leaf is opening:
@@ -5678,11 +5690,13 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[Leaf
a preceding closing bracket fits in one line.
Yielded sets are cumulative (contain results of previous yields, too). First
- set is empty.
+ set is empty, unless the line should explode, in which case bracket pairs until
+ the one that needs to explode are omitted.
"""
omit: Set[LeafID] = set()
- yield omit
+ if not line.should_explode:
+ yield omit
length = 4 * line.depth
opening_bracket: Optional[Leaf] = None
@@ -5701,9 +5715,24 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[Leaf
if leaf is opening_bracket:
opening_bracket = None
elif leaf.type in CLOSING_BRACKETS:
+ prev = line.leaves[index - 1] if index > 0 else None
+ if (
+ line.should_explode
+ and prev
+ and prev.type == token.COMMA
+ and not prev.was_checked
+ and not is_one_tuple_between(
+ leaf.opening_bracket, leaf, line.leaves
+ )
+ ):
+ # Never omit bracket pairs with pre-existing trailing commas.
+ # We need to explode on those.
+ break
+
inner_brackets.add(id(leaf))
elif leaf.type in CLOSING_BRACKETS:
- if index > 0 and line.leaves[index - 1].type in OPENING_BRACKETS:
+ prev = line.leaves[index - 1] if index > 0 else None
+ if prev and prev.type in OPENING_BRACKETS:
# Empty brackets would fail a split so treat them as "inner"
# brackets (e.g. only add them to the `omit` set if another
# pair of brackets was good enough.
@@ -5716,6 +5745,17 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[Leaf
inner_brackets.clear()
yield omit
+ if (
+ line.should_explode
+ and prev
+ and prev.type == token.COMMA
+ and not prev.was_checked
+ and not is_one_tuple_between(leaf.opening_bracket, leaf, line.leaves)
+ ):
+ # Never omit bracket pairs with pre-existing trailing commas.
+ # We need to explode on those.
+ break
+
if leaf.value:
opening_bracket = leaf.opening_bracket
closing_bracket = leaf
@@ -6297,7 +6337,11 @@ def can_be_split(line: Line) -> bool:
return True
-def can_omit_invisible_parens(line: Line, line_length: int) -> bool:
+def can_omit_invisible_parens(
+ line: Line,
+ line_length: int,
+ omit_on_explode: Collection[LeafID] = (),
+) -> bool:
"""Does `line` have a shape safe to reformat without optional parens around it?
Returns True for only a subset of potentially nice looking formattings but
@@ -6320,37 +6364,27 @@ def can_omit_invisible_parens(line: Line, line_length: int) -> bool:
assert len(line.leaves) >= 2, "Stranded delimiter"
- first = line.leaves[0]
- second = line.leaves[1]
- penultimate = line.leaves[-2]
- last = line.leaves[-1]
-
# With a single delimiter, omit if the expression starts or ends with
# a bracket.
+ first = line.leaves[0]
+ second = line.leaves[1]
if first.type in OPENING_BRACKETS and second.type not in CLOSING_BRACKETS:
- remainder = False
- length = 4 * line.depth
- for _index, leaf, leaf_length in enumerate_with_length(line):
- if leaf.type in CLOSING_BRACKETS and leaf.opening_bracket is first:
- remainder = True
- if remainder:
- length += leaf_length
- if length > line_length:
- break
-
- if leaf.type in OPENING_BRACKETS:
- # There are brackets we can further split on.
- remainder = False
-
- else:
- # checked the entire string and line length wasn't exceeded
- if len(line.leaves) == _index + 1:
- return True
+ if _can_omit_opening_paren(line, first=first, line_length=line_length):
+ return True
# Note: we are not returning False here because a line might have *both*
# a leading opening bracket and a trailing closing bracket. If the
# opening bracket doesn't match our rule, maybe the closing will.
+ penultimate = line.leaves[-2]
+ last = line.leaves[-1]
+ if line.should_explode:
+ try:
+ penultimate, last = last_two_except(line.leaves, omit=omit_on_explode)
+ except LookupError:
+ # Turns out we'd omit everything. We cannot skip the optional parentheses.
+ return False
+
if (
last.type == token.RPAR
or last.type == token.RBRACE
@@ -6371,21 +6405,124 @@ def can_omit_invisible_parens(line: Line, line_length: int) -> bool:
# unnecessary.
return True
- length = 4 * line.depth
- seen_other_brackets = False
- for _index, leaf, leaf_length in enumerate_with_length(line):
+ if (
+ line.should_explode
+ and penultimate.type == token.COMMA
+ and not penultimate.was_checked
+ ):
+ # The rightmost non-omitted bracket pair is the one we want to explode on.
+ return True
+
+ if _can_omit_closing_paren(line, last=last, line_length=line_length):
+ return True
+
+ return False
+
+
+def _can_omit_opening_paren(line: Line, *, first: Leaf, line_length: int) -> bool:
+ """See `can_omit_invisible_parens`."""
+ remainder = False
+ length = 4 * line.depth
+ _index = -1
+ for _index, leaf, leaf_length in enumerate_with_length(line):
+ if leaf.type in CLOSING_BRACKETS and leaf.opening_bracket is first:
+ remainder = True
+ if remainder:
length += leaf_length
- if leaf is last.opening_bracket:
- if seen_other_brackets or length <= line_length:
- return True
+ if length > line_length:
+ break
- elif leaf.type in OPENING_BRACKETS:
+ if leaf.type in OPENING_BRACKETS:
# There are brackets we can further split on.
- seen_other_brackets = True
+ remainder = False
+
+ else:
+ # checked the entire string and line length wasn't exceeded
+ if len(line.leaves) == _index + 1:
+ return True
+
+ return False
+
+
+def _can_omit_closing_paren(line: Line, *, last: Leaf, line_length: int) -> bool:
+ """See `can_omit_invisible_parens`."""
+ length = 4 * line.depth
+ seen_other_brackets = False
+ for _index, leaf, leaf_length in enumerate_with_length(line):
+ length += leaf_length
+ if leaf is last.opening_bracket:
+ if seen_other_brackets or length <= line_length:
+ return True
+
+ elif leaf.type in OPENING_BRACKETS:
+ # There are brackets we can further split on.
+ seen_other_brackets = True
return False
+def last_two_except(leaves: List[Leaf], omit: Collection[LeafID]) -> Tuple[Leaf, Leaf]:
+ """Return (penultimate, last) leaves skipping brackets in `omit` and contents."""
+ stop_after = None
+ last = None
+ for leaf in reversed(leaves):
+ if stop_after:
+ if leaf is stop_after:
+ stop_after = None
+ continue
+
+ if last:
+ return leaf, last
+
+ if id(leaf) in omit:
+ stop_after = leaf.opening_bracket
+ else:
+ last = leaf
+ else:
+ raise LookupError("Last two leaves were also skipped")
+
+
+def run_transformer(
+ line: Line,
+ transform: Transformer,
+ mode: Mode,
+ features: Collection[Feature],
+ *,
+ line_str: str = "",
+) -> List[Line]:
+ if not line_str:
+ line_str = line_to_string(line)
+ result: List[Line] = []
+ for transformed_line in transform(line, features):
+ if str(transformed_line).strip("\n") == line_str:
+ raise CannotTransform("Line transformer returned an unchanged result")
+
+ result.extend(transform_line(transformed_line, mode=mode, features=features))
+
+ if not (
+ transform.__name__ == "rhs"
+ and line.bracket_tracker.invisible
+ and not any(bracket.value for bracket in line.bracket_tracker.invisible)
+ and not line.contains_multiline_strings()
+ and not result[0].contains_uncollapsable_type_comments()
+ and not result[0].contains_unsplittable_type_ignore()
+ and not is_line_short_enough(result[0], line_length=mode.line_length)
+ ):
+ return result
+
+ line_copy = line.clone()
+ append_leaves(line_copy, line, line.leaves)
+ features_fop = set(features) | {Feature.FORCE_OPTIONAL_PARENTHESES}
+ second_opinion = run_transformer(
+ line_copy, transform, mode, features_fop, line_str=line_str
+ )
+ if all(
+ is_line_short_enough(ln, line_length=mode.line_length) for ln in second_opinion
+ ):
+ result = second_opinion
+ return result
+
+
def get_cache_file(mode: Mode) -> Path:
return CACHE_DIR / f"cache.{mode.get_cache_key()}.pickle"
diff --git a/tests/data/cantfit.py b/tests/data/cantfit.py
index ef9b78e09a9..0849374f776 100644
--- a/tests/data/cantfit.py
+++ b/tests/data/cantfit.py
@@ -67,11 +67,15 @@
normal_name = (
but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying()
)
-normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying(
- arg1, arg2, arg3
+normal_name = (
+ but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying(
+ arg1, arg2, arg3
+ )
)
-normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying(
- [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
+normal_name = (
+ but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying(
+ [1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
+ )
)
# long arguments
normal_name = normal_function_name(
diff --git a/tests/data/function_trailing_comma.py b/tests/data/function_trailing_comma.py
index 314a56cf67b..d15459cbeb5 100644
--- a/tests/data/function_trailing_comma.py
+++ b/tests/data/function_trailing_comma.py
@@ -9,6 +9,12 @@ def f2(a,b,):
def f(a:int=1,):
call(arg={'explode': 'this',})
call2(arg=[1,2,3],)
+ x = {
+ "a": 1,
+ "b": 2,
+ }["a"]
+ if a == {"a": 1,"b": 2,"c": 3,"d": 4,"e": 5,"f": 6,"g": 7,"h": 8,}["a"]:
+ pass
def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@@ -51,6 +57,21 @@ def f(
call2(
arg=[1, 2, 3],
)
+ x = {
+ "a": 1,
+ "b": 2,
+ }["a"]
+ if a == {
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ "d": 4,
+ "e": 5,
+ "f": 6,
+ "g": 7,
+ "h": 8,
+ }["a"]:
+ pass
def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[
diff --git a/tests/data/function_trailing_comma_wip.py b/tests/data/function_trailing_comma_wip.py
deleted file mode 100644
index c41fc709d97..00000000000
--- a/tests/data/function_trailing_comma_wip.py
+++ /dev/null
@@ -1,5 +0,0 @@
-CONFIG_FILES = [CONFIG_FILE] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final
-
-# output
-
-CONFIG_FILES = [CONFIG_FILE] + SHARED_CONFIG_FILES + USER_CONFIG_FILES # type: Final
\ No newline at end of file
diff --git a/tests/data/long_strings_flag_disabled.py b/tests/data/long_strings_flag_disabled.py
index db3954e3abd..ef3094fd779 100644
--- a/tests/data/long_strings_flag_disabled.py
+++ b/tests/data/long_strings_flag_disabled.py
@@ -133,14 +133,11 @@
"Use f-strings instead!",
)
-old_fmt_string3 = (
- "Whereas only the strings after the percent sign were long in the last example, this example uses a long initial string as well. This is another %s %s %s %s"
- % (
- "really really really really really",
- "old",
- "way to format strings!",
- "Use f-strings instead!",
- )
+old_fmt_string3 = "Whereas only the strings after the percent sign were long in the last example, this example uses a long initial string as well. This is another %s %s %s %s" % (
+ "really really really really really",
+ "old",
+ "way to format strings!",
+ "Use f-strings instead!",
)
fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one."
diff --git a/tests/test_black.py b/tests/test_black.py
index 16002c0b728..6705490ea13 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -346,11 +346,16 @@ def test_function2(self) -> None:
black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
- def test_function_trailing_comma_wip(self) -> None:
- source, expected = read_data("function_trailing_comma_wip")
- # sys.settrace(tracefunc)
- actual = fs(source)
- # sys.settrace(None)
+ def _test_wip(self) -> None:
+ source, expected = read_data("wip")
+ sys.settrace(tracefunc)
+ mode = replace(
+ DEFAULT_MODE,
+ experimental_string_processing=False,
+ target_versions={black.TargetVersion.PY38},
+ )
+ actual = fs(source, mode=mode)
+ sys.settrace(None)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, black.FileMode())
@@ -2085,6 +2090,7 @@ def tracefunc(frame: types.FrameType, event: str, arg: Any) -> Callable:
return tracefunc
stack = len(inspect.stack()) - 19
+ stack *= 2
filename = frame.f_code.co_filename
lineno = frame.f_lineno
func_sig_lineno = lineno - 1
From 9270a10f6f59f069eb14ffba0c75f58e5895b27c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Tue, 25 Aug 2020 22:26:13 +0200
Subject: [PATCH 038/680] Improve docstring re-indentation handling
This addresses a few crashers, namely:
* producing non-equivalent code due to mangling escaped newlines,
* invalid hugging quote characters in the docstring body to the docstring outer
triple quotes (causing a quadruple quote which is a syntax error),
* lack of handling for docstrings that start on the same line as the `def`, and
* invalid stripping of outer triple quotes when the docstring contained
a string prefix.
As a bonus, tests now also run when string normalization is disabled.
---
src/black/__init__.py | 42 +++++++++++++++++++++++------
tests/data/docstring.py | 59 +++++++++++++++++++++++++++++++++++++++++
tests/test_black.py | 5 ++++
3 files changed, 98 insertions(+), 8 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index e37caa98a2c..c3c8c207cd4 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -2037,13 +2037,20 @@ def visit_factor(self, node: Node) -> Iterator[Line]:
yield from self.visit_default(node)
def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
- # Check if it's a docstring
- if prev_siblings_are(
- leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt]
- ) and is_multiline_string(leaf):
- prefix = " " * self.current_line.depth
- docstring = fix_docstring(leaf.value[3:-3], prefix)
- leaf.value = leaf.value[0:3] + docstring + leaf.value[-3:]
+ if is_docstring(leaf) and "\\\n" not in leaf.value:
+ # We're ignoring docstrings with backslash newline escapes because changing
+ # indentation of those changes the AST representation of the code.
+ prefix = get_string_prefix(leaf.value)
+ lead_len = len(prefix) + 3
+ tail_len = -3
+ indent = " " * 4 * self.current_line.depth
+ docstring = fix_docstring(leaf.value[lead_len:tail_len], indent)
+ if docstring:
+ if leaf.value[lead_len - 1] == docstring[0]:
+ docstring = " " + docstring
+ if leaf.value[tail_len + 1] == docstring[-1]:
+ docstring = docstring + " "
+ leaf.value = leaf.value[0:lead_len] + docstring + leaf.value[tail_len:]
normalize_string_quotes(leaf)
yield from self.visit_default(leaf)
@@ -6608,6 +6615,26 @@ def patched_main() -> None:
main()
+def is_docstring(leaf: Leaf) -> bool:
+ if not is_multiline_string(leaf):
+ # For the purposes of docstring re-indentation, we don't need to do anything
+ # with single-line docstrings.
+ return False
+
+ if prev_siblings_are(
+ leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt]
+ ):
+ return True
+
+ # Multiline docstring on the same line as the `def`.
+ if prev_siblings_are(leaf.parent, [syms.parameters, token.COLON, syms.simple_stmt]):
+ # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python
+ # grammar. We're safe to return True without further checks.
+ return True
+
+ return False
+
+
def fix_docstring(docstring: str, prefix: str) -> str:
# https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation
if not docstring:
@@ -6631,7 +6658,6 @@ def fix_docstring(docstring: str, prefix: str) -> str:
trimmed.append(prefix + stripped_line)
else:
trimmed.append("")
- # Return a single string:
return "\n".join(trimmed)
diff --git a/tests/data/docstring.py b/tests/data/docstring.py
index fcb8eb12a78..2d3d73a101c 100644
--- a/tests/data/docstring.py
+++ b/tests/data/docstring.py
@@ -81,6 +81,35 @@ def single_line():
"""
pass
+
+def this():
+ r"""
+ 'hey ho'
+ """
+
+
+def that():
+ """ "hey yah" """
+
+
+def and_that():
+ """
+ "hey yah" """
+
+
+def and_this():
+ '''
+ "hey yah"'''
+
+
+def believe_it_or_not_this_is_in_the_py_stdlib(): '''
+"hey yah"'''
+
+
+def ignored_docstring():
+ """a => \
+b"""
+
# output
class MyClass:
@@ -164,3 +193,33 @@ def over_indent():
def single_line():
"""But with a newline after it!"""
pass
+
+
+def this():
+ r"""
+ 'hey ho'
+ """
+
+
+def that():
+ """ "hey yah" """
+
+
+def and_that():
+ """
+ "hey yah" """
+
+
+def and_this():
+ '''
+ "hey yah"'''
+
+
+def believe_it_or_not_this_is_in_the_py_stdlib():
+ '''
+ "hey yah"'''
+
+
+def ignored_docstring():
+ """a => \
+b"""
\ No newline at end of file
diff --git a/tests/test_black.py b/tests/test_black.py
index 6705490ea13..cf311f52e14 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -496,6 +496,11 @@ def test_docstring(self) -> None:
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
+ mode = replace(DEFAULT_MODE, string_normalization=False)
+ not_normalized = fs(source, mode=mode)
+ self.assertFormatEqual(expected, not_normalized)
+ black.assert_equivalent(source, not_normalized)
+ black.assert_stable(source, not_normalized, mode=mode)
def test_long_strings(self) -> None:
"""Tests for splitting long strings."""
From 4a065a43e1f641a8c5e9266d77feba810e8905ac Mon Sep 17 00:00:00 2001
From: Ned Twigg
Date: Tue, 25 Aug 2020 18:54:05 -0700
Subject: [PATCH 039/680] Add links regarding Spotless integration for
gradle/maven users (#1622)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Łukasz Langa
---
docs/editor_integration.md | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/docs/editor_integration.md b/docs/editor_integration.md
index eb83a1a4b43..73107d6a4a1 100644
--- a/docs/editor_integration.md
+++ b/docs/editor_integration.md
@@ -255,6 +255,10 @@ Sublime Text, Visual Studio Code and many more), you can use the
Use [python-black](https://atom.io/packages/python-black).
+## Gradle (the build tool)
+
+Use the [Spotless](https://github.com/diffplug/spotless/tree/main/plugin-gradle) plugin.
+
## Kakoune
Add the following hook to your kakrc, then run _Black_ with `:format`.
@@ -269,9 +273,9 @@ hook global WinSetOption filetype=python %{
Use [Thonny-black-code-format](https://github.com/Franccisco/thonny-black-code-format).
-## Other editors
+## Other integrations
-Other editors will require external contributions.
+Other editors and tools will require external contributions.
Patches welcome! ✨ 🍰 ✨
From 89b776678a9953e08d2d6ae35f577789be9dde00 Mon Sep 17 00:00:00 2001
From: Cooper Lees
Date: Tue, 25 Aug 2020 21:55:05 -0700
Subject: [PATCH 040/680] Primer update config - enable pytest (#1626)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Reformatted projects I have acceess to:
- aioexabgp
- bandersnatch
- flake8-bugbear
```
-- primer results 📊 --
13 / 16 succeeded (81.25%) ✅
0 / 16 FAILED (0.0%) 💩
- 3 projects disabled by config
- 0 projects skipped due to Python version
- 0 skipped due to long checkout
```
* Also re-enable pytest
```
-- primer results 📊 --
14 / 16 succeeded (87.5%) ✅
0 / 16 FAILED (0.0%) 💩
- 2 projects disabled by config
- 0 projects skipped due to Python version
- 0 skipped due to long checkout
real 2m26.207s
user 17m55.404s
sys 0m43.061s
```
---
src/black_primer/primer.json | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index 83a9cb50161..546f47782cd 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -3,7 +3,7 @@
"projects": {
"aioexabgp": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/cooperlees/aioexabgp.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -17,7 +17,7 @@
},
"bandersnatch": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/pypa/bandersnatch.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -30,7 +30,7 @@
"py_versions": ["all"]
},
"django": {
- "disabled_reason": "black --check --diff returned 123",
+ "disabled_reason": "black --check --diff returned 123 on two files",
"disabled": true,
"cli_arguments": [],
"expect_formatting_changes": true,
@@ -40,7 +40,7 @@
},
"flake8-bugbear": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/PyCQA/flake8-bugbear.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -53,10 +53,10 @@
"py_versions": ["all"]
},
"pandas": {
- "disabled_reason": "black --check --diff returned 123",
+ "disabled_reason": "black --check --diff returned 123 on one file",
"disabled": true,
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/pandas-dev/pandas.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -83,10 +83,8 @@
"py_versions": ["all"]
},
"pytest": {
- "disabled_reason": "black --check --diff returned 123",
- "disabled": true,
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/pytest-dev/pytest.git",
"long_checkout": false,
"py_versions": ["all"]
From 824d06f7204d36fc1afcf09a090c4e418e3d4cfc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Wed, 26 Aug 2020 16:00:55 +0200
Subject: [PATCH 041/680] v20.8b0
---
CHANGES.md | 2 +-
docs/change_log.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index d36d8463a45..b475e90616e 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,6 @@
## Change Log
-### Unreleased
+### 20.8b0
#### _Black_
diff --git a/docs/change_log.md b/docs/change_log.md
index 5b7f08e101d..1219893899a 100644
--- a/docs/change_log.md
+++ b/docs/change_log.md
@@ -2,7 +2,7 @@
## Change Log
-### Unreleased
+### 20.8b0
#### _Black_
From d7aa7f3cdd1e832204cd63a574a8935157e18de7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Wed, 26 Aug 2020 12:22:56 +0200
Subject: [PATCH 042/680] Treat all trailing commas as pre-existing, as they
effectively are
On a second pass of Black on the same file, inserted trailing commas are now
pre-existing. Doesn't make sense to differentiate between the passes then.
---
src/black/__init__.py | 27 +++++++++------------------
1 file changed, 9 insertions(+), 18 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index c3c8c207cd4..4d4f4b73b91 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -1627,14 +1627,13 @@ def contains_multiline_strings(self) -> bool:
def maybe_should_explode(self, closing: Leaf) -> bool:
"""Return True if this line should explode (always be split), that is when:
- - there's a pre-existing trailing comma here; and
+ - there's a trailing comma here; and
- it's not a one-tuple.
"""
if not (
closing.type in CLOSING_BRACKETS
and self.leaves
and self.leaves[-1].type == token.COMMA
- and not self.leaves[-1].was_checked # pre-existing
):
return False
@@ -2661,7 +2660,7 @@ def rhs(line: Line, features: Collection[Feature]) -> Iterator[Line]:
# All splits failed, best effort split with no omits.
# This mostly happens to multiline strings that are by definition
# reported as not fitting a single line, as well as lines that contain
- # pre-existing trailing commas (those have to be exploded).
+ # trailing commas (those have to be exploded).
yield from right_hand_split(
line, line_length=mode.line_length, features=features
)
@@ -4855,7 +4854,6 @@ def bracket_split_build_line(
if leaves[i].type != token.COMMA:
new_comma = Leaf(token.COMMA, ",")
- new_comma.was_checked = True
leaves.insert(i + 1, new_comma)
break
@@ -4951,7 +4949,6 @@ def append_to_line(leaf: Leaf) -> Iterator[Line]:
and current_line.leaves[-1].type != STANDALONE_COMMENT
):
new_comma = Leaf(token.COMMA, ",")
- new_comma.was_checked = True
current_line.append(new_comma)
yield current_line
@@ -5584,20 +5581,20 @@ def should_split_body_explode(line: Line, opening_bracket: Leaf) -> bool:
# than one of them (we're excluding the trailing comma and if the delimiter priority
# is still commas, that means there's more).
exclude = set()
- pre_existing_trailing_comma = False
+ trailing_comma = False
try:
last_leaf = line.leaves[-1]
if last_leaf.type == token.COMMA:
- pre_existing_trailing_comma = not last_leaf.was_checked
+ trailing_comma = True
exclude.add(id(last_leaf))
max_priority = line.bracket_tracker.max_delimiter_priority(exclude=exclude)
except (IndexError, ValueError):
return False
return max_priority == COMMA_PRIORITY and (
+ trailing_comma
# always explode imports
- opening_bracket.parent.type in {syms.atom, syms.import_from}
- or pre_existing_trailing_comma
+ or opening_bracket.parent.type in {syms.atom, syms.import_from}
)
@@ -5727,12 +5724,11 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[Leaf
line.should_explode
and prev
and prev.type == token.COMMA
- and not prev.was_checked
and not is_one_tuple_between(
leaf.opening_bracket, leaf, line.leaves
)
):
- # Never omit bracket pairs with pre-existing trailing commas.
+ # Never omit bracket pairs with trailing commas.
# We need to explode on those.
break
@@ -5756,10 +5752,9 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[Set[Leaf
line.should_explode
and prev
and prev.type == token.COMMA
- and not prev.was_checked
and not is_one_tuple_between(leaf.opening_bracket, leaf, line.leaves)
):
- # Never omit bracket pairs with pre-existing trailing commas.
+ # Never omit bracket pairs with trailing commas.
# We need to explode on those.
break
@@ -6412,11 +6407,7 @@ def can_omit_invisible_parens(
# unnecessary.
return True
- if (
- line.should_explode
- and penultimate.type == token.COMMA
- and not penultimate.was_checked
- ):
+ if line.should_explode and penultimate.type == token.COMMA:
# The rightmost non-omitted bracket pair is the one we want to explode on.
return True
From 30a332c32fabe13d883d86b0422bcded0c91642f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Wed, 26 Aug 2020 12:57:05 +0200
Subject: [PATCH 043/680] Include mode information for unstable formattings
---
src/black/__init__.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 4d4f4b73b91..200e31fd458 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -6163,6 +6163,7 @@ def assert_stable(src: str, dst: str, mode: Mode) -> None:
newdst = format_str(dst, mode=mode)
if dst != newdst:
log = dump_to_file(
+ str(mode),
diff(src, dst, "source", "first pass"),
diff(dst, newdst, "first pass", "second pass"),
)
From ceeb1d9a2ee08190704076f616e74a3cdd5e10c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Wed, 26 Aug 2020 12:57:38 +0200
Subject: [PATCH 044/680] Add expected failure tests with the unstable
formattings
---
tests/data/trailing_comma_optional_parens1.py | 3 +++
tests/data/trailing_comma_optional_parens2.py | 3 +++
tests/data/trailing_comma_optional_parens3.py | 8 +++++++
tests/test_black.py | 21 +++++++++++++++++++
4 files changed, 35 insertions(+)
create mode 100644 tests/data/trailing_comma_optional_parens1.py
create mode 100644 tests/data/trailing_comma_optional_parens2.py
create mode 100644 tests/data/trailing_comma_optional_parens3.py
diff --git a/tests/data/trailing_comma_optional_parens1.py b/tests/data/trailing_comma_optional_parens1.py
new file mode 100644
index 00000000000..5ad29a8affd
--- /dev/null
+++ b/tests/data/trailing_comma_optional_parens1.py
@@ -0,0 +1,3 @@
+if e1234123412341234.winerror not in (_winapi.ERROR_SEM_TIMEOUT,
+ _winapi.ERROR_PIPE_BUSY) or _check_timeout(t):
+ pass
\ No newline at end of file
diff --git a/tests/data/trailing_comma_optional_parens2.py b/tests/data/trailing_comma_optional_parens2.py
new file mode 100644
index 00000000000..2817073816e
--- /dev/null
+++ b/tests/data/trailing_comma_optional_parens2.py
@@ -0,0 +1,3 @@
+if (e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') or
+ (8, 5, 8) <= get_tk_patchlevel() < (8, 6)):
+ pass
\ No newline at end of file
diff --git a/tests/data/trailing_comma_optional_parens3.py b/tests/data/trailing_comma_optional_parens3.py
new file mode 100644
index 00000000000..e6a673ec537
--- /dev/null
+++ b/tests/data/trailing_comma_optional_parens3.py
@@ -0,0 +1,8 @@
+if True:
+ if True:
+ if True:
+ return _(
+ "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas "
+ + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.",
+ "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe",
+ ) % {"reported_username": reported_username, "report_reason": report_reason}
\ No newline at end of file
diff --git a/tests/test_black.py b/tests/test_black.py
index cf311f52e14..f5d4e1115a8 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -368,6 +368,27 @@ def test_function_trailing_comma(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
+ @unittest.expectedFailure
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_trailing_comma_optional_parens_stability1(self) -> None:
+ source, _expected = read_data("trailing_comma_optional_parens1")
+ actual = fs(source)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @unittest.expectedFailure
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_trailing_comma_optional_parens_stability2(self) -> None:
+ source, _expected = read_data("trailing_comma_optional_parens2")
+ actual = fs(source)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @unittest.expectedFailure
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_trailing_comma_optional_parens_stability3(self) -> None:
+ source, _expected = read_data("trailing_comma_optional_parens3")
+ actual = fs(source)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
@patch("black.dump_to_file", dump_to_stderr)
def test_expression(self) -> None:
source, expected = read_data("expression")
From 3ae83c3090954c45b805010bc04354c2fbe3f2aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Wed, 26 Aug 2020 17:15:20 +0200
Subject: [PATCH 045/680] Make dependency on Click 7.0, regex 2020.1.8, and
toml 0.10.1 explicit
---
Pipfile | 4 ++--
Pipfile.lock | 53 ++++++++++++++++++++++++++--------------------------
setup.py | 5 ++---
3 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/Pipfile b/Pipfile
index 18e8a545617..44f57f6773c 100644
--- a/Pipfile
+++ b/Pipfile
@@ -24,10 +24,10 @@ black = {editable = true, extras = ["d"], path = "."}
aiohttp = ">=3.3.2"
aiohttp-cors = "*"
appdirs = "*"
-click = ">=6.5"
+click = ">=7.0"
mypy_extensions = ">=0.4.3"
pathspec = ">=0.6"
-regex = ">=2019.8"
+regex = ">=2020.1.8"
toml = ">=0.10.1"
typed-ast = "==1.4.0"
typing_extensions = ">=3.7.4"
diff --git a/Pipfile.lock b/Pipfile.lock
index ddbf9b99520..32b8012ff0e 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "682054eb4a3d4366e2f76b3ae74286d156a270c0d7b57299a81f8cc1d0a51d19"
+ "sha256": "61d09a6b8a8c310becd5e108ed08e0eeae50c7323c08c8040367abded0cb1031"
},
"pipfile-spec": 6,
"requires": {},
@@ -186,10 +186,10 @@
},
"toml": {
"hashes": [
- "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
+ "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
+ "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"index": "pypi",
"version": "==0.10.1"
@@ -222,12 +222,12 @@
},
"typing-extensions": {
"hashes": [
- "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5",
- "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae",
- "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"
+ "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
+ "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
+ "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
],
"index": "pypi",
- "version": "==3.7.4.2"
+ "version": "==3.7.4.3"
},
"yarl": {
"hashes": [
@@ -468,11 +468,11 @@
},
"identify": {
"hashes": [
- "sha256:69c4769f085badafd0e04b1763e847258cbbf6d898e8678ebffc91abdb86f6c6",
- "sha256:d6ae6daee50ba1b493e9ca4d36a5edd55905d2cf43548fdc20b2a14edef102e7"
+ "sha256:9f5fcf22b665eaece583bd395b103c2769772a0f646ffabb5b1f155901b07de2",
+ "sha256:b1aa2e05863dc80242610d46a7b49105e2eafe00ef0c8ff311c1828680760c76"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==1.4.28"
+ "version": "==1.4.29"
},
"idna": {
"hashes": [
@@ -500,11 +500,11 @@
},
"keyring": {
"hashes": [
- "sha256:22df6abfed49912fc560806030051067fba9f0069cffa79da72899aeea4ccbd5",
- "sha256:e7a17caf40c40b6bb8c4772224a487e4a63013560ed0c521065aeba7ecd42182"
+ "sha256:182f94fc0381546489e3e4d90384a8c1d43cc09ffe2eb4a826e7312df6e1be7c",
+ "sha256:cd4d486803d55bdb13e2d453eb61dbbc984773e4f2b98a455aa85b1f4bc421e4"
],
"markers": "python_version >= '3.6'",
- "version": "==21.3.0"
+ "version": "==21.3.1"
},
"markupsafe": {
"hashes": [
@@ -605,9 +605,10 @@
},
"nodeenv": {
"hashes": [
- "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"
+ "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9",
+ "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"
],
- "version": "==1.4.0"
+ "version": "==1.5.0"
},
"packaging": {
"hashes": [
@@ -634,11 +635,11 @@
},
"pre-commit": {
"hashes": [
- "sha256:1657663fdd63a321a4a739915d7d03baedd555b25054449090f97bb0cb30a915",
- "sha256:e8b1315c585052e729ab7e99dcca5698266bedce9067d21dc909c23e3ceed626"
+ "sha256:810aef2a2ba4f31eed1941fc270e72696a1ad5590b9751839c90807d0fff6b9a",
+ "sha256:c54fd3e574565fe128ecc5e7d2f91279772ddb03f8729645fa812fe809084a70"
],
"index": "pypi",
- "version": "==2.6.0"
+ "version": "==2.7.1"
},
"pycodestyle": {
"hashes": [
@@ -847,10 +848,10 @@
},
"toml": {
"hashes": [
- "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
+ "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
+ "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"index": "pypi",
"version": "==0.10.1"
@@ -899,12 +900,12 @@
},
"typing-extensions": {
"hashes": [
- "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5",
- "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae",
- "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"
+ "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
+ "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
+ "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
],
"index": "pypi",
- "version": "==3.7.4.2"
+ "version": "==3.7.4.3"
},
"urllib3": {
"hashes": [
diff --git a/setup.py b/setup.py
index bff439c6d8c..12fde2568cf 100644
--- a/setup.py
+++ b/setup.py
@@ -68,10 +68,9 @@ def get_long_description() -> str:
python_requires=">=3.6",
zip_safe=False,
install_requires=[
- "click>=6.5",
- "attrs>=18.1.0",
+ "click>=7.1.2",
"appdirs",
- "toml>=0.9.4",
+ "toml>=0.10.1",
"typed-ast>=1.4.0",
"regex>=2020.1.8",
"pathspec>=0.6, <1",
From 235412635e91950c8ef2d9ebe777f97fffd4f01d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Wed, 26 Aug 2020 17:50:44 +0200
Subject: [PATCH 046/680] v20.8b1
---
CHANGES.md | 7 +++++++
docs/change_log.md | 7 +++++++
2 files changed, 14 insertions(+)
diff --git a/CHANGES.md b/CHANGES.md
index b475e90616e..11341779f58 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,12 @@
## Change Log
+### 20.8b1
+
+#### _Packaging_
+
+- explicitly depend on Click 7.1.2 or newer as `Black` no longer works with versions
+ older than 7.0
+
### 20.8b0
#### _Black_
diff --git a/docs/change_log.md b/docs/change_log.md
index 1219893899a..658414bf967 100644
--- a/docs/change_log.md
+++ b/docs/change_log.md
@@ -2,6 +2,13 @@
## Change Log
+### 20.8b1
+
+#### _Packaging_
+
+- explicitly depend on Click 7.1.2 or newer as `Black` no longer works with versions
+ older than 7.0
+
### 20.8b0
#### _Black_
From 20f74c20f7efe80f0b0199153934dddd80e21d8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Wed, 26 Aug 2020 18:18:14 +0200
Subject: [PATCH 047/680] Stop running Primer on macOS as it's flaky on GitHub
Actions
---
.github/workflows/primer.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/primer.yml b/.github/workflows/primer.yml
index b5dea5e7139..9b10db0d285 100644
--- a/.github/workflows/primer.yml
+++ b/.github/workflows/primer.yml
@@ -9,7 +9,7 @@ jobs:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8]
- os: [ubuntu-latest, macOS-latest, windows-latest]
+ os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
From 1ebe9b70c5624da964364841e30a2e3ffe109c4e Mon Sep 17 00:00:00 2001
From: Yurii Karabas <1998uriyyo@gmail.com>
Date: Wed, 24 Jun 2020 00:33:05 +0300
Subject: [PATCH 048/680] Simplify black code by using generator expressions
---
src/black/__init__.py | 44 +++++++++++++++++++++----------------------
1 file changed, 21 insertions(+), 23 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 200e31fd458..954b93c77d7 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -641,10 +641,9 @@ def path_empty(
"""
Exit if there is no `src` provided for formatting
"""
- if len(src) == 0:
- if verbose or not quiet:
- out(msg)
- ctx.exit(0)
+ if not src and (verbose or not quiet):
+ out(msg)
+ ctx.exit(0)
def reformat_one(
@@ -928,7 +927,7 @@ def format_file_contents(src_contents: str, *, fast: bool, mode: Mode) -> FileCo
valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it.
`mode` is passed to :func:`format_str`.
"""
- if src_contents.strip() == "":
+ if not src_contents.strip():
raise NothingChanged
dst_contents = format_str(src_contents, mode=mode)
@@ -1062,7 +1061,7 @@ def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]:
def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) -> Node:
"""Given a string with source, return the lib2to3 Node."""
- if src_txt[-1:] != "\n":
+ if not src_txt.endswith("\n"):
src_txt += "\n"
for grammar in get_grammars(set(target_versions)):
@@ -1547,11 +1546,10 @@ def is_triple_quoted_string(self) -> bool:
def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool:
"""If so, needs to be split before emitting."""
- for leaf in self.leaves:
- if leaf.type == STANDALONE_COMMENT and leaf.bracket_depth <= depth_limit:
- return True
-
- return False
+ return any(
+ leaf.type == STANDALONE_COMMENT and leaf.bracket_depth <= depth_limit
+ for leaf in self.leaves
+ )
def contains_uncollapsable_type_comments(self) -> bool:
ignored_ids = set()
@@ -3992,12 +3990,13 @@ class StringParenWrapper(CustomSplitMapMixin, BaseStringSplitter):
def do_splitter_match(self, line: Line) -> TMatchResult:
LL = line.leaves
- string_idx = None
- string_idx = string_idx or self._return_match(LL)
- string_idx = string_idx or self._else_match(LL)
- string_idx = string_idx or self._assert_match(LL)
- string_idx = string_idx or self._assign_match(LL)
- string_idx = string_idx or self._dict_match(LL)
+ string_idx = (
+ self._return_match(LL)
+ or self._else_match(LL)
+ or self._assert_match(LL)
+ or self._assign_match(LL)
+ or self._dict_match(LL)
+ )
if string_idx is not None:
string_value = line.leaves[string_idx].value
@@ -4196,7 +4195,7 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]:
is_valid_index = is_valid_index_factory(LL)
insert_str_child = insert_str_child_factory(LL[string_idx])
- comma_idx = len(LL) - 1
+ comma_idx = -1
ends_with_comma = False
if LL[comma_idx].type == token.COMMA:
ends_with_comma = True
@@ -4444,11 +4443,10 @@ def contains_pragma_comment(comment_list: List[Leaf]) -> bool:
of the more common static analysis tools for python (e.g. mypy, flake8,
pylint).
"""
- for comment in comment_list:
- if comment.value.startswith(("# type:", "# noqa", "# pylint:")):
- return True
-
- return False
+ return any(
+ comment.value.startswith(("# type:", "# noqa", "# pylint:"))
+ for comment in comment_list
+ )
def insert_str_child_factory(string_leaf: Leaf) -> Callable[[LN], None]:
From 4ca92ac91c944b252caf8c2265e5854f839a66f4 Mon Sep 17 00:00:00 2001
From: Yurii Karabas <1998uriyyo@gmail.com>
Date: Wed, 26 Aug 2020 09:35:21 +0300
Subject: [PATCH 049/680] Revert contains_standalone_comments function changes
---
src/black/__init__.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 954b93c77d7..96dd0e46e7b 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -1546,10 +1546,11 @@ def is_triple_quoted_string(self) -> bool:
def contains_standalone_comments(self, depth_limit: int = sys.maxsize) -> bool:
"""If so, needs to be split before emitting."""
- return any(
- leaf.type == STANDALONE_COMMENT and leaf.bracket_depth <= depth_limit
- for leaf in self.leaves
- )
+ for leaf in self.leaves:
+ if leaf.type == STANDALONE_COMMENT and leaf.bracket_depth <= depth_limit:
+ return True
+
+ return False
def contains_uncollapsable_type_comments(self) -> bool:
ignored_ids = set()
From e268cbaf668cc86b1e6aa52e52483cf4592e176c Mon Sep 17 00:00:00 2001
From: Yurii Karabas <1998uriyyo@gmail.com>
Date: Wed, 26 Aug 2020 13:43:00 +0300
Subject: [PATCH 050/680] Revert contains_pragma_comment function changes
---
src/black/__init__.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 96dd0e46e7b..f51a50d3612 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -4444,10 +4444,11 @@ def contains_pragma_comment(comment_list: List[Leaf]) -> bool:
of the more common static analysis tools for python (e.g. mypy, flake8,
pylint).
"""
- return any(
- comment.value.startswith(("# type:", "# noqa", "# pylint:"))
- for comment in comment_list
- )
+ for comment in comment_list:
+ if comment.value.startswith(("# type:", "# noqa", "# pylint:")):
+ return True
+
+ return False
def insert_str_child_factory(string_leaf: Leaf) -> Callable[[LN], None]:
From 5f78401b2863c4c2e8417905fa8b8bf4d648510d Mon Sep 17 00:00:00 2001
From: Rowan Rodrik van der Molen
Date: Sat, 11 Jul 2020 21:20:31 +0200
Subject: [PATCH 051/680] Fix g:black_fast and
g:black_(skip)_string_normalization opts
---
plugin/black.vim | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/plugin/black.vim b/plugin/black.vim
index 4af044e7a68..3dd3f2151c3 100644
--- a/plugin/black.vim
+++ b/plugin/black.vim
@@ -41,8 +41,12 @@ endif
if !exists("g:black_linelength")
let g:black_linelength = 88
endif
-if !exists("g:black_skip_string_normalization")
- let g:black_skip_string_normalization = 0
+if !exists("g:black_string_normalization")
+ if exists("g:black_skip_string_normalization")
+ let g:black_string_normalization = !g:black_skip_string_normalization
+ else
+ let g:black_string_normalization = 1
+ endif
endif
python3 << EndPython3
@@ -50,6 +54,7 @@ import collections
import os
import sys
import vim
+from distutils.util import strtobool
class Flag(collections.namedtuple("FlagBase", "name, cast")):
@@ -62,15 +67,13 @@ class Flag(collections.namedtuple("FlagBase", "name, cast")):
name = self.var_name
if name == "line_length":
name = name.replace("_", "")
- if name == "string_normalization":
- name = "skip_" + name
return "g:black_" + name
FLAGS = [
Flag(name="line_length", cast=int),
- Flag(name="fast", cast=bool),
- Flag(name="string_normalization", cast=bool),
+ Flag(name="fast", cast=strtobool),
+ Flag(name="string_normalization", cast=strtobool),
]
From 7fe19fac5bccc08dbcf21c293ab50f1fcda7716f Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Wed, 26 Aug 2020 18:17:59 -0400
Subject: [PATCH 052/680] Fix multiline docstring quote normalization
The quotes of multiline docstrings are now only normalized when string
normalization is off, instead of the string normalization setting being
ignored and the quotes being *always* normalized.
I had to make a new test case and data file since the current pair for
docstrings only worked when there is no formatting difference between the
formatting results with string normalization on and off. I needed to add
tests for when there *are* differences between the two. So I split
test_docstring's test code when string normalization is disabled into a
new test case along with a new data file.
---
CHANGES.md | 7 +
docs/change_log.md | 7 +
src/black/__init__.py | 1 -
.../data/docstring_no_string_normalization.py | 209 ++++++++++++++++++
tests/test_black.py | 13 +-
5 files changed, 232 insertions(+), 5 deletions(-)
create mode 100644 tests/data/docstring_no_string_normalization.py
diff --git a/CHANGES.md b/CHANGES.md
index 11341779f58..7352b857075 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,12 @@
## Change Log
+### Unreleased
+
+#### _Black_
+
+- `Black` now respects `--skip-string-normalization` when normalizing multiline
+ docstring quotes (#1637)
+
### 20.8b1
#### _Packaging_
diff --git a/docs/change_log.md b/docs/change_log.md
index 658414bf967..b7337166659 100644
--- a/docs/change_log.md
+++ b/docs/change_log.md
@@ -2,6 +2,13 @@
## Change Log
+### Unreleased
+
+#### _Black_
+
+- `Black` now respects `--skip-string-normalization` when normalizing multiline
+ docstring quotes (#1637)
+
### 20.8b1
#### _Packaging_
diff --git a/src/black/__init__.py b/src/black/__init__.py
index f51a50d3612..34d8145c0f7 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -2049,7 +2049,6 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
if leaf.value[tail_len + 1] == docstring[-1]:
docstring = docstring + " "
leaf.value = leaf.value[0:lead_len] + docstring + leaf.value[tail_len:]
- normalize_string_quotes(leaf)
yield from self.visit_default(leaf)
diff --git a/tests/data/docstring_no_string_normalization.py b/tests/data/docstring_no_string_normalization.py
new file mode 100644
index 00000000000..0457fcf114f
--- /dev/null
+++ b/tests/data/docstring_no_string_normalization.py
@@ -0,0 +1,209 @@
+class ALonelyClass:
+ '''
+ A multiline class docstring.
+ '''
+ def AnEquallyLonelyMethod(self):
+ '''
+ A multiline method docstring'''
+ pass
+
+
+def one_function():
+ '''This is a docstring with a single line of text.'''
+ pass
+
+
+def shockingly_the_quotes_are_normalized():
+ '''This is a multiline docstring.
+ This is a multiline docstring.
+ This is a multiline docstring.
+ '''
+ pass
+
+
+def foo():
+ """This is a docstring with
+ some lines of text here
+ """
+ return
+
+
+def baz():
+ '''"This" is a string with some
+ embedded "quotes"'''
+ return
+
+
+def poit():
+ """
+ Lorem ipsum dolor sit amet.
+
+ Consectetur adipiscing elit:
+ - sed do eiusmod tempor incididunt ut labore
+ - dolore magna aliqua
+ - enim ad minim veniam
+ - quis nostrud exercitation ullamco laboris nisi
+ - aliquip ex ea commodo consequat
+ """
+ pass
+
+
+def under_indent():
+ """
+ These lines are indented in a way that does not
+make sense.
+ """
+ pass
+
+
+def over_indent():
+ """
+ This has a shallow indent
+ - But some lines are deeper
+ - And the closing quote is too deep
+ """
+ pass
+
+
+def single_line():
+ """But with a newline after it!
+
+ """
+ pass
+
+
+def this():
+ r"""
+ 'hey ho'
+ """
+
+
+def that():
+ """ "hey yah" """
+
+
+def and_that():
+ """
+ "hey yah" """
+
+
+def and_this():
+ '''
+ "hey yah"'''
+
+
+def believe_it_or_not_this_is_in_the_py_stdlib(): '''
+"hey yah"'''
+
+
+def shockingly_the_quotes_are_normalized_v2():
+ '''
+ Docstring Docstring Docstring
+ '''
+ pass
+
+# output
+
+class ALonelyClass:
+ '''
+ A multiline class docstring.
+ '''
+
+ def AnEquallyLonelyMethod(self):
+ '''
+ A multiline method docstring'''
+ pass
+
+
+def one_function():
+ '''This is a docstring with a single line of text.'''
+ pass
+
+
+def shockingly_the_quotes_are_normalized():
+ '''This is a multiline docstring.
+ This is a multiline docstring.
+ This is a multiline docstring.
+ '''
+ pass
+
+
+def foo():
+ """This is a docstring with
+ some lines of text here
+ """
+ return
+
+
+def baz():
+ '''"This" is a string with some
+ embedded "quotes"'''
+ return
+
+
+def poit():
+ """
+ Lorem ipsum dolor sit amet.
+
+ Consectetur adipiscing elit:
+ - sed do eiusmod tempor incididunt ut labore
+ - dolore magna aliqua
+ - enim ad minim veniam
+ - quis nostrud exercitation ullamco laboris nisi
+ - aliquip ex ea commodo consequat
+ """
+ pass
+
+
+def under_indent():
+ """
+ These lines are indented in a way that does not
+ make sense.
+ """
+ pass
+
+
+def over_indent():
+ """
+ This has a shallow indent
+ - But some lines are deeper
+ - And the closing quote is too deep
+ """
+ pass
+
+
+def single_line():
+ """But with a newline after it!"""
+ pass
+
+
+def this():
+ r"""
+ 'hey ho'
+ """
+
+
+def that():
+ """ "hey yah" """
+
+
+def and_that():
+ """
+ "hey yah" """
+
+
+def and_this():
+ '''
+ "hey yah"'''
+
+
+def believe_it_or_not_this_is_in_the_py_stdlib():
+ '''
+ "hey yah"'''
+
+
+def shockingly_the_quotes_are_normalized_v2():
+ '''
+ Docstring Docstring Docstring
+ '''
+ pass
diff --git a/tests/test_black.py b/tests/test_black.py
index f5d4e1115a8..629afc5b0ad 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -517,11 +517,16 @@ def test_docstring(self) -> None:
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_docstring_no_string_normalization(self) -> None:
+ """Like test_docstring but with string normalization off."""
+ source, expected = read_data("docstring_no_string_normalization")
mode = replace(DEFAULT_MODE, string_normalization=False)
- not_normalized = fs(source, mode=mode)
- self.assertFormatEqual(expected, not_normalized)
- black.assert_equivalent(source, not_normalized)
- black.assert_stable(source, not_normalized, mode=mode)
+ actual = fs(source, mode=mode)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, mode)
def test_long_strings(self) -> None:
"""Tests for splitting long strings."""
From 2b75f8870eac943c93e93c740c9d2ef74efeeb41 Mon Sep 17 00:00:00 2001
From: mbarkhau
Date: Thu, 27 Aug 2020 11:47:59 +0000
Subject: [PATCH 053/680] fix 1631 and add test (#1641)
---
CHANGES.md | 2 ++
src/black/__init__.py | 3 ++-
tests/test_black.py | 18 ++++++++++++++++++
3 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/CHANGES.md b/CHANGES.md
index 7352b857075..1c53604d4d5 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,8 @@
- `Black` now respects `--skip-string-normalization` when normalizing multiline
docstring quotes (#1637)
+- fixed a crash when PWD=/ on POSIX (#1631)
+
### 20.8b1
#### _Packaging_
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 34d8145c0f7..048e771ce96 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -5831,7 +5831,8 @@ def normalize_path_maybe_ignore(
`report` is where "path ignored" output goes.
"""
try:
- normalized_path = path.resolve().relative_to(root).as_posix()
+ abspath = path if path.is_absolute() else Path.cwd() / path
+ normalized_path = abspath.resolve().relative_to(root).as_posix()
except OSError as e:
report.path_ignored(path, f"cannot be read because {e}")
return None
diff --git a/tests/test_black.py b/tests/test_black.py
index 629afc5b0ad..bc80c8fca8b 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -9,6 +9,7 @@
from io import BytesIO, TextIOWrapper
import os
from pathlib import Path
+from platform import system
import regex as re
import sys
from tempfile import TemporaryDirectory
@@ -1939,6 +1940,23 @@ def test_find_project_root(self) -> None:
self.assertEqual(black.find_project_root((src_dir,)), src_dir.resolve())
self.assertEqual(black.find_project_root((src_python,)), src_dir.resolve())
+ def test_bpo_33660_workaround(self) -> None:
+ if system() == "Windows":
+ return
+
+ # https://bugs.python.org/issue33660
+
+ old_cwd = Path.cwd()
+ try:
+ root = Path("/")
+ os.chdir(str(root))
+ path = Path("workspace") / "project"
+ report = black.Report(verbose=True)
+ normalized_path = black.normalize_path_maybe_ignore(path, root, report)
+ self.assertEqual(normalized_path, "workspace/project")
+ finally:
+ os.chdir(str(old_cwd))
+
class BlackDTestCase(AioHTTPTestCase):
async def get_application(self) -> web.Application:
From 0c6d4ca0c33aa97202e0e94a4a2389cc432597bc Mon Sep 17 00:00:00 2001
From: Tristan Seligmann
Date: Fri, 28 Aug 2020 17:37:37 +0200
Subject: [PATCH 054/680] Fix typo in comment (#1650)
Co-authored-by: Hugo van Kemenade
---
fuzz.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fuzz.py b/fuzz.py
index fdd4917f2ec..604e6ced291 100644
--- a/fuzz.py
+++ b/fuzz.py
@@ -42,8 +42,8 @@ def test_idempotent_any_syntatically_valid_python(
try:
dst_contents = black.format_str(src_contents, mode=mode)
except black.InvalidInput:
- # This is a bug - if it's valid Python code, as above, black should be
- # able to code with it. See issues #970, #1012, #1358, and #1557.
+ # This is a bug - if it's valid Python code, as above, Black should be
+ # able to cope with it. See issues #970, #1012, #1358, and #1557.
# TODO: remove this try-except block when issues are resolved.
return
From 573b8de54470dbad8bcaaebd5a28dad507c44666 Mon Sep 17 00:00:00 2001
From: Jon Dufresne
Date: Mon, 31 Aug 2020 14:18:43 -0700
Subject: [PATCH 055/680] Remove flake8 W503 from docs as it is ignored by
default (#1661)
Fixes #1660
---
.flake8 | 4 ++--
docs/compatible_configs.md | 20 +++++++-------------
docs/the_black_code_style.md | 10 +++-------
3 files changed, 12 insertions(+), 22 deletions(-)
diff --git a/.flake8 b/.flake8
index cee6db4446b..656c0df24ee 100644
--- a/.flake8
+++ b/.flake8
@@ -1,7 +1,7 @@
[flake8]
-ignore = E203, E266, E501, W503
+extend-ignore = E203, E266, E501
# line length is intentionally set to 80 here because black uses Bugbear
-# See https://github.com/psf/black/blob/master/README.md#line-length for more details
+# See https://github.com/psf/black/blob/master/docs/the_black_code_style.md#line-length for more details
max-line-length = 80
max-complexity = 18
select = B,C,E,F,W,T4,B9
diff --git a/docs/compatible_configs.md b/docs/compatible_configs.md
index 25e959e3281..82f13932b0b 100644
--- a/docs/compatible_configs.md
+++ b/docs/compatible_configs.md
@@ -32,7 +32,7 @@ line_length = 88
_Black_ wraps imports that surpass `line-length` by moving identifiers into their own
indented line. If that still doesn't fit the bill, it will put all of them in separate
lines and put a trailing comma. A more detailed explanation of this behaviour can be
-[found here](https://github.com/psf/black#how-black-wraps-lines).
+[found here](https://github.com/psf/black/blob/master/docs/the_black_code_style.md#how-black-wraps-lines).
isort's default mode of wrapping imports that extend past the `line_length` limit is
"Grid".
@@ -146,21 +146,15 @@ There are a few deviations that cause incompatibilities with _Black_.
```
max-line-length = 88
-extend-ignore = E203, W503
+extend-ignore = E203
```
### Why those options above?
-When breaking a line, _Black_ will break it before a binary operator. This is compliant
-with PEP 8, but this behaviour will cause flake8 to raise
-`W503 line break before binary operator` warnings.
-
In some cases, as determined by PEP 8, _Black_ will enforce an equal amount of
whitespace around slice operators. Due to this, Flake8 will raise
-`E203 whitespace before ':'` warnings.
-
-Since both of these warnings are not PEP 8 compliant, Flake8 should be configured to
-ignore these warnings via `extend-ignore = E203, W503`.
+`E203 whitespace before ':'` warnings. Since this warning is not PEP 8 compliant, Flake8
+should be configured to ignore it via `extend-ignore = E203`.
Also, as like with isort, flake8 should be configured to allow lines up to the length
limit of `88`, _Black_'s default. This explains `max-line-length = 88`.
@@ -173,7 +167,7 @@ limit of `88`, _Black_'s default. This explains `max-line-length = 88`.
```ini
[flake8]
max-line-length = 88
-extend-ignore = E203, W503
+extend-ignore = E203
```
@@ -184,7 +178,7 @@ extend-ignore = E203, W503
```cfg
[flake8]
max-line-length = 88
-extend-ignore = E203, W503
+extend-ignore = E203
```
@@ -195,7 +189,7 @@ extend-ignore = E203, W503
```ini
[flake8]
max-line-length = 88
-extend-ignore = E203, W503
+extend-ignore = E203
```
diff --git a/docs/the_black_code_style.md b/docs/the_black_code_style.md
index 09d58307a05..735e9c015d7 100644
--- a/docs/the_black_code_style.md
+++ b/docs/the_black_code_style.md
@@ -199,12 +199,12 @@ You'd do it like this:
max-line-length = 80
...
select = C,E,F,W,B,B950
-ignore = E203, E501, W503
+extend-ignore = E203, E501
```
You'll find _Black_'s own .flake8 config file is configured like this. Explanation of
-why W503 and E203 are disabled can be found further in this documentation. And if you're
-curious about the reasoning behind B950,
+why E203 is disabled can be found further in this documentation. And if you're curious
+about the reasoning behind B950,
[Bugbear's documentation](https://github.com/PyCQA/flake8-bugbear#opinionated-warnings)
explains it. The tl;dr is "it's like highway speed limits, we won't bother you if you
overdo it by a few km/h".
@@ -309,10 +309,6 @@ multiple lines. This is so that _Black_ is compliant with the recent changes in
[PEP 8](https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-after-a-binary-operator)
style guide, which emphasizes that this approach improves readability.
-This behaviour may raise `W503 line break before binary operator` warnings in style
-guide enforcement tools like Flake8. Since `W503` is not PEP 8 compliant, you should
-tell Flake8 to ignore these warnings.
-
### Slices
PEP 8
From 1d2d7264ec7c448744b771910cc972da03b1cb80 Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Mon, 31 Aug 2020 17:20:05 -0400
Subject: [PATCH 056/680] Fix incorrect space before colon in if/while stmts
(#1655)
* Fix incorrect space before colon in if/while stmts
Previously Black would format this code
```
if (foo := True):
print(foo)
```
as
```
if (foo := True) :
print(foo)
```
adding an incorrect space after the RPAR. Buggy code in the
normalize_invisible_parens function caused the colon to be wrapped in
invisible parentheses. The LPAR of that pair was then prefixed with a
single space at the request of the whitespace function.
This commit fixes the accidental skipping of a pre-condition check
which must return True before parenthesis normalization of a specific
child Leaf or Node can happen. The pre-condition check being skipped
was why the colon was wrapped in invisible parentheses.
* Add an entry in CHANGES.md
---
CHANGES.md | 3 +++
docs/change_log.md | 5 +++++
src/black/__init__.py | 4 ++--
tests/data/pep_572.py | 4 ++++
4 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 1c53604d4d5..7e356f1f29c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,9 @@
- `Black` now respects `--skip-string-normalization` when normalizing multiline
docstring quotes (#1637)
+- `Black` no longer adds an incorrect space after a parenthesized assignment expression
+ in if/while statements (#1655)
+
- fixed a crash when PWD=/ on POSIX (#1631)
### 20.8b1
diff --git a/docs/change_log.md b/docs/change_log.md
index b7337166659..cc5015f873c 100644
--- a/docs/change_log.md
+++ b/docs/change_log.md
@@ -9,6 +9,11 @@
- `Black` now respects `--skip-string-normalization` when normalizing multiline
docstring quotes (#1637)
+- `Black` no longer adds an incorrect space after a parenthesized assignment expression
+ in if/while statements (#1655)
+
+- fixed a crash when PWD=/ on POSIX (#1631)
+
### 20.8b1
#### _Packaging_
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 048e771ce96..64a18655905 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -5190,9 +5190,9 @@ def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
if check_lpar:
if is_walrus_assignment(child):
- continue
+ pass
- if child.type == syms.atom:
+ elif child.type == syms.atom:
if maybe_make_parens_invisible_in_atom(child, parent=node):
wrap_in_parentheses(node, child, visible=False)
elif is_one_tuple(child):
diff --git a/tests/data/pep_572.py b/tests/data/pep_572.py
index 9e429f913ce..637b3bb38c6 100644
--- a/tests/data/pep_572.py
+++ b/tests/data/pep_572.py
@@ -2,6 +2,8 @@
(a := a)
if (match := pattern.search(data)) is None:
pass
+if (match := pattern.search(data)):
+ pass
[y := f(x), y ** 2, y ** 3]
filtered_data = [y for x in data if (y := f(x)) is None]
(y := f(x))
@@ -41,3 +43,5 @@ def foo(answer: (p := 42) = 5):
while x := f(x):
pass
+while (x := f(x)):
+ pass
From 1af648d0c14342021bb8c4220fb8adcd7362d45d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Tue, 1 Sep 2020 13:18:46 +0200
Subject: [PATCH 057/680] Mention optional invalid W503 warning in pycodestyle
---
docs/compatible_configs.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/docs/compatible_configs.md b/docs/compatible_configs.md
index 82f13932b0b..990820a6771 100644
--- a/docs/compatible_configs.md
+++ b/docs/compatible_configs.md
@@ -156,6 +156,13 @@ whitespace around slice operators. Due to this, Flake8 will raise
`E203 whitespace before ':'` warnings. Since this warning is not PEP 8 compliant, Flake8
should be configured to ignore it via `extend-ignore = E203`.
+When breaking a line, _Black_ will break it before a binary operator. This is compliant
+with PEP 8 as of
+[April 2016](https://github.com/python/peps/commit/c59c4376ad233a62ca4b3a6060c81368bd21e85b#diff-64ec08cc46db7540f18f2af46037f599).
+There's a disabled-by-default warning in Flake8 which goes against this PEP 8
+recommendation called `W503 line break before binary operator`. It should not be enabled
+in your configuration.
+
Also, as like with isort, flake8 should be configured to allow lines up to the length
limit of `88`, _Black_'s default. This explains `max-line-length = 88`.
From 6b935a34d08c0b18d858e7da5093dc69a29c55f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Langa?=
Date: Tue, 1 Sep 2020 13:24:31 +0200
Subject: [PATCH 058/680] Clarify current trailing comma behavior in the docs
---
docs/the_black_code_style.md | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/docs/the_black_code_style.md b/docs/the_black_code_style.md
index 735e9c015d7..19464ba482a 100644
--- a/docs/the_black_code_style.md
+++ b/docs/the_black_code_style.md
@@ -244,16 +244,6 @@ required due to an inner function starting immediately after.
_Black_ will add trailing commas to expressions that are split by comma where each
element is on its own line. This includes function signatures.
-Unnecessary trailing commas are removed if an expression fits in one line. This makes it
-1% more likely that your line won't exceed the allotted line length limit. Moreover, in
-this scenario, if you added another argument to your call, you'd probably fit it in the
-same line anyway. That doesn't make diffs any larger.
-
-One exception to removing trailing commas is tuple expressions with just one element. In
-this case _Black_ won't touch the single trailing comma as this would unexpectedly
-change the underlying data type. Note that this is also the case when commas are used
-while indexing. This is a tuple in disguise: `numpy_array[3, ]`.
-
One exception to adding trailing commas is function signatures containing `*`, `*args`,
or `**kwargs`. In this case a trailing comma is only safe to use on Python 3.6. _Black_
will detect if your file is already 3.6+ only and use trailing commas in this situation.
@@ -262,6 +252,10 @@ in function signatures that have stars in them. In other words, if you'd like a
comma in this situation and _Black_ didn't recognize it was safe to do so, put it there
manually and _Black_ will keep it.
+A pre-existing trailing comma informs _Black_ to always explode contents of the current
+bracket pair into one item per line. Read more about this in the
+[Pragmatism](#pragmatism) section below.
+
### Strings
_Black_ prefers double quotes (`"` and `"""`) over single quotes (`'` and `'''`). It
From 25c1b6dff7264278c1ca1649f321a3bd8df293b1 Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Fri, 4 Sep 2020 17:20:55 -0400
Subject: [PATCH 059/680] Update primer.json to reflect Black's adoption
(#1674)
- tox recently adopted Black
https://github.com/tox-dev/tox/commit/a7903508fa07068b327e15cfdbf8ea330ab78765
- attrs already adopted Black but they updated to 20.08b1 + did a format pass and removed some trailing commas
https://github.com/python-attrs/attrs/commit/f680c5b83e65413eeb684c68ece60198015058c3
---
src/black_primer/primer.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index 546f47782cd..6371d44ce1f 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -10,7 +10,7 @@
},
"attrs": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/python-attrs/attrs.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -98,7 +98,7 @@
},
"tox": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/tox-dev/tox.git",
"long_checkout": false,
"py_versions": ["all"]
From 17908718338e6ba10d01f3b484ed0fe9542b8169 Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade
Date: Sat, 5 Sep 2020 19:38:43 +0300
Subject: [PATCH 060/680] Test primer on Pillow (#1679)
---
src/black_primer/primer.json | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index 6371d44ce1f..6086c4c813a 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -61,6 +61,13 @@
"long_checkout": false,
"py_versions": ["all"]
},
+ "pillow": {
+ "cli_arguments": [],
+ "expect_formatting_changes": false,
+ "git_clone_url": "https://github.com/python-pillow/Pillow.git",
+ "long_checkout": false,
+ "py_versions": ["all"]
+ },
"poetry": {
"cli_arguments": [],
"expect_formatting_changes": true,
From 6b5753a41781ef7a8746aeea52c26e07f4b43c27 Mon Sep 17 00:00:00 2001
From: Tom Saunders
Date: Sat, 5 Sep 2020 20:15:28 +0100
Subject: [PATCH 061/680] Handle .COLOR_DIFF in the same way as .DIFF (#1673)
---
CHANGES.md | 2 ++
src/black/__init__.py | 6 ++---
tests/test_black.py | 53 ++++++++++++++++++++++++++++++++++++++++---
3 files changed, 55 insertions(+), 6 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 7e356f1f29c..52c8016a257 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -12,6 +12,8 @@
- fixed a crash when PWD=/ on POSIX (#1631)
+- Prevent coloured diff output being interleaved with multiple files (#1673)
+
### 20.8b1
#### _Packaging_
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 64a18655905..f7e7603321a 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -661,7 +661,7 @@ def reformat_one(
changed = Changed.YES
else:
cache: Cache = {}
- if write_back != WriteBack.DIFF:
+ if write_back not in (WriteBack.DIFF, WriteBack.COLOR_DIFF):
cache = read_cache(mode)
res_src = src.resolve()
if res_src in cache and cache[res_src] == get_cache_info(res_src):
@@ -735,7 +735,7 @@ async def schedule_formatting(
:func:`format_file_in_place`.
"""
cache: Cache = {}
- if write_back != WriteBack.DIFF:
+ if write_back not in (WriteBack.DIFF, WriteBack.COLOR_DIFF):
cache = read_cache(mode)
sources, cached = filter_cached(cache, sources)
for src in sorted(cached):
@@ -746,7 +746,7 @@ async def schedule_formatting(
cancelled = []
sources_to_cache = []
lock = None
- if write_back == WriteBack.DIFF:
+ if write_back in (WriteBack.DIFF, WriteBack.COLOR_DIFF):
# For diff output, we need locks to ensure we don't interleave output
# from different processes.
manager = Manager()
diff --git a/tests/test_black.py b/tests/test_black.py
index bc80c8fca8b..edcf7208b46 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
+import multiprocessing
import asyncio
import logging
from concurrent.futures import ThreadPoolExecutor
@@ -1395,9 +1396,55 @@ def test_no_cache_when_writeback_diff(self) -> None:
src = (workspace / "test.py").resolve()
with src.open("w") as fobj:
fobj.write("print('hello')")
- self.invokeBlack([str(src), "--diff"])
- cache_file = black.get_cache_file(mode)
- self.assertFalse(cache_file.exists())
+ with patch("black.read_cache") as read_cache, patch(
+ "black.write_cache"
+ ) as write_cache:
+ self.invokeBlack([str(src), "--diff"])
+ cache_file = black.get_cache_file(mode)
+ self.assertFalse(cache_file.exists())
+ write_cache.assert_not_called()
+ read_cache.assert_not_called()
+
+ def test_no_cache_when_writeback_color_diff(self) -> None:
+ mode = DEFAULT_MODE
+ with cache_dir() as workspace:
+ src = (workspace / "test.py").resolve()
+ with src.open("w") as fobj:
+ fobj.write("print('hello')")
+ with patch("black.read_cache") as read_cache, patch(
+ "black.write_cache"
+ ) as write_cache:
+ self.invokeBlack([str(src), "--diff", "--color"])
+ cache_file = black.get_cache_file(mode)
+ self.assertFalse(cache_file.exists())
+ write_cache.assert_not_called()
+ read_cache.assert_not_called()
+
+ @event_loop()
+ def test_output_locking_when_writeback_diff(self) -> None:
+ with cache_dir() as workspace:
+ for tag in range(0, 4):
+ src = (workspace / f"test{tag}.py").resolve()
+ with src.open("w") as fobj:
+ fobj.write("print('hello')")
+ with patch("black.Manager", wraps=multiprocessing.Manager) as mgr:
+ self.invokeBlack(["--diff", str(workspace)], exit_code=0)
+ # this isn't quite doing what we want, but if it _isn't_
+ # called then we cannot be using the lock it provides
+ mgr.assert_called()
+
+ @event_loop()
+ def test_output_locking_when_writeback_color_diff(self) -> None:
+ with cache_dir() as workspace:
+ for tag in range(0, 4):
+ src = (workspace / f"test{tag}.py").resolve()
+ with src.open("w") as fobj:
+ fobj.write("print('hello')")
+ with patch("black.Manager", wraps=multiprocessing.Manager) as mgr:
+ self.invokeBlack(["--diff", "--color", str(workspace)], exit_code=0)
+ # this isn't quite doing what we want, but if it _isn't_
+ # called then we cannot be using the lock it provides
+ mgr.assert_called()
def test_no_cache_when_stdin(self) -> None:
mode = DEFAULT_MODE
From e3ccabb23c5dc5495bd8f96b5c90c1db6a350d6d Mon Sep 17 00:00:00 2001
From: Bryan Bugyi
Date: Sat, 5 Sep 2020 20:24:00 -0400
Subject: [PATCH 062/680] Fix unstable formatting on string split + %
formatting (#1680)
Fixes #1595
---
src/black/__init__.py | 2 +-
src/blib2to3/pgen2/pgen.py | 3 +--
tests/data/long_strings.py | 6 ++----
tests/data/long_strings__regression.py | 21 +++++++++++++++++----
4 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index f7e7603321a..eed059e2837 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -2668,9 +2668,9 @@ def rhs(line: Line, features: Collection[Feature]) -> Iterator[Line]:
transformers = [
string_merge,
string_paren_strip,
+ string_split,
delimiter_split,
standalone_comment_split,
- string_split,
string_paren_wrap,
rhs,
]
diff --git a/src/blib2to3/pgen2/pgen.py b/src/blib2to3/pgen2/pgen.py
index 13ec51d1878..a685145933c 100644
--- a/src/blib2to3/pgen2/pgen.py
+++ b/src/blib2to3/pgen2/pgen.py
@@ -168,8 +168,7 @@ def calcfirst(self, name: Text) -> None:
if symbol in inverse:
raise ValueError(
"rule %s is ambiguous; %s is in the first sets of %s as well"
- " as %s"
- % (name, symbol, label, inverse[symbol])
+ " as %s" % (name, symbol, label, inverse[symbol])
)
inverse[symbol] = label
self.first[name] = totalset
diff --git a/tests/data/long_strings.py b/tests/data/long_strings.py
index e1ed90f22de..151396b5239 100644
--- a/tests/data/long_strings.py
+++ b/tests/data/long_strings.py
@@ -380,8 +380,7 @@ def foo():
old_fmt_string1 = (
"While we are on the topic of %s, we should also note that old-style formatting"
- " must also be preserved, since some %s still uses it."
- % ("formatting", "code")
+ " must also be preserved, since some %s still uses it." % ("formatting", "code")
)
old_fmt_string2 = "This is a %s %s %s %s" % (
@@ -448,8 +447,7 @@ def foo():
assert some_type_of_boolean_expression, (
"Followed by a really really really long string that is used to provide context to"
- " the AssertionError exception, which uses dynamic string %s."
- % "formatting"
+ " the AssertionError exception, which uses dynamic string %s." % "formatting"
)
assert some_type_of_boolean_expression, (
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
index 044bb4a5deb..33bf14cfaa3 100644
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -310,6 +310,13 @@ def who(self):
passenger_association=passenger_association,
)
+if __name__ == "__main__":
+ for i in range(4, 8):
+ cmd = (
+ r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk '{print $2}'); do kill $pid; done"
+ % (i)
+ )
+
# output
@@ -435,14 +442,12 @@ def foo():
func_call_where_string_arg_has_old_fmt_and_bad_parens(
"A long string with {}. This string is so long that it is ridiculous. It can't fit"
- " on one line at alllll."
- % "formatting",
+ " on one line at alllll." % "formatting",
)
func_call_where_string_arg_has_old_fmt_and_bad_parens(
"A long string with {}. This {} is so long that it is ridiculous. It can't fit on"
- " one line at alllll."
- % ("formatting", "string"),
+ " one line at alllll." % ("formatting", "string"),
)
@@ -702,3 +707,11 @@ def who(self):
passenger_association=passenger_association,
)
)
+
+
+if __name__ == "__main__":
+ for i in range(4, 8):
+ cmd = (
+ r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk"
+ r" '{print $2}'); do kill $pid; done" % (i)
+ )
From 7bca930ca3d84bbd01e98937b6b8a493d0254c7c Mon Sep 17 00:00:00 2001
From: Bryan Bugyi
Date: Sun, 6 Sep 2020 11:02:57 -0400
Subject: [PATCH 063/680] Fix crash on concatenated string + comment (fixes
#1596) (#1677)
Co-authored-by: Jelle Zijlstra
---
src/black/__init__.py | 28 +++++++++++++++++++++-----
tests/data/long_strings__regression.py | 27 +++++++++++++++++++++++++
2 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index eed059e2837..ed5256eefe1 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -2889,11 +2889,8 @@ class StringMerger(CustomSplitMapMixin, StringTransformer):
"""StringTransformer that merges strings together.
Requirements:
- (A) The line contains adjacent strings such that at most one substring
- has inline comments AND none of those inline comments are pragmas AND
- the set of all substring prefixes is either of length 1 or equal to
- {"", "f"} AND none of the substrings are raw strings (i.e. are prefixed
- with 'r').
+ (A) The line contains adjacent strings such that ALL of the validation checks
+ listed in StringMerger.__validate_msg(...)'s docstring pass.
OR
(B) The line contains a string which uses line continuation backslashes.
@@ -3142,6 +3139,7 @@ def __validate_msg(line: Line, string_idx: int) -> TResult[None]:
* Ok(None), if ALL validation checks (listed below) pass.
OR
* Err(CannotTransform), if any of the following are true:
+ - The target string group does not contain ANY stand-alone comments.
- The target string is not in a string group (i.e. it has no
adjacent strings).
- The string group has more than one inline comment.
@@ -3150,6 +3148,26 @@ def __validate_msg(line: Line, string_idx: int) -> TResult[None]:
length greater than one and is not equal to {"", "f"}.
- The string group consists of raw strings.
"""
+ # We first check for "inner" stand-alone comments (i.e. stand-alone
+ # comments that have a string leaf before them AND after them).
+ for inc in [1, -1]:
+ i = string_idx
+ found_sa_comment = False
+ is_valid_index = is_valid_index_factory(line.leaves)
+ while is_valid_index(i) and line.leaves[i].type in [
+ token.STRING,
+ STANDALONE_COMMENT,
+ ]:
+ if line.leaves[i].type == STANDALONE_COMMENT:
+ found_sa_comment = True
+ elif found_sa_comment:
+ return TErr(
+ "StringMerger does NOT merge string groups which contain "
+ "stand-alone comments."
+ )
+
+ i += inc
+
num_of_inline_string_comments = 0
set_of_prefixes = set()
num_of_strings = 0
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
index 33bf14cfaa3..745a636cdcf 100644
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -310,6 +310,19 @@ def who(self):
passenger_association=passenger_association,
)
+xxxxxxx_xxxxxx_xxxxxxx = xxx(
+ [
+ xxxxxxxxxxxx(
+ xxxxxx_xxxxxxx=(
+ '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx = "xxxxxxxxxxxx")) && '
+ # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx.
+ "(x.bbbbbbbbbbbb.xxx != "
+ '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && '
+ )
+ )
+ ]
+)
+
if __name__ == "__main__":
for i in range(4, 8):
cmd = (
@@ -709,6 +722,20 @@ def who(self):
)
+xxxxxxx_xxxxxx_xxxxxxx = xxx(
+ [
+ xxxxxxxxxxxx(
+ xxxxxx_xxxxxxx=(
+ '((x.aaaaaaaaa = "xxxxxx.xxxxxxxxxxxxxxxxxxxxx") || (x.xxxxxxxxx ='
+ ' "xxxxxxxxxxxx")) && '
+ # xxxxx xxxxxxxxxxxx xxxx xxx (xxxxxxxxxxxxxxxx) xx x xxxxxxxxx xx xxxxxx.
+ "(x.bbbbbbbbbbbb.xxx != "
+ '"xxx:xxx:xxx::cccccccccccc:xxxxxxx-xxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxx") && '
+ )
+ )
+ ]
+)
+
if __name__ == "__main__":
for i in range(4, 8):
cmd = (
From 6284953d07060804dcdeacf4626b76aed7a20683 Mon Sep 17 00:00:00 2001
From: Bryan Bugyi
Date: Sun, 6 Sep 2020 12:15:40 -0400
Subject: [PATCH 064/680] Fix crash on assert and parenthesized % format (fixes
#1597, fixes #1605) (#1681)
---
src/black/__init__.py | 27 ++++++++++++++---
tests/data/long_strings__regression.py | 42 ++++++++++++++++++++++++++
2 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index ed5256eefe1..3753d5fca8d 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -112,6 +112,10 @@ class InvalidInput(ValueError):
"""Raised when input source code fails all parse attempts."""
+class BracketMatchError(KeyError):
+ """Raised when an opening bracket is unable to be matched to a closing bracket."""
+
+
T = TypeVar("T")
E = TypeVar("E", bound=Exception)
@@ -1308,7 +1312,13 @@ def mark(self, leaf: Leaf) -> None:
self.maybe_decrement_after_lambda_arguments(leaf)
if leaf.type in CLOSING_BRACKETS:
self.depth -= 1
- opening_bracket = self.bracket_match.pop((self.depth, leaf.type))
+ try:
+ opening_bracket = self.bracket_match.pop((self.depth, leaf.type))
+ except KeyError as e:
+ raise BracketMatchError(
+ "Unable to match a closing bracket to the following opening"
+ f" bracket: {leaf}"
+ ) from e
leaf.opening_bracket = opening_bracket
if not leaf.value:
self.invisible.append(leaf)
@@ -3324,10 +3334,17 @@ def do_transform(self, line: Line, string_idx: int) -> Iterator[TResult[Line]]:
yield TErr(
"Will not strip parentheses which have comments attached to them."
)
+ return
new_line = line.clone()
new_line.comments = line.comments.copy()
- append_leaves(new_line, line, LL[: string_idx - 1])
+ try:
+ append_leaves(new_line, line, LL[: string_idx - 1])
+ except BracketMatchError:
+ # HACK: I believe there is currently a bug somewhere in
+ # right_hand_split() that is causing brackets to not be tracked
+ # properly by a shared BracketTracker.
+ append_leaves(new_line, line, LL[: string_idx - 1], preformatted=True)
string_leaf = Leaf(token.STRING, LL[string_idx].value)
LL[string_idx - 1].remove()
@@ -4598,7 +4615,9 @@ def line_to_string(line: Line) -> str:
return str(line).strip("\n")
-def append_leaves(new_line: Line, old_line: Line, leaves: List[Leaf]) -> None:
+def append_leaves(
+ new_line: Line, old_line: Line, leaves: List[Leaf], preformatted: bool = False
+) -> None:
"""
Append leaves (taken from @old_line) to @new_line, making sure to fix the
underlying Node structure where appropriate.
@@ -4614,7 +4633,7 @@ def append_leaves(new_line: Line, old_line: Line, leaves: List[Leaf]) -> None:
for old_leaf in leaves:
new_leaf = Leaf(old_leaf.type, old_leaf.value)
replace_child(old_leaf, new_leaf)
- new_line.append(new_leaf)
+ new_line.append(new_leaf, preformatted=preformatted)
for comment_leaf in old_line.comments_after(old_leaf):
new_line.append(comment_leaf, preformatted=True)
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
index 745a636cdcf..1c82613d410 100644
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -330,6 +330,25 @@ def who(self):
% (i)
)
+def A():
+ def B():
+ def C():
+ def D():
+ def E():
+ def F():
+ def G():
+ assert (
+ c_float(val[0][0] / val[0][1]).value
+ == c_float(value[0][0] / value[0][1]).value
+ ), "%s didn't roundtrip" % tag
+
+class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx):
+ def xxxxxxx_xxxxxx(xxxx):
+ assert xxxxxxx_xxxx in [
+ x.xxxxx.xxxxxx.xxxxx.xxxxxx,
+ x.xxxxx.xxxxxx.xxxxx.xxxx,
+ ], ("xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx)
+
# output
@@ -742,3 +761,26 @@ def who(self):
r"for pid in $(ps aux | grep paster | grep -v grep | grep '\-%d' | awk"
r" '{print $2}'); do kill $pid; done" % (i)
)
+
+
+def A():
+ def B():
+ def C():
+ def D():
+ def E():
+ def F():
+ def G():
+ assert (
+ c_float(val[0][0] / val[0][1]).value
+ == c_float(value[0][0] / value[0][1]).value
+ ), "%s didn't roundtrip" % tag
+
+
+class xxxxxxxxxxxxxxxxxxxxx(xxxx.xxxxxxxxxxxxx):
+ def xxxxxxx_xxxxxx(xxxx):
+ assert xxxxxxx_xxxx in [
+ x.xxxxx.xxxxxx.xxxxx.xxxxxx,
+ x.xxxxx.xxxxxx.xxxxx.xxxx,
+ ], (
+ "xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx
+ )
From 1e8916f450505bfe58649c1ff8c0a3708de47daa Mon Sep 17 00:00:00 2001
From: Michael Wilkinson
Date: Tue, 8 Sep 2020 12:42:57 -0400
Subject: [PATCH 065/680] Add link to conda-forge integration (#1687)
* Add link to conda-forge integration
resolves #1686
* README: keep PyPI tags together
Co-authored-by: Hugo van Kemenade
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 20f6fa420b2..942e85444d3 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@
+
From cd055efd7d999421984e8b63afe53b5f9854deb9 Mon Sep 17 00:00:00 2001
From: Bryan Bugyi
Date: Thu, 10 Sep 2020 12:24:01 -0400
Subject: [PATCH 066/680] Fix unstable subscript assignment string wrapping
(#1678)
Fixes #1598
---
src/black/__init__.py | 7 +++++--
tests/data/long_strings__regression.py | 9 +++++++++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 3753d5fca8d..bfb77126218 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -3507,9 +3507,12 @@ def __get_max_string_length(self, line: Line, string_idx: int) -> int:
# WMA4 a single space.
offset += 1
- # WMA4 the lengths of any leaves that came before that space.
- for leaf in LL[: p_idx + 1]:
+ # WMA4 the lengths of any leaves that came before that space,
+ # but after any closing bracket before that space.
+ for leaf in reversed(LL[: p_idx + 1]):
offset += len(str(leaf))
+ if leaf.type in CLOSING_BRACKETS:
+ break
if is_valid_index(string_idx + 1):
N = LL[string_idx + 1]
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
index 1c82613d410..8290a4cbc1c 100644
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -349,6 +349,10 @@ def xxxxxxx_xxxxxx(xxxx):
x.xxxxx.xxxxxx.xxxxx.xxxx,
], ("xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx)
+value.__dict__[
+ key
+] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee
+
# output
@@ -784,3 +788,8 @@ def xxxxxxx_xxxxxx(xxxx):
], (
"xxxxxxxxxxx xxxxxxx xxxx (xxxxxx xxxx) %x xxx xxxxx" % xxxxxxx_xxxx
)
+
+
+value.__dict__[
+ key
+] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee
From ecc1f17ee57fd1d6c29e47194b1025721f99455b Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade
Date: Thu, 10 Sep 2020 22:52:23 +0300
Subject: [PATCH 067/680] Virtualenv is now formatted with newest Black
https://github.com/pypa/virtualenv/pull/1939 (#1695)
---
src/black_primer/primer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index 6086c4c813a..8a90192f27d 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -112,7 +112,7 @@
},
"virtualenv": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/pypa/virtualenv.git",
"long_checkout": false,
"py_versions": ["all"]
From c0a8e4224360a6917dcb5d889b08d5fdcfaf7c2d Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Thu, 10 Sep 2020 16:21:37 -0400
Subject: [PATCH 068/680] Fix empty line handling when formatting typing stubs
(#1646)
Black used to erroneously remove all empty lines between non-function
code and decorators when formatting typing stubs. Now a single empty
line is enforced.
I chose for putting empty lines around decorated classes that have empty
bodies since removing empty lines around such classes would cause a
formatting issue that seems to be impossible to fix.
For example:
```
class A: ...
@some_decorator
class B: ...
class C: ...
class D: ...
@some_other_decorator
def foo(): -> None: ...
```
It is easy to enforce no empty lines between class A, B, and C.
Just return 0, 0 for a line that is a decorator and precedes an stub
class. Fortunately before this commit, empty lines after that class
would be removed already.
Now let's look at the empty line between class D and function foo. In
this case, there should be an empty line there since it's class code next
to function code. The problem is that when deciding to add X empty lines
before a decorator, you can't tell whether it's before a class or a
function. If the decorator is before a function, then an empty line
is needed, while no empty lines are needed when the decorator is
before a class.
So even though I personally prefer no empty lines around decorated
classes, I had to go the other way surrounding decorated classes with
empty lines.
Co-authored-by: Jelle Zijlstra
---
CHANGES.md | 3 ++
docs/change_log.md | 3 ++
src/black/__init__.py | 11 +++++--
tests/data/force_pyi.py | 67 ++++++++++++++++++++++++++++++++++++++---
tests/test_black.py | 7 +++--
5 files changed, 82 insertions(+), 9 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 52c8016a257..59d9320639b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,9 @@
- `Black` now respects `--skip-string-normalization` when normalizing multiline
docstring quotes (#1637)
+- `Black` no longer removes all empty lines between non-function code and decorators
+ when formatting typing stubs. Now `Black` enforces a single empty line. (#1646)
+
- `Black` no longer adds an incorrect space after a parenthesized assignment expression
in if/while statements (#1655)
diff --git a/docs/change_log.md b/docs/change_log.md
index cc5015f873c..e183ca545b6 100644
--- a/docs/change_log.md
+++ b/docs/change_log.md
@@ -9,6 +9,9 @@
- `Black` now respects `--skip-string-normalization` when normalizing multiline
docstring quotes (#1637)
+- `Black` no longer removes all empty lines between non-function code and decorators
+ when formatting typing stubs. Now `Black` enforces a single empty line. (#1646)
+
- `Black` no longer adds an incorrect space after a parenthesized assignment expression
in if/while statements (#1655)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index bfb77126218..9e18a7d904b 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -1834,6 +1834,10 @@ def _maybe_empty_lines_for_class_or_def(
return 0, 0
if self.previous_line.is_decorator:
+ if self.is_pyi and current_line.is_stub_class:
+ # Insert an empty line after a decorated stub class
+ return 0, 1
+
return 0, 0
if self.previous_line.depth < current_line.depth and (
@@ -1857,8 +1861,11 @@ def _maybe_empty_lines_for_class_or_def(
newlines = 0
else:
newlines = 1
- elif current_line.is_def and not self.previous_line.is_def:
- # Blank line between a block of functions and a block of non-functions
+ elif (
+ current_line.is_def or current_line.is_decorator
+ ) and not self.previous_line.is_def:
+ # Blank line between a block of functions (maybe with preceding
+ # decorators) and a block of non-functions
newlines = 1
else:
newlines = 0
diff --git a/tests/data/force_pyi.py b/tests/data/force_pyi.py
index 25246c22ca7..07ed93c6879 100644
--- a/tests/data/force_pyi.py
+++ b/tests/data/force_pyi.py
@@ -1,6 +1,65 @@
-def f(): ...
+from typing import Union
+
+@bird
+def zoo(): ...
+
+class A: ...
+@bar
+class B:
+ def BMethod(self) -> None: ...
+ @overload
+ def BMethod(self, arg : List[str]) -> None: ...
+
+class C: ...
+@hmm
+class D: ...
+class E: ...
+
+@baz
+def foo() -> None:
+ ...
+
+class F (A , C): ...
+def spam() -> None: ...
+
+@overload
+def spam(arg: str) -> str: ...
+
+var : int = 1
+
+def eggs() -> Union[str, int]: ...
-def g(): ...
# output
-def f(): ...
-def g(): ...
+
+from typing import Union
+
+@bird
+def zoo(): ...
+
+class A: ...
+
+@bar
+class B:
+ def BMethod(self) -> None: ...
+ @overload
+ def BMethod(self, arg: List[str]) -> None: ...
+
+class C: ...
+
+@hmm
+class D: ...
+
+class E: ...
+
+@baz
+def foo() -> None: ...
+
+class F(A, C): ...
+
+def spam() -> None: ...
+@overload
+def spam(arg: str) -> str: ...
+
+var: int = 1
+
+def eggs() -> Union[str, int]: ...
diff --git a/tests/test_black.py b/tests/test_black.py
index edcf7208b46..e928dc04984 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -1574,7 +1574,6 @@ def test_tricky_unicode_symbols(self) -> None:
black.assert_stable(source, actual, DEFAULT_MODE)
def test_single_file_force_pyi(self) -> None:
- reg_mode = DEFAULT_MODE
pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
contents, expected = read_data("force_pyi")
with cache_dir() as workspace:
@@ -1587,9 +1586,11 @@ def test_single_file_force_pyi(self) -> None:
# verify cache with --pyi is separate
pyi_cache = black.read_cache(pyi_mode)
self.assertIn(path, pyi_cache)
- normal_cache = black.read_cache(reg_mode)
+ normal_cache = black.read_cache(DEFAULT_MODE)
self.assertNotIn(path, normal_cache)
- self.assertEqual(actual, expected)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(contents, actual)
+ black.assert_stable(contents, actual, pyi_mode)
@event_loop()
def test_multi_file_force_pyi(self) -> None:
From 811decd7f10fb2fb3ae343b9d9d0a3ae53b86a53 Mon Sep 17 00:00:00 2001
From: Daniel <61800298+ffe4@users.noreply.github.com>
Date: Sun, 13 Sep 2020 17:59:18 +0200
Subject: [PATCH 069/680] Fix typo in docstring (#1700)
Added a missing preposition
---
src/black/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 9e18a7d904b..ffaafe23f9e 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -925,7 +925,7 @@ def format_stdin_to_stdout(
def format_file_contents(src_contents: str, *, fast: bool, mode: Mode) -> FileContent:
- """Reformat contents a file and return new contents.
+ """Reformat contents of a file and return new contents.
If `fast` is False, additionally confirm that the reformatted code is
valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it.
From 6dddbd72414061cde9dd8ee72eac373b7fcf8b54 Mon Sep 17 00:00:00 2001
From: QuentinSoubeyran <45202794+QuentinSoubeyran@users.noreply.github.com>
Date: Sat, 19 Sep 2020 20:33:10 +0200
Subject: [PATCH 070/680] PEP 614 support (#1717)
---
CHANGES.md | 2 +
Pipfile | 2 +-
Pipfile.lock | 297 +++++++++++++++++++++++++--------------
src/black/__init__.py | 70 ++++++++-
src/blib2to3/Grammar.txt | 2 +-
tests/data/decorators.py | 176 +++++++++++++++++++++++
tests/data/python39.py | 37 +++++
tests/test_black.py | 61 +++++++-
8 files changed, 530 insertions(+), 117 deletions(-)
create mode 100644 tests/data/decorators.py
create mode 100644 tests/data/python39.py
diff --git a/CHANGES.md b/CHANGES.md
index 59d9320639b..7ad3482d07a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -17,6 +17,8 @@
- Prevent coloured diff output being interleaved with multiple files (#1673)
+- Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711)
+
### 20.8b1
#### _Packaging_
diff --git a/Pipfile b/Pipfile
index 44f57f6773c..1ced1ed096b 100644
--- a/Pipfile
+++ b/Pipfile
@@ -29,7 +29,7 @@ mypy_extensions = ">=0.4.3"
pathspec = ">=0.6"
regex = ">=2020.1.8"
toml = ">=0.10.1"
-typed-ast = "==1.4.0"
+typed-ast = "==1.4.1"
typing_extensions = ">=3.7.4"
black = {editable = true,extras = ["d"],path = "."}
dataclasses = {"python_version <" = "3.7","version >" = "0.6"}
diff --git a/Pipfile.lock b/Pipfile.lock
index 32b8012ff0e..e09d69ea83d 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "61d09a6b8a8c310becd5e108ed08e0eeae50c7323c08c8040367abded0cb1031"
+ "sha256": "46390803c9b9e1b77a1b4a29de602d864dea188488d3aee6361030c91529611c"
},
"pipfile-spec": 6,
"requires": {},
@@ -58,11 +58,11 @@
},
"attrs": {
"hashes": [
- "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a",
- "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"
+ "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
+ "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==20.1.0"
+ "version": "==20.2.0"
},
"black": {
"editable": true,
@@ -186,39 +186,40 @@
},
"toml": {
"hashes": [
- "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"
],
"index": "pypi",
"version": "==0.10.1"
},
"typed-ast": {
"hashes": [
- "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161",
- "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
- "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
- "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
- "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
- "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47",
- "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
- "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
- "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
- "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
- "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2",
- "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e",
- "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
- "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
- "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
- "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
- "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
- "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
- "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66",
- "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
+ "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
+ "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
+ "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
+ "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
+ "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
+ "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
+ "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
+ "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
+ "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
+ "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
+ "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
+ "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
+ "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
+ "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
+ "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
+ "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
+ "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
+ "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
+ "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
+ "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
+ "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
],
"index": "pypi",
- "version": "==1.4.0"
+ "version": "==1.4.1"
},
"typing-extensions": {
"hashes": [
@@ -305,11 +306,11 @@
},
"attrs": {
"hashes": [
- "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a",
- "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"
+ "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
+ "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==20.1.0"
+ "version": "==20.2.0"
},
"babel": {
"hashes": [
@@ -328,11 +329,11 @@
},
"bleach": {
"hashes": [
- "sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f",
- "sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b"
+ "sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080",
+ "sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==3.1.5"
+ "version": "==3.2.1"
},
"certifi": {
"hashes": [
@@ -341,6 +342,39 @@
],
"version": "==2020.6.20"
},
+ "cffi": {
+ "hashes": [
+ "sha256:0da50dcbccd7cb7e6c741ab7912b2eff48e85af217d72b57f80ebc616257125e",
+ "sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c",
+ "sha256:15419020b0e812b40d96ec9d369b2bc8109cc3295eac6e013d3261343580cc7e",
+ "sha256:15a5f59a4808f82d8ec7364cbace851df591c2d43bc76bcbe5c4543a7ddd1bf1",
+ "sha256:23e44937d7695c27c66a54d793dd4b45889a81b35c0751ba91040fe825ec59c4",
+ "sha256:29c4688ace466a365b85a51dcc5e3c853c1d283f293dfcc12f7a77e498f160d2",
+ "sha256:57214fa5430399dffd54f4be37b56fe22cedb2b98862550d43cc085fb698dc2c",
+ "sha256:577791f948d34d569acb2d1add5831731c59d5a0c50a6d9f629ae1cefd9ca4a0",
+ "sha256:6539314d84c4d36f28d73adc1b45e9f4ee2a89cdc7e5d2b0a6dbacba31906798",
+ "sha256:65867d63f0fd1b500fa343d7798fa64e9e681b594e0a07dc934c13e76ee28fb1",
+ "sha256:672b539db20fef6b03d6f7a14b5825d57c98e4026401fce838849f8de73fe4d4",
+ "sha256:6843db0343e12e3f52cc58430ad559d850a53684f5b352540ca3f1bc56df0731",
+ "sha256:7057613efefd36cacabbdbcef010e0a9c20a88fc07eb3e616019ea1692fa5df4",
+ "sha256:76ada88d62eb24de7051c5157a1a78fd853cca9b91c0713c2e973e4196271d0c",
+ "sha256:837398c2ec00228679513802e3744d1e8e3cb1204aa6ad408b6aff081e99a487",
+ "sha256:8662aabfeab00cea149a3d1c2999b0731e70c6b5bac596d95d13f643e76d3d4e",
+ "sha256:95e9094162fa712f18b4f60896e34b621df99147c2cee216cfa8f022294e8e9f",
+ "sha256:99cc66b33c418cd579c0f03b77b94263c305c389cb0c6972dac420f24b3bf123",
+ "sha256:9b219511d8b64d3fa14261963933be34028ea0e57455baf6781fe399c2c3206c",
+ "sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b",
+ "sha256:b9aa9d8818c2e917fa2c105ad538e222a5bce59777133840b93134022a7ce650",
+ "sha256:bf44a9a0141a082e89c90e8d785b212a872db793a0080c20f6ae6e2a0ebf82ad",
+ "sha256:c0b48b98d79cf795b0916c57bebbc6d16bb43b9fc9b8c9f57f4cf05881904c75",
+ "sha256:da9d3c506f43e220336433dffe643fbfa40096d408cb9b7f2477892f369d5f82",
+ "sha256:e4082d832e36e7f9b2278bc774886ca8207346b99f278e54c9de4834f17232f7",
+ "sha256:e4b9b7af398c32e408c00eb4e0d33ced2f9121fd9fb978e6c1b57edd014a7d15",
+ "sha256:e613514a82539fc48291d01933951a13ae93b6b444a88782480be32245ed4afa",
+ "sha256:f5033952def24172e60493b68717792e3aebb387a8d186c43c020d9363ee7281"
+ ],
+ "version": "==1.14.2"
+ },
"cfgv": {
"hashes": [
"sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d",
@@ -381,43 +415,71 @@
},
"coverage": {
"hashes": [
- "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb",
- "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3",
- "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716",
- "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034",
- "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3",
- "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8",
- "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0",
- "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f",
- "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4",
- "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962",
- "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d",
- "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b",
- "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4",
- "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3",
- "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258",
- "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59",
- "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01",
- "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd",
- "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b",
- "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d",
- "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89",
- "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd",
- "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b",
- "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d",
- "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46",
- "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546",
- "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082",
- "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b",
- "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4",
- "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8",
- "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811",
- "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd",
- "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651",
- "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"
- ],
- "index": "pypi",
- "version": "==5.2.1"
+ "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516",
+ "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259",
+ "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9",
+ "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097",
+ "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0",
+ "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f",
+ "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7",
+ "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c",
+ "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5",
+ "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7",
+ "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729",
+ "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978",
+ "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9",
+ "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f",
+ "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9",
+ "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822",
+ "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418",
+ "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82",
+ "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f",
+ "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d",
+ "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221",
+ "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4",
+ "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21",
+ "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709",
+ "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54",
+ "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d",
+ "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270",
+ "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24",
+ "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751",
+ "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a",
+ "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237",
+ "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7",
+ "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636",
+ "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"
+ ],
+ "index": "pypi",
+ "version": "==5.3"
+ },
+ "cryptography": {
+ "hashes": [
+ "sha256:10c9775a3f31610cf6b694d1fe598f2183441de81cedcf1814451ae53d71b13a",
+ "sha256:180c9f855a8ea280e72a5d61cf05681b230c2dce804c48e9b2983f491ecc44ed",
+ "sha256:247df238bc05c7d2e934a761243bfdc67db03f339948b1e2e80c75d41fc7cc36",
+ "sha256:26409a473cc6278e4c90f782cd5968ebad04d3911ed1c402fc86908c17633e08",
+ "sha256:2a27615c965173c4c88f2961cf18115c08fedfb8bdc121347f26e8458dc6d237",
+ "sha256:2e26223ac636ca216e855748e7d435a1bf846809ed12ed898179587d0cf74618",
+ "sha256:321761d55fb7cb256b771ee4ed78e69486a7336be9143b90c52be59d7657f50f",
+ "sha256:4005b38cd86fc51c955db40b0f0e52ff65340874495af72efabb1bb8ca881695",
+ "sha256:4b9e96543d0784acebb70991ebc2dbd99aa287f6217546bb993df22dd361d41c",
+ "sha256:548b0818e88792318dc137d8b1ec82a0ab0af96c7f0603a00bb94f896fbf5e10",
+ "sha256:725875681afe50b41aee7fdd629cedbc4720bab350142b12c55c0a4d17c7416c",
+ "sha256:7a63e97355f3cd77c94bd98c59cb85fe0efd76ea7ef904c9b0316b5bbfde6ed1",
+ "sha256:94191501e4b4009642be21dde2a78bd3c2701a81ee57d3d3d02f1d99f8b64a9e",
+ "sha256:969ae512a250f869c1738ca63be843488ff5cc031987d302c1f59c7dbe1b225f",
+ "sha256:9f734423eb9c2ea85000aa2476e0d7a58e021bc34f0a373ac52a5454cd52f791",
+ "sha256:b45ab1c6ece7c471f01c56f5d19818ca797c34541f0b2351635a5c9fe09ac2e0",
+ "sha256:cc6096c86ec0de26e2263c228fb25ee01c3ff1346d3cfc219d67d49f303585af",
+ "sha256:dc3f437ca6353979aace181f1b790f0fc79e446235b14306241633ab7d61b8f8",
+ "sha256:e7563eb7bc5c7e75a213281715155248cceba88b11cb4b22957ad45b85903761",
+ "sha256:e7dad66a9e5684a40f270bd4aee1906878193ae50a4831922e454a2a457f1716",
+ "sha256:eb80a288e3cfc08f679f95da72d2ef90cb74f6d8a8ba69d2f215c5e110b2ca32",
+ "sha256:fa7fbcc40e2210aca26c7ac8a39467eae444d90a2c346cbcffd9133a166bcc67"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==3.1"
},
"distlib": {
"hashes": [
@@ -468,11 +530,11 @@
},
"identify": {
"hashes": [
- "sha256:9f5fcf22b665eaece583bd395b103c2769772a0f646ffabb5b1f155901b07de2",
- "sha256:b1aa2e05863dc80242610d46a7b49105e2eafe00ef0c8ff311c1828680760c76"
+ "sha256:c770074ae1f19e08aadbda1c886bc6d0cb55ffdc503a8c0fe8699af2fc9664ae",
+ "sha256:d02d004568c5a01261839a05e91705e3e9f5c57a3551648f9b3fb2b9c62c0f62"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==1.4.29"
+ "version": "==1.5.3"
},
"idna": {
"hashes": [
@@ -490,6 +552,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.0"
},
+ "jeepney": {
+ "hashes": [
+ "sha256:3479b861cc2b6407de5188695fa1a8d57e5072d7059322469b62628869b8e36e",
+ "sha256:d6c6b49683446d2407d2fe3acb7a368a77ff063f9182fe427da15d622adc24cf"
+ ],
+ "markers": "sys_platform == 'linux'",
+ "version": "==0.4.3"
+ },
"jinja2": {
"hashes": [
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
@@ -500,11 +570,11 @@
},
"keyring": {
"hashes": [
- "sha256:182f94fc0381546489e3e4d90384a8c1d43cc09ffe2eb4a826e7312df6e1be7c",
- "sha256:cd4d486803d55bdb13e2d453eb61dbbc984773e4f2b98a455aa85b1f4bc421e4"
+ "sha256:4e34ea2fdec90c1c43d6610b5a5fafa1b9097db1802948e90caf5763974b8f8d",
+ "sha256:9aeadd006a852b78f4b4ef7c7556c2774d2432bbef8ee538a3e9089ac8b11466"
],
"markers": "python_version >= '3.6'",
- "version": "==21.3.1"
+ "version": "==21.4.0"
},
"markupsafe": {
"hashes": [
@@ -649,6 +719,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.6.0"
},
+ "pycparser": {
+ "hashes": [
+ "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
+ "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.20"
+ },
"pyflakes": {
"hashes": [
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
@@ -659,11 +737,11 @@
},
"pygments": {
"hashes": [
- "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
- "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"
+ "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998",
+ "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"
],
"markers": "python_version >= '3.5'",
- "version": "==2.6.1"
+ "version": "==2.7.1"
},
"pyparsing": {
"hashes": [
@@ -761,6 +839,14 @@
],
"version": "==1.4.0"
},
+ "secretstorage": {
+ "hashes": [
+ "sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6",
+ "sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b"
+ ],
+ "markers": "sys_platform == 'linux'",
+ "version": "==3.1.2"
+ },
"setuptools-scm": {
"hashes": [
"sha256:09c659d1d6680811c43f476a33c6d3d9872416580786e96bd29ea03e6a818e41",
@@ -848,21 +934,21 @@
},
"toml": {
"hashes": [
- "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"
],
"index": "pypi",
"version": "==0.10.1"
},
"tqdm": {
"hashes": [
- "sha256:1a336d2b829be50e46b84668691e0a2719f26c97c62846298dd5ae2937e4d5cf",
- "sha256:564d632ea2b9cb52979f7956e093e831c28d441c11751682f84c86fc46e4fd21"
+ "sha256:8f3c5815e3b5e20bc40463fa6b42a352178859692a68ffaa469706e6d38342a5",
+ "sha256:faf9c671bd3fad5ebaeee366949d969dca2b2be32c872a7092a1e1a9048d105b"
],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==4.48.2"
+ "version": "==4.49.0"
},
"twine": {
"hashes": [
@@ -874,29 +960,30 @@
},
"typed-ast": {
"hashes": [
- "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161",
- "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
- "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
- "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
- "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
- "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47",
- "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
- "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
- "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
- "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
- "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2",
- "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e",
- "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
- "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
- "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
- "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
- "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
- "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
- "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66",
- "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
+ "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
+ "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
+ "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
+ "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
+ "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
+ "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
+ "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
+ "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
+ "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
+ "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
+ "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
+ "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
+ "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
+ "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
+ "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
+ "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
+ "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
+ "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
+ "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
+ "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
+ "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
],
"index": "pypi",
- "version": "==1.4.0"
+ "version": "==1.4.1"
},
"typing-extensions": {
"hashes": [
diff --git a/src/black/__init__.py b/src/black/__init__.py
index ffaafe23f9e..c1db8e6ef64 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -178,14 +178,12 @@ class TargetVersion(Enum):
PY36 = 6
PY37 = 7
PY38 = 8
+ PY39 = 9
def is_python2(self) -> bool:
return self is TargetVersion.PY27
-PY36_VERSIONS = {TargetVersion.PY36, TargetVersion.PY37, TargetVersion.PY38}
-
-
class Feature(Enum):
# All string literals are unicode
UNICODE_LITERALS = 1
@@ -199,6 +197,7 @@ class Feature(Enum):
ASYNC_KEYWORDS = 7
ASSIGNMENT_EXPRESSIONS = 8
POS_ONLY_ARGUMENTS = 9
+ RELAXED_DECORATORS = 10
FORCE_OPTIONAL_PARENTHESES = 50
@@ -237,6 +236,17 @@ class Feature(Enum):
Feature.ASSIGNMENT_EXPRESSIONS,
Feature.POS_ONLY_ARGUMENTS,
},
+ TargetVersion.PY39: {
+ Feature.UNICODE_LITERALS,
+ Feature.F_STRINGS,
+ Feature.NUMERIC_UNDERSCORES,
+ Feature.TRAILING_COMMA_IN_CALL,
+ Feature.TRAILING_COMMA_IN_DEF,
+ Feature.ASYNC_KEYWORDS,
+ Feature.ASSIGNMENT_EXPRESSIONS,
+ Feature.RELAXED_DECORATORS,
+ Feature.POS_ONLY_ARGUMENTS,
+ },
}
@@ -2184,6 +2194,9 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901
):
# Python 2 print chevron
return NO
+ elif prevp.type == token.AT and p.parent and p.parent.type == syms.decorator:
+ # no space in decorators
+ return NO
elif prev.type in OPENING_BRACKETS:
return NO
@@ -5499,6 +5512,49 @@ def is_walrus_assignment(node: LN) -> bool:
return inner is not None and inner.type == syms.namedexpr_test
+def is_simple_decorator_trailer(node: LN, last: bool = False) -> bool:
+ """Return True iff `node` is a trailer valid in a simple decorator"""
+ return node.type == syms.trailer and (
+ (
+ len(node.children) == 2
+ and node.children[0].type == token.DOT
+ and node.children[1].type == token.NAME
+ )
+ # last trailer can be arguments
+ or (
+ last
+ and len(node.children) == 3
+ and node.children[0].type == token.LPAR
+ # and node.children[1].type == syms.argument
+ and node.children[2].type == token.RPAR
+ )
+ )
+
+
+def is_simple_decorator_expression(node: LN) -> bool:
+ """Return True iff `node` could be a 'dotted name' decorator
+
+ This function takes the node of the 'namedexpr_test' of the new decorator
+ grammar and test if it would be valid under the old decorator grammar.
+
+ The old grammar was: decorator: @ dotted_name [arguments] NEWLINE
+ The new grammar is : decorator: @ namedexpr_test NEWLINE
+ """
+ if node.type == token.NAME:
+ return True
+ if node.type == syms.power:
+ if node.children:
+ return (
+ node.children[0].type == token.NAME
+ and all(map(is_simple_decorator_trailer, node.children[1:-1]))
+ and (
+ len(node.children) < 2
+ or is_simple_decorator_trailer(node.children[-1], last=True)
+ )
+ )
+ return False
+
+
def is_yield(node: LN) -> bool:
"""Return True if `node` holds a `yield` or `yield from` expression."""
if node.type == syms.yield_expr:
@@ -5684,6 +5740,8 @@ def get_features_used(node: Node) -> Set[Feature]:
- underscores in numeric literals;
- trailing commas after * or ** in function signatures and calls;
- positional only arguments in function signatures and lambdas;
+ - assignment expression;
+ - relaxed decorator syntax;
"""
features: Set[Feature] = set()
for n in node.pre_order():
@@ -5703,6 +5761,12 @@ def get_features_used(node: Node) -> Set[Feature]:
elif n.type == token.COLONEQUAL:
features.add(Feature.ASSIGNMENT_EXPRESSIONS)
+ elif n.type == syms.decorator:
+ if len(n.children) > 1 and not is_simple_decorator_expression(
+ n.children[1]
+ ):
+ features.add(Feature.RELAXED_DECORATORS)
+
elif (
n.type in {syms.typedargslist, syms.arglist}
and n.children
diff --git a/src/blib2to3/Grammar.txt b/src/blib2to3/Grammar.txt
index f14e2b516bd..eafaee84cb3 100644
--- a/src/blib2to3/Grammar.txt
+++ b/src/blib2to3/Grammar.txt
@@ -12,7 +12,7 @@ file_input: (NEWLINE | stmt)* ENDMARKER
single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
eval_input: testlist NEWLINE* ENDMARKER
-decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
+decorator: '@' namedexpr_test NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef | async_funcdef)
async_funcdef: ASYNC funcdef
diff --git a/tests/data/decorators.py b/tests/data/decorators.py
new file mode 100644
index 00000000000..acfad51fcb8
--- /dev/null
+++ b/tests/data/decorators.py
@@ -0,0 +1,176 @@
+# This file doesn't use the standard decomposition.
+# Decorator syntax test cases are separated by double # comments.
+# Those before the 'output' comment are valid under the old syntax.
+# Those after the 'ouput' comment require PEP614 relaxed syntax.
+# Do not remove the double # separator before the first test case, it allows
+# the comment before the test case to be ignored.
+
+##
+
+@decorator
+def f():
+ ...
+
+##
+
+@decorator(arg)
+def f():
+ ...
+
+##
+
+@decorator(kwarg=0)
+def f():
+ ...
+
+##
+
+@decorator(*args)
+def f():
+ ...
+
+##
+
+@decorator(**kwargs)
+def f():
+ ...
+
+##
+
+@decorator(*args, **kwargs)
+def f():
+ ...
+
+##
+
+@decorator(*args, **kwargs,)
+def f():
+ ...
+
+##
+
+@dotted.decorator
+def f():
+ ...
+
+##
+
+@dotted.decorator(arg)
+def f():
+ ...
+
+##
+
+@dotted.decorator(kwarg=0)
+def f():
+ ...
+
+##
+
+@dotted.decorator(*args)
+def f():
+ ...
+
+##
+
+@dotted.decorator(**kwargs)
+def f():
+ ...
+
+##
+
+@dotted.decorator(*args, **kwargs)
+def f():
+ ...
+
+##
+
+@dotted.decorator(*args, **kwargs,)
+def f():
+ ...
+
+##
+
+@double.dotted.decorator
+def f():
+ ...
+
+##
+
+@double.dotted.decorator(arg)
+def f():
+ ...
+
+##
+
+@double.dotted.decorator(kwarg=0)
+def f():
+ ...
+
+##
+
+@double.dotted.decorator(*args)
+def f():
+ ...
+
+##
+
+@double.dotted.decorator(**kwargs)
+def f():
+ ...
+
+##
+
+@double.dotted.decorator(*args, **kwargs)
+def f():
+ ...
+
+##
+
+@double.dotted.decorator(*args, **kwargs,)
+def f():
+ ...
+
+##
+
+@_(sequence["decorator"])
+def f():
+ ...
+
+##
+
+@eval("sequence['decorator']")
+def f():
+ ...
+
+# output
+
+##
+
+@decorator()()
+def f():
+ ...
+
+##
+
+@(decorator)
+def f():
+ ...
+
+##
+
+@sequence["decorator"]
+def f():
+ ...
+
+##
+
+@decorator[List[str]]
+def f():
+ ...
+
+##
+
+@var := decorator
+def f():
+ ...
\ No newline at end of file
diff --git a/tests/data/python39.py b/tests/data/python39.py
new file mode 100644
index 00000000000..ae67c2257eb
--- /dev/null
+++ b/tests/data/python39.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3.9
+
+@relaxed_decorator[0]
+def f():
+ ...
+
+@relaxed_decorator[extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length]
+def f():
+ ...
+
+@extremely_long_variable_name_that_doesnt_fit := complex.expression(with_long="arguments_value_that_wont_fit_at_the_end_of_the_line")
+def f():
+ ...
+
+# output
+
+
+#!/usr/bin/env python3.9
+
+
+@relaxed_decorator[0]
+def f():
+ ...
+
+
+@relaxed_decorator[
+ extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length
+]
+def f():
+ ...
+
+
+@extremely_long_variable_name_that_doesnt_fit := complex.expression(
+ with_long="arguments_value_that_wont_fit_at_the_end_of_the_line"
+)
+def f():
+ ...
\ No newline at end of file
diff --git a/tests/test_black.py b/tests/test_black.py
index e928dc04984..e17f43b88d4 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -59,9 +59,13 @@
PROJECT_ROOT = THIS_DIR.parent
DETERMINISTIC_HEADER = "[Deterministic header]"
EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
-PY36_ARGS = [
- f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
-]
+PY36_VERSIONS = {
+ TargetVersion.PY36,
+ TargetVersion.PY37,
+ TargetVersion.PY38,
+ TargetVersion.PY39,
+}
+PY36_ARGS = [f"--target-version={version.name.lower()}" for version in PY36_VERSIONS]
T = TypeVar("T")
R = TypeVar("R")
@@ -705,7 +709,7 @@ def test_string_prefixes(self) -> None:
@patch("black.dump_to_file", dump_to_stderr)
def test_numeric_literals(self) -> None:
source, expected = read_data("numeric_literals")
- mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
+ mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@@ -714,7 +718,7 @@ def test_numeric_literals(self) -> None:
@patch("black.dump_to_file", dump_to_stderr)
def test_numeric_literals_ignoring_underscores(self) -> None:
source, expected = read_data("numeric_literals_skip_underscores")
- mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
+ mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@@ -800,6 +804,16 @@ def test_python38(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_python39(self) -> None:
+ source, expected = read_data("python39")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ major, minor = sys.version_info[:2]
+ if major > 3 or (major == 3 and minor >= 9):
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff(self) -> None:
source, expected = read_data("fmtonoff")
@@ -1212,6 +1226,39 @@ def test_lib2to3_parse(self) -> None:
black.lib2to3_parse(py3_only, {TargetVersion.PY36})
black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
+ def test_get_features_used_decorator(self) -> None:
+ # Test the feature detection of new decorator syntax
+ # since this makes some test cases of test_get_features_used()
+ # fails if it fails, this is tested first so that a useful case
+ # is identified
+ simples, relaxed = read_data("decorators")
+ # skip explanation comments at the top of the file
+ for simple_test in simples.split("##")[1:]:
+ node = black.lib2to3_parse(simple_test)
+ decorator = str(node.children[0].children[0]).strip()
+ self.assertNotIn(
+ Feature.RELAXED_DECORATORS,
+ black.get_features_used(node),
+ msg=(
+ f"decorator '{decorator}' follows python<=3.8 syntax"
+ "but is detected as 3.9+"
+ # f"The full node is\n{node!r}"
+ ),
+ )
+ # skip the '# output' comment at the top of the output part
+ for relaxed_test in relaxed.split("##")[1:]:
+ node = black.lib2to3_parse(relaxed_test)
+ decorator = str(node.children[0].children[0]).strip()
+ self.assertIn(
+ Feature.RELAXED_DECORATORS,
+ black.get_features_used(node),
+ msg=(
+ f"decorator '{decorator}' uses python3.9+ syntax"
+ "but is detected as python<=3.8"
+ # f"The full node is\n{node!r}"
+ ),
+ )
+
def test_get_features_used(self) -> None:
node = black.lib2to3_parse("def f(*, arg): ...\n")
self.assertEqual(black.get_features_used(node), set())
@@ -1628,7 +1675,7 @@ def test_pipe_force_pyi(self) -> None:
def test_single_file_force_py36(self) -> None:
reg_mode = DEFAULT_MODE
- py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
+ py36_mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
source, expected = read_data("force_py36")
with cache_dir() as workspace:
path = (workspace / "file.py").resolve()
@@ -1647,7 +1694,7 @@ def test_single_file_force_py36(self) -> None:
@event_loop()
def test_multi_file_force_py36(self) -> None:
reg_mode = DEFAULT_MODE
- py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
+ py36_mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
source, expected = read_data("force_py36")
with cache_dir() as workspace:
paths = [
From bc138d12630ec38cb1b11b2fb2ca8762ac2ae7a4 Mon Sep 17 00:00:00 2001
From: Vipul
Date: Sun, 27 Sep 2020 05:54:21 +0000
Subject: [PATCH 071/680] End 'force-exclude' help message with a period
(#1727)
It would be nice, if like other options help message, force-exclude's
help message also ends with a period punctuation mark.
---
README.md | 2 +-
docs/installation_and_usage.md | 2 +-
src/black/__init__.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 942e85444d3..aef850f1415 100644
--- a/README.md
+++ b/README.md
@@ -133,7 +133,7 @@ Options:
--force-exclude TEXT Like --exclude, but files and directories
matching this regex will be excluded even
- when they are passed explicitly as arguments
+ when they are passed explicitly as arguments.
-q, --quiet Don't emit non-error messages to stderr.
Errors are still emitted; silence those with
diff --git a/docs/installation_and_usage.md b/docs/installation_and_usage.md
index cc0269198a2..7d37f59264b 100644
--- a/docs/installation_and_usage.md
+++ b/docs/installation_and_usage.md
@@ -88,7 +88,7 @@ Options:
--force-exclude TEXT Like --exclude, but files and directories
matching this regex will be excluded even
- when they are passed explicitly as arguments
+ when they are passed explicitly as arguments.
-q, --quiet Don't emit non-error messages to stderr.
Errors are still emitted; silence those with
diff --git a/src/black/__init__.py b/src/black/__init__.py
index c1db8e6ef64..dce7a24445a 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -454,7 +454,7 @@ def target_version_option_callback(
type=str,
help=(
"Like --exclude, but files and directories matching this regex will be "
- "excluded even when they are passed explicitly as arguments"
+ "excluded even when they are passed explicitly as arguments."
),
)
@click.option(
From 4d71d74a442ccc6c309a0667147997c1eeb755fd Mon Sep 17 00:00:00 2001
From: Pete Grayson
Date: Sun, 27 Sep 2020 21:41:11 +0000
Subject: [PATCH 072/680] Repair colorama wrapping on non-Windows platforms
(#1670)
* Repair colorama wrapping on non-Windows platforms
The wrap_stream_for_windows() function calls
colorama.initialise.wrap_stream() function to apply colorama's magic to
wrapper to the output stream. Except this wrapper is only applied on
Windows platforms that need it, otherwise the original stream is
returned as-is.
The colorama wrapped stream lacks a detach() method, so a no-op lambda
was being assigned to the wrapped stream.
The problem is that the no-op lambda was being assigned unconditionally
whether or not colorama actually returns a wrapped stream, thus
replacing the original TextIOWrapper's detach() method. Replacing the
detach() method with a no-op lambda is the root cause of the problem
observed in #1664.
The solution is to only assign the no-op detach() method if the stream
lacks its own detach() method.
Repairs #1664
---
CHANGES.md | 2 ++
README.md | 1 +
src/black/__init__.py | 30 +++++++++++-------------------
3 files changed, 14 insertions(+), 19 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 7ad3482d07a..dfc54224b41 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -15,6 +15,8 @@
- fixed a crash when PWD=/ on POSIX (#1631)
+- fixed "I/O operation on closed file" when using --diff (#1664)
+
- Prevent coloured diff output being interleaved with multiple files (#1673)
- Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711)
diff --git a/README.md b/README.md
index aef850f1415..f17b353b023 100644
--- a/README.md
+++ b/README.md
@@ -636,6 +636,7 @@ Multiple contributions by:
- [Paul Ganssle](mailto:p.ganssle@gmail.com)
- [Paul Meinhardt](mailto:mnhrdt@gmail.com)
- [Peter Bengtsson](mailto:mail@peterbe.com)
+- [Peter Grayson](mailto:pete@jpgrayson.net)
- [Peter Stensmyr](mailto:peter.stensmyr@gmail.com)
- pmacosta
- [Quentin Pradet](mailto:quentin@pradet.me)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index dce7a24445a..9af58014c51 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -871,30 +871,22 @@ def color_diff(contents: str) -> str:
def wrap_stream_for_windows(
f: io.TextIOWrapper,
-) -> Union[io.TextIOWrapper, "colorama.AnsiToWin32.AnsiToWin32"]:
+) -> Union[io.TextIOWrapper, "colorama.AnsiToWin32"]:
"""
- Wrap the stream in colorama's wrap_stream so colors are shown on Windows.
+ Wrap stream with colorama's wrap_stream so colors are shown on Windows.
- If `colorama` is not found, then no change is made. If `colorama` does
- exist, then it handles the logic to determine whether or not to change
- things.
+ If `colorama` is unavailable, the original stream is returned unmodified.
+ Otherwise, the `wrap_stream()` function determines whether the stream needs
+ to be wrapped for a Windows environment and will accordingly either return
+ an `AnsiToWin32` wrapper or the original stream.
"""
try:
- from colorama import initialise
-
- # We set `strip=False` so that we can don't have to modify
- # test_express_diff_with_color.
- f = initialise.wrap_stream(
- f, convert=None, strip=False, autoreset=False, wrap=True
- )
-
- # wrap_stream returns a `colorama.AnsiToWin32.AnsiToWin32` object
- # which does not have a `detach()` method. So we fake one.
- f.detach = lambda *args, **kwargs: None # type: ignore
+ from colorama.initialise import wrap_stream
except ImportError:
- pass
-
- return f
+ return f
+ else:
+ # Set `strip=False` to avoid needing to modify test_express_diff_with_color.
+ return wrap_stream(f, convert=None, strip=False, autoreset=False, wrap=True)
def format_stdin_to_stdout(
From 82c1f871d07e9c6334b5fb0bf089273a08ffd195 Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade
Date: Mon, 28 Sep 2020 05:42:01 +0300
Subject: [PATCH 073/680] Hypothesis is now formatted with Black 20.8b1 (#1729)
---
src/black_primer/primer.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index 8a90192f27d..5818444e632 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -47,7 +47,7 @@
},
"hypothesis": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/HypothesisWorks/hypothesis.git",
"long_checkout": false,
"py_versions": ["all"]
From 172c0a78facd8f938629f216c95507bbfeddfe5e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hakan=20=C3=87elik?=
Date: Mon, 28 Sep 2020 22:55:35 +0300
Subject: [PATCH 074/680] Fix unnecessary if checks (#1728)
---
src/black/__init__.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 9af58014c51..24e9d4edaaa 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -859,9 +859,9 @@ def color_diff(contents: str) -> str:
for i, line in enumerate(lines):
if line.startswith("+++") or line.startswith("---"):
line = "\033[1;37m" + line + "\033[0m" # bold white, reset
- if line.startswith("@@"):
+ elif line.startswith("@@"):
line = "\033[36m" + line + "\033[0m" # cyan, reset
- if line.startswith("+"):
+ elif line.startswith("+"):
line = "\033[32m" + line + "\033[0m" # green, reset
elif line.startswith("-"):
line = "\033[31m" + line + "\033[0m" # red, reset
From 283d999c3f89e2204cbf5a61242665bedd6887ef Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade
Date: Fri, 2 Oct 2020 14:47:57 +0300
Subject: [PATCH 075/680] Primer: pyramid and sqlalchemy are now formatted with
latest Black (#1736)
---
src/black_primer/primer.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index 5818444e632..cdc863ca032 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -77,7 +77,7 @@
},
"pyramid": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/Pylons/pyramid.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -98,7 +98,7 @@
},
"sqlalchemy": {
"cli_arguments": [],
- "expect_formatting_changes": true,
+ "expect_formatting_changes": false,
"git_clone_url": "https://github.com/sqlalchemy/sqlalchemy.git",
"long_checkout": false,
"py_versions": ["all"]
From d9884b6d330d56b2d2ea1d24f1bdf36ff433c0e6 Mon Sep 17 00:00:00 2001
From: Cha Gyuseok
Date: Wed, 7 Oct 2020 23:45:06 +0900
Subject: [PATCH 076/680] docs: update `used-by` link to proper url (#1745)
---
README.md | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index f17b353b023..9c10a63fe29 100644
--- a/README.md
+++ b/README.md
@@ -212,12 +212,13 @@ know!)
### NOTE: This is a beta product
-_Black_ is already [successfully used](#used-by) by many projects, small and big. It
-also sports a decent test suite. However, it is still very new. Things will probably be
-wonky for a while. This is made explicit by the "Beta" trove classifier, as well as by
-the "b" in the version number. What this means for you is that **until the formatter
-becomes stable, you should expect some formatting to change in the future**. That being
-said, no drastic stylistic changes are planned, mostly responses to bug reports.
+_Black_ is already [successfully used](https://github.com/psf/black#used-by) by many
+projects, small and big. It also sports a decent test suite. However, it is still very
+new. Things will probably be wonky for a while. This is made explicit by the "Beta"
+trove classifier, as well as by the "b" in the version number. What this means for you
+is that **until the formatter becomes stable, you should expect some formatting to
+change in the future**. That being said, no drastic stylistic changes are planned,
+mostly responses to bug reports.
Also, as a temporary safety measure, _Black_ will check that the reformatted code still
produces a valid AST that is equivalent to the original. This slows it down. If you're
From dd2f86ac0a043815821d228b9db036a295be5372 Mon Sep 17 00:00:00 2001
From: Hadi Alqattan
Date: Fri, 9 Oct 2020 00:13:13 +0300
Subject: [PATCH 077/680] Support stable Python3.9. (#1748)
* Support stable Python3.9.
* Get back to 3.9-dev
* Add py39 to black usage.
* remove 3.9 temporarily.
---
.github/workflows/doc.yml | 4 ++--
.github/workflows/fuzz.yml | 2 +-
.github/workflows/primer.yml | 2 +-
.github/workflows/test.yml | 2 +-
CONTRIBUTING.md | 2 +-
README.md | 2 +-
docs/contributing_to_black.md | 2 +-
docs/installation_and_usage.md | 2 +-
readthedocs.yml | 2 +-
setup.py | 1 +
10 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index 6023a02a7f7..d266e55aa02 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -21,10 +21,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: Set up Python 3.8
+ - name: Set up Python 3.9
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: 3.9
- name: Install dependencies
run: |
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index 92caa0fd5c1..9aec3c08c63 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: [3.6, 3.7, 3.8]
+ python-version: [3.6, 3.7, 3.8] # Python3.9 should be added after fixing [https://github.com/Zac-HD/hypothesmith/issues/11].
steps:
- uses: actions/checkout@v2
diff --git a/.github/workflows/primer.yml b/.github/workflows/primer.yml
index 9b10db0d285..b623b938345 100644
--- a/.github/workflows/primer.yml
+++ b/.github/workflows/primer.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: [3.6, 3.7, 3.8]
+ python-version: [3.6, 3.7, 3.8, 3.9]
os: [ubuntu-latest, windows-latest]
steps:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index bd0af61e7f5..e30a8b8eb68 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: [3.6, 3.7, 3.8]
+ python-version: [3.6, 3.7, 3.8, 3.9]
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0687aaeee52..d446e6a0806 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -19,7 +19,7 @@ unlikely to get accepted. You can still try but prepare to be disappointed.
## Technicalities
-Development on the latest version of Python is preferred. As of this writing it's 3.8.
+Development on the latest version of Python is preferred. As of this writing it's 3.9.
You can use any operating system. I am using macOS myself and CentOS at work.
Install all development dependencies using:
diff --git a/README.md b/README.md
index 9c10a63fe29..2ca76dd6415 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,7 @@ Options:
-l, --line-length INTEGER How many characters per line to allow.
[default: 88]
- -t, --target-version [py27|py33|py34|py35|py36|py37|py38]
+ -t, --target-version [py27|py33|py34|py35|py36|py37|py38|py39]
Python versions that should be supported by
Black's output. [default: per-file auto-
detection]
diff --git a/docs/contributing_to_black.md b/docs/contributing_to_black.md
index e5307adb5d0..8e332993f97 100644
--- a/docs/contributing_to_black.md
+++ b/docs/contributing_to_black.md
@@ -21,7 +21,7 @@ unlikely to get accepted. You can still try but prepare to be disappointed.
## Technicalities
-Development on the latest version of Python is preferred. As of this writing it's 3.8.
+Development on the latest version of Python is preferred. As of this writing it's 3.9.
You can use any operating system. I am using macOS myself and CentOS at work.
Install all development dependencies using:
diff --git a/docs/installation_and_usage.md b/docs/installation_and_usage.md
index 7d37f59264b..7de8b657adf 100644
--- a/docs/installation_and_usage.md
+++ b/docs/installation_and_usage.md
@@ -41,7 +41,7 @@ Options:
-l, --line-length INTEGER How many characters per line to allow.
[default: 88]
- -t, --target-version [py27|py33|py34|py35|py36|py37|py38]
+ -t, --target-version [py27|py33|py34|py35|py36|py37|py38|py39]
Python versions that should be supported by
Black's output. [default: per-file auto-
detection]
diff --git a/readthedocs.yml b/readthedocs.yml
index 15065033d0f..32bcf1fa50e 100644
--- a/readthedocs.yml
+++ b/readthedocs.yml
@@ -1,6 +1,6 @@
version: 2
python:
- version: 3.8
+ version: 3.9
install:
- requirements: docs/requirements.txt
- method: setuptools
diff --git a/setup.py b/setup.py
index 12fde2568cf..14bc1ef586a 100644
--- a/setup.py
+++ b/setup.py
@@ -93,6 +93,7 @@ def get_long_description() -> str:
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",
From f311d82569b9595d85c08cc8fcf5250de525e7a0 Mon Sep 17 00:00:00 2001
From: Cooper Lees
Date: Mon, 12 Oct 2020 22:03:00 -0700
Subject: [PATCH 078/680] Add blackd to nicely exit if missing aiohttp deps
(#1761)
- If no aiohttp* deps exist nicely print a helpful message and exit
- There seems to be no nice way to optionally install the entry point, so lets make the entry point nicer
Test:
```
cooper-mbp1:black cooper$ /tmp/tb/bin/pip install .
cooper-mbp1:black cooper$ /tmp/tb/bin/blackd
aiohttp dependency is not installed: No module named 'aiohttp'. Please re-install black with the '[d]' extra install to obtain aiohttp_cors: `pip install black[d]`
cooper-mbp1:black cooper$ /tmp/tb/bin/pip install .[d]
...
Successfully installed aiohttp-3.6.3 aiohttp-cors-0.7.0 black
cooper-mbp1:black cooper$ /tmp/tb/bin/blackd
blackd version 20.8b2.dev31+gdd2f86a.d20201013 listening on localhost port 45484
```
Fixes #1688
---
src/blackd/__init__.py | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/blackd/__init__.py b/src/blackd/__init__.py
index d79bfe75bc2..f77a5e8e7be 100644
--- a/src/blackd/__init__.py
+++ b/src/blackd/__init__.py
@@ -1,13 +1,24 @@
import asyncio
+import logging
+import sys
from concurrent.futures import Executor, ProcessPoolExecutor
from datetime import datetime
from functools import partial
-import logging
from multiprocessing import freeze_support
from typing import Set, Tuple
-from aiohttp import web
-import aiohttp_cors
+try:
+ from aiohttp import web
+ import aiohttp_cors
+except ImportError as ie:
+ print(
+ f"aiohttp dependency is not installed: {ie}. "
+ + "Please re-install black with the '[d]' extra install "
+ + "to obtain aiohttp_cors: `pip install black[d]`",
+ file=sys.stderr,
+ )
+ sys.exit(-1)
+
import black
import click
From 4d6a84a8294d035747404d00b88a925fc2640e1e Mon Sep 17 00:00:00 2001
From: Hadi Alqattan
Date: Mon, 19 Oct 2020 00:24:33 +0300
Subject: [PATCH 079/680] Allow black's Github action params overriding.
(#1755)
* Allow default params overriding.
* Update: docs and action.yaml.
* The second contirbution, add my name to authors.md
* Correct docs `with.args` example.
* Just to rerun the Travis jobs.
* chmod 755
---
README.md | 1 +
action.yml | 2 +-
Dockerfile => action/Dockerfile | 4 +++-
action/entrypoint.sh | 10 ++++++++++
docs/authors.md | 1 +
docs/github_actions.md | 4 +++-
6 files changed, 19 insertions(+), 3 deletions(-)
rename Dockerfile => action/Dockerfile (64%)
create mode 100755 action/entrypoint.sh
diff --git a/README.md b/README.md
index 2ca76dd6415..7cd4d7f13b2 100644
--- a/README.md
+++ b/README.md
@@ -574,6 +574,7 @@ Multiple contributions by:
- [Gregory P. Smith](mailto:greg@krypto.org)
- Gustavo Camargo
- hauntsaninja
+- [Hadi Alqattan](mailto:alqattanhadizaki@gmail.com)
- [Heaford](mailto:dan@heaford.com)
- [Hugo Barrera](mailto::hugo@barrera.io)
- Hugo van Kemenade
diff --git a/action.yml b/action.yml
index 2ce1c0bf2ef..60bf369a3d8 100644
--- a/action.yml
+++ b/action.yml
@@ -6,4 +6,4 @@ branding:
icon: "check-circle"
runs:
using: "docker"
- image: "Dockerfile"
+ image: "action/Dockerfile"
diff --git a/Dockerfile b/action/Dockerfile
similarity index 64%
rename from Dockerfile
rename to action/Dockerfile
index a03d23a1078..eb2209940db 100644
--- a/Dockerfile
+++ b/action/Dockerfile
@@ -5,4 +5,6 @@ ENV PYTHONUNBUFFERED 1
RUN pip install --upgrade --no-cache-dir black
-ENTRYPOINT /usr/local/bin/black --check --diff .
+COPY entrypoint.sh /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/action/entrypoint.sh b/action/entrypoint.sh
new file mode 100755
index 00000000000..dc86fa1996e
--- /dev/null
+++ b/action/entrypoint.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+set -e
+
+if [ $# -eq 0 ]; then
+ # Default (if no args provided).
+ sh -c "black . --check --diff"
+else
+ # Custom args.
+ sh -c "black $*"
+fi
diff --git a/docs/authors.md b/docs/authors.md
index a5349b4b9df..ebf64eaae76 100644
--- a/docs/authors.md
+++ b/docs/authors.md
@@ -69,6 +69,7 @@ Multiple contributions by:
- [Gregory P. Smith](mailto:greg@krypto.org)
- Gustavo Camargo
- hauntsaninja
+- [Hadi Alqattan](mailto:alqattanhadizaki@gmail.com)
- [Heaford](mailto:dan@heaford.com)
- [Hugo Barrera](mailto::hugo@barrera.io)
- Hugo van Kemenade
diff --git a/docs/github_actions.md b/docs/github_actions.md
index 7ff87540242..ac80c2fab6f 100644
--- a/docs/github_actions.md
+++ b/docs/github_actions.md
@@ -15,5 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- - uses: psf/black@stable
+ - uses: psf/black@stable # the default is equivalent to `black . --diff --check`.
+ with: # (optional - override the default parameters).
+ args: ". --diff --check"
```
From 3e3da8eef6f4ba48da1505ad68ca0e1d788f6d6b Mon Sep 17 00:00:00 2001
From: "David W.H. Swenson"
Date: Sun, 18 Oct 2020 23:27:15 +0200
Subject: [PATCH 080/680] Fix GitHub markdown links to work on RTD (#1752)
* Fix internal links to work on RTD
Note that these still lead to GitHub, instead of staying on RTD.
* Point links to better anchors
---
CONTRIBUTING.md | 2 +-
README.md | 2 +-
docs/authors.md | 1 +
docs/change_log.md | 6 ++++++
docs/contributing_to_black.md | 2 +-
docs/installation_and_usage.md | 15 ++++++++-------
6 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d446e6a0806..a4250f78718 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -52,7 +52,7 @@ your PR. You may need to change
configuration for it to pass.
For more `black-primer` information visit the
-[documentation](https://github.com/psf/black/blob/master/docs/black_primer.md).
+[documentation](https://github.com/psf/black/blob/master/docs/black_primer.md#black-primer).
## Hygiene
diff --git a/README.md b/README.md
index 7cd4d7f13b2..47472810114 100644
--- a/README.md
+++ b/README.md
@@ -164,7 +164,7 @@ about _Black_'s changes or will overwrite _Black_'s changes. A good example of t
should be configured to neither warn about nor overwrite _Black_'s changes.
Actual details on _Black_ compatible configurations for various tools can be found in
-[compatible_configs](https://github.com/psf/black/blob/master/docs/compatible_configs.md).
+[compatible_configs](https://github.com/psf/black/blob/master/docs/compatible_configs.md#black-compatible-configurations).
### Migrating your code style without ruining git blame
diff --git a/docs/authors.md b/docs/authors.md
index ebf64eaae76..cdf5046c446 100644
--- a/docs/authors.md
+++ b/docs/authors.md
@@ -133,6 +133,7 @@ Multiple contributions by:
- [Paul Ganssle](mailto:p.ganssle@gmail.com)
- [Paul Meinhardt](mailto:mnhrdt@gmail.com)
- [Peter Bengtsson](mailto:mail@peterbe.com)
+- [Peter Grayson](mailto:pete@jpgrayson.net)
- [Peter Stensmyr](mailto:peter.stensmyr@gmail.com)
- pmacosta
- [Quentin Pradet](mailto:quentin@pradet.me)
diff --git a/docs/change_log.md b/docs/change_log.md
index e183ca545b6..1ee35a4d8f9 100644
--- a/docs/change_log.md
+++ b/docs/change_log.md
@@ -17,6 +17,12 @@
- fixed a crash when PWD=/ on POSIX (#1631)
+- fixed "I/O operation on closed file" when using --diff (#1664)
+
+- Prevent coloured diff output being interleaved with multiple files (#1673)
+
+- Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711)
+
### 20.8b1
#### _Packaging_
diff --git a/docs/contributing_to_black.md b/docs/contributing_to_black.md
index 8e332993f97..f0be872a578 100644
--- a/docs/contributing_to_black.md
+++ b/docs/contributing_to_black.md
@@ -54,7 +54,7 @@ your PR. You may need to change
configuration for it to pass.
For more `black-primer` information visit the
-[documentation](https://github.com/psf/black/blob/master/docs/black_primer.md).
+[documentation](https://github.com/psf/black/blob/master/docs/black_primer.md#).
## Hygiene
diff --git a/docs/installation_and_usage.md b/docs/installation_and_usage.md
index 7de8b657adf..c91a1c400ef 100644
--- a/docs/installation_and_usage.md
+++ b/docs/installation_and_usage.md
@@ -119,7 +119,7 @@ about _Black_'s changes or will overwrite _Black_'s changes. A good example of t
should be configured to neither warn about nor overwrite _Black_'s changes.
Actual details on _Black_ compatible configurations for various tools can be found in
-[compatible_configs](https://github.com/psf/black/blob/master/docs/compatible_configs.md).
+[compatible_configs](https://github.com/psf/black/blob/master/docs/compatible_configs.md#).
## Migrating your code style without ruining git blame
@@ -167,12 +167,13 @@ know!)
## NOTE: This is a beta product
-_Black_ is already [successfully used](#used-by) by many projects, small and big. It
-also sports a decent test suite. However, it is still very new. Things will probably be
-wonky for a while. This is made explicit by the "Beta" trove classifier, as well as by
-the "b" in the version number. What this means for you is that **until the formatter
-becomes stable, you should expect some formatting to change in the future**. That being
-said, no drastic stylistic changes are planned, mostly responses to bug reports.
+_Black_ is already [successfully used](https://github.com/psf/black#used-by) by many
+projects, small and big. It also sports a decent test suite. However, it is still very
+new. Things will probably be wonky for a while. This is made explicit by the "Beta"
+trove classifier, as well as by the "b" in the version number. What this means for you
+is that **until the formatter becomes stable, you should expect some formatting to
+change in the future**. That being said, no drastic stylistic changes are planned,
+mostly responses to bug reports.
Also, as a temporary safety measure, _Black_ will check that the reformatted code still
produces a valid AST that is equivalent to the original. This slows it down. If you're
From 3be0aa71d9e39a8fdb6f38cc796709bf4f9c11d9 Mon Sep 17 00:00:00 2001
From: johnthagen
Date: Mon, 19 Oct 2020 13:34:42 -0400
Subject: [PATCH 081/680] Update PyCharm integrations instructions to avoid
running on external changes (#1769)
---
docs/editor_integration.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/editor_integration.md b/docs/editor_integration.md
index 73107d6a4a1..21a6865d60e 100644
--- a/docs/editor_integration.md
+++ b/docs/editor_integration.md
@@ -72,7 +72,9 @@ On Windows / Linux / BSD:
- Output paths to refresh: `$FilePath$`
- Working directory: `$ProjectFileDir$`
- - Uncheck "Auto-save edited files to trigger the watcher" in Advanced Options
+ - In Advanced Options
+ - Uncheck "Auto-save edited files to trigger the watcher"
+ - Uncheck "Trigger the watcher on external changes"
## Wing IDE
From 407052724fa1c97ee8bcd4e96de650def00be03e Mon Sep 17 00:00:00 2001
From: Sagi Shadur
Date: Mon, 19 Oct 2020 20:35:26 +0300
Subject: [PATCH 082/680] Switch to pytest and tox (#1763)
* Add venv to .gitignore
* Use tox to run tests
* Make fuzz run in tox
* Split tests files
* Fix import error
---
.github/workflows/fuzz.yml | 7 +-
.github/workflows/test.yml | 5 +-
.gitignore | 1 +
test_requirements.txt | 4 +
tests/test_black.py | 230 +------------------------------------
tests/test_blackd.py | 192 +++++++++++++++++++++++++++++++
tests/util.py | 43 +++++++
tox.ini | 26 +++++
8 files changed, 275 insertions(+), 233 deletions(-)
create mode 100644 test_requirements.txt
create mode 100644 tests/test_blackd.py
create mode 100644 tests/util.py
create mode 100644 tox.ini
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index 9aec3c08c63..343eed10df8 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -21,11 +21,8 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- python -m pip install --upgrade coverage
- python -m pip install --upgrade hypothesmith
- python -m pip install -e ".[d]"
+ python -m pip install --upgrade tox
- name: Run fuzz tests
run: |
- coverage run fuzz.py
- coverage report
+ tox -e fuzz
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index e30a8b8eb68..8dd4e4f59fd 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -22,9 +22,8 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- python -m pip install --upgrade coverage
- python -m pip install -e ".[d]"
+ python -m pip install --upgrade tox
- name: Unit tests
run: |
- coverage run -m unittest
+ tox -e py
diff --git a/.gitignore b/.gitignore
index 6b94cacd183..3207e72ae28 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ src/_black_version.py
.dmypy.json
*.swp
.hypothesis/
+venv/
\ No newline at end of file
diff --git a/test_requirements.txt b/test_requirements.txt
new file mode 100644
index 00000000000..999d05c0106
--- /dev/null
+++ b/test_requirements.txt
@@ -0,0 +1,4 @@
+pytest >= 6.0.1
+pytest-mock >= 3.2.0
+pytest-cases >= 2.1.2
+coverage >= 5.2.1
\ No newline at end of file
diff --git a/tests/test_black.py b/tests/test_black.py
index e17f43b88d4..7927b368b5d 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -22,7 +22,6 @@
Dict,
Generator,
List,
- Tuple,
Iterator,
TypeVar,
)
@@ -36,18 +35,10 @@
import black
from black import Feature, TargetVersion
-try:
- import blackd
- from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
- from aiohttp import web
-except ImportError:
- has_blackd_deps = False
-else:
- has_blackd_deps = True
-
from pathspec import PathSpec
# Import other test classes
+from tests.util import THIS_DIR, read_data, DETERMINISTIC_HEADER
from .test_primer import PrimerCLITests # noqa: F401
@@ -55,10 +46,6 @@
ff = partial(black.format_file_in_place, mode=DEFAULT_MODE, fast=True)
fs = partial(black.format_str, mode=DEFAULT_MODE)
THIS_FILE = Path(__file__)
-THIS_DIR = THIS_FILE.parent
-PROJECT_ROOT = THIS_DIR.parent
-DETERMINISTIC_HEADER = "[Deterministic header]"
-EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
PY36_VERSIONS = {
TargetVersion.PY36,
TargetVersion.PY37,
@@ -74,29 +61,6 @@ def dump_to_stderr(*output: str) -> str:
return "\n" + "\n".join(output) + "\n"
-def read_data(name: str, data: bool = True) -> Tuple[str, str]:
- """read_data('test_name') -> 'input', 'output'"""
- if not name.endswith((".py", ".pyi", ".out", ".diff")):
- name += ".py"
- _input: List[str] = []
- _output: List[str] = []
- base_dir = THIS_DIR / "data" if data else PROJECT_ROOT
- with open(base_dir / name, "r", encoding="utf8") as test:
- lines = test.readlines()
- result = _input
- for line in lines:
- line = line.replace(EMPTY_LINE, "")
- if line.rstrip() == "# output":
- result = _output
- continue
-
- result.append(line)
- if _input and not _output:
- # If there's no output marker, treat the entire file as already pre-formatted.
- _output = _input[:]
- return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
-
-
@contextmanager
def cache_dir(exists: bool = True) -> Iterator[Path]:
with TemporaryDirectory() as workspace:
@@ -119,17 +83,6 @@ def event_loop() -> Iterator[None]:
loop.close()
-@contextmanager
-def skip_if_exception(e: str) -> Iterator[None]:
- try:
- yield
- except Exception as exc:
- if exc.__class__.__name__ == e:
- unittest.skip(f"Encountered expected exception {exc}, skipping")
- else:
- raise
-
-
class FakeContext(click.Context):
"""A fake click Context for when calling functions that need it."""
@@ -239,9 +192,12 @@ def test_empty_ff(self) -> None:
os.unlink(tmp_file)
self.assertFormatEqual(expected, actual)
- def test_self(self) -> None:
+ def test_run_on_test_black(self) -> None:
self.checkSourceFile("tests/test_black.py")
+ def test_run_on_test_blackd(self) -> None:
+ self.checkSourceFile("tests/test_blackd.py")
+
def test_black(self) -> None:
self.checkSourceFile("src/black/__init__.py")
@@ -1969,14 +1925,6 @@ def fail(*args: Any, **kwargs: Any) -> None:
):
ff(THIS_FILE)
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- def test_blackd_main(self) -> None:
- with patch("blackd.web.run_app"):
- result = CliRunner().invoke(blackd.main, [])
- if result.exception is not None:
- raise result.exception
- self.assertEqual(result.exit_code, 0)
-
def test_invalid_config_return_code(self) -> None:
tmp_file = Path(black.dump_to_file())
try:
@@ -2053,174 +2001,6 @@ def test_bpo_33660_workaround(self) -> None:
os.chdir(str(old_cwd))
-class BlackDTestCase(AioHTTPTestCase):
- async def get_application(self) -> web.Application:
- return blackd.make_app()
-
- # TODO: remove these decorators once the below is released
- # https://github.com/aio-libs/aiohttp/pull/3727
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_request_needs_formatting(self) -> None:
- response = await self.client.post("/", data=b"print('hello world')")
- self.assertEqual(response.status, 200)
- self.assertEqual(response.charset, "utf8")
- self.assertEqual(await response.read(), b'print("hello world")\n')
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_request_no_change(self) -> None:
- response = await self.client.post("/", data=b'print("hello world")\n')
- self.assertEqual(response.status, 204)
- self.assertEqual(await response.read(), b"")
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_request_syntax_error(self) -> None:
- response = await self.client.post("/", data=b"what even ( is")
- self.assertEqual(response.status, 400)
- content = await response.text()
- self.assertTrue(
- content.startswith("Cannot parse"),
- msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
- )
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_unsupported_version(self) -> None:
- response = await self.client.post(
- "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
- )
- self.assertEqual(response.status, 501)
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_supported_version(self) -> None:
- response = await self.client.post(
- "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
- )
- self.assertEqual(response.status, 200)
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_invalid_python_variant(self) -> None:
- async def check(header_value: str, expected_status: int = 400) -> None:
- response = await self.client.post(
- "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
- )
- self.assertEqual(response.status, expected_status)
-
- await check("lol")
- await check("ruby3.5")
- await check("pyi3.6")
- await check("py1.5")
- await check("2.8")
- await check("py2.8")
- await check("3.0")
- await check("pypy3.0")
- await check("jython3.4")
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_pyi(self) -> None:
- source, expected = read_data("stub.pyi")
- response = await self.client.post(
- "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
- )
- self.assertEqual(response.status, 200)
- self.assertEqual(await response.text(), expected)
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_diff(self) -> None:
- diff_header = re.compile(
- r"(In|Out)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
- )
-
- source, _ = read_data("blackd_diff.py")
- expected, _ = read_data("blackd_diff.diff")
-
- response = await self.client.post(
- "/", data=source, headers={blackd.DIFF_HEADER: "true"}
- )
- self.assertEqual(response.status, 200)
-
- actual = await response.text()
- actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
- self.assertEqual(actual, expected)
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_python_variant(self) -> None:
- code = (
- "def f(\n"
- " and_has_a_bunch_of,\n"
- " very_long_arguments_too,\n"
- " and_lots_of_them_as_well_lol,\n"
- " **and_very_long_keyword_arguments\n"
- "):\n"
- " pass\n"
- )
-
- async def check(header_value: str, expected_status: int) -> None:
- response = await self.client.post(
- "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
- )
- self.assertEqual(
- response.status, expected_status, msg=await response.text()
- )
-
- await check("3.6", 200)
- await check("py3.6", 200)
- await check("3.6,3.7", 200)
- await check("3.6,py3.7", 200)
- await check("py36,py37", 200)
- await check("36", 200)
- await check("3.6.4", 200)
-
- await check("2", 204)
- await check("2.7", 204)
- await check("py2.7", 204)
- await check("3.4", 204)
- await check("py3.4", 204)
- await check("py34,py36", 204)
- await check("34", 204)
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_line_length(self) -> None:
- response = await self.client.post(
- "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
- )
- self.assertEqual(response.status, 200)
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_invalid_line_length(self) -> None:
- response = await self.client.post(
- "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
- )
- self.assertEqual(response.status, 400)
-
- @skip_if_exception("ClientOSError")
- @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
- @unittest_run_loop
- async def test_blackd_response_black_version_header(self) -> None:
- response = await self.client.post("/")
- self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
-
-
with open(black.__file__, "r", encoding="utf-8") as _bf:
black_source_lines = _bf.readlines()
diff --git a/tests/test_blackd.py b/tests/test_blackd.py
new file mode 100644
index 00000000000..9127297c54f
--- /dev/null
+++ b/tests/test_blackd.py
@@ -0,0 +1,192 @@
+import re
+import unittest
+from unittest.mock import patch
+
+from click.testing import CliRunner
+
+from tests.util import read_data, DETERMINISTIC_HEADER, skip_if_exception
+
+try:
+ import blackd
+ from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
+ from aiohttp import web
+except ImportError:
+ has_blackd_deps = False
+else:
+ has_blackd_deps = True
+
+
+class BlackDTestCase(AioHTTPTestCase):
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ def test_blackd_main(self) -> None:
+ with patch("blackd.web.run_app"):
+ result = CliRunner().invoke(blackd.main, [])
+ if result.exception is not None:
+ raise result.exception
+ self.assertEqual(result.exit_code, 0)
+
+ async def get_application(self) -> web.Application:
+ return blackd.make_app()
+
+ # TODO: remove these decorators once the below is released
+ # https://github.com/aio-libs/aiohttp/pull/3727
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_request_needs_formatting(self) -> None:
+ response = await self.client.post("/", data=b"print('hello world')")
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.charset, "utf8")
+ self.assertEqual(await response.read(), b'print("hello world")\n')
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_request_no_change(self) -> None:
+ response = await self.client.post("/", data=b'print("hello world")\n')
+ self.assertEqual(response.status, 204)
+ self.assertEqual(await response.read(), b"")
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_request_syntax_error(self) -> None:
+ response = await self.client.post("/", data=b"what even ( is")
+ self.assertEqual(response.status, 400)
+ content = await response.text()
+ self.assertTrue(
+ content.startswith("Cannot parse"),
+ msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
+ )
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_unsupported_version(self) -> None:
+ response = await self.client.post(
+ "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
+ )
+ self.assertEqual(response.status, 501)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_supported_version(self) -> None:
+ response = await self.client.post(
+ "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
+ )
+ self.assertEqual(response.status, 200)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_invalid_python_variant(self) -> None:
+ async def check(header_value: str, expected_status: int = 400) -> None:
+ response = await self.client.post(
+ "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
+ )
+ self.assertEqual(response.status, expected_status)
+
+ await check("lol")
+ await check("ruby3.5")
+ await check("pyi3.6")
+ await check("py1.5")
+ await check("2.8")
+ await check("py2.8")
+ await check("3.0")
+ await check("pypy3.0")
+ await check("jython3.4")
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_pyi(self) -> None:
+ source, expected = read_data("stub.pyi")
+ response = await self.client.post(
+ "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
+ )
+ self.assertEqual(response.status, 200)
+ self.assertEqual(await response.text(), expected)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_diff(self) -> None:
+ diff_header = re.compile(
+ r"(In|Out)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
+ )
+
+ source, _ = read_data("blackd_diff.py")
+ expected, _ = read_data("blackd_diff.diff")
+
+ response = await self.client.post(
+ "/", data=source, headers={blackd.DIFF_HEADER: "true"}
+ )
+ self.assertEqual(response.status, 200)
+
+ actual = await response.text()
+ actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
+ self.assertEqual(actual, expected)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_python_variant(self) -> None:
+ code = (
+ "def f(\n"
+ " and_has_a_bunch_of,\n"
+ " very_long_arguments_too,\n"
+ " and_lots_of_them_as_well_lol,\n"
+ " **and_very_long_keyword_arguments\n"
+ "):\n"
+ " pass\n"
+ )
+
+ async def check(header_value: str, expected_status: int) -> None:
+ response = await self.client.post(
+ "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
+ )
+ self.assertEqual(
+ response.status, expected_status, msg=await response.text()
+ )
+
+ await check("3.6", 200)
+ await check("py3.6", 200)
+ await check("3.6,3.7", 200)
+ await check("3.6,py3.7", 200)
+ await check("py36,py37", 200)
+ await check("36", 200)
+ await check("3.6.4", 200)
+
+ await check("2", 204)
+ await check("2.7", 204)
+ await check("py2.7", 204)
+ await check("3.4", 204)
+ await check("py3.4", 204)
+ await check("py34,py36", 204)
+ await check("34", 204)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_line_length(self) -> None:
+ response = await self.client.post(
+ "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
+ )
+ self.assertEqual(response.status, 200)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_invalid_line_length(self) -> None:
+ response = await self.client.post(
+ "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
+ )
+ self.assertEqual(response.status, 400)
+
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_response_black_version_header(self) -> None:
+ response = await self.client.post("/")
+ self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
diff --git a/tests/util.py b/tests/util.py
new file mode 100644
index 00000000000..9c3d3cbc99d
--- /dev/null
+++ b/tests/util.py
@@ -0,0 +1,43 @@
+import unittest
+from contextlib import contextmanager
+from pathlib import Path
+from typing import List, Tuple, Iterator
+
+THIS_DIR = Path(__file__).parent
+PROJECT_ROOT = THIS_DIR.parent
+EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
+DETERMINISTIC_HEADER = "[Deterministic header]"
+
+
+@contextmanager
+def skip_if_exception(e: str) -> Iterator[None]:
+ try:
+ yield
+ except Exception as exc:
+ if exc.__class__.__name__ == e:
+ unittest.skip(f"Encountered expected exception {exc}, skipping")
+ else:
+ raise
+
+
+def read_data(name: str, data: bool = True) -> Tuple[str, str]:
+ """read_data('test_name') -> 'input', 'output'"""
+ if not name.endswith((".py", ".pyi", ".out", ".diff")):
+ name += ".py"
+ _input: List[str] = []
+ _output: List[str] = []
+ base_dir = THIS_DIR / "data" if data else PROJECT_ROOT
+ with open(base_dir / name, "r", encoding="utf8") as test:
+ lines = test.readlines()
+ result = _input
+ for line in lines:
+ line = line.replace(EMPTY_LINE, "")
+ if line.rstrip() == "# output":
+ result = _output
+ continue
+
+ result.append(line)
+ if _input and not _output:
+ # If there's no output marker, treat the entire file as already pre-formatted.
+ _output = _input[:]
+ return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000000..500a2cad579
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,26 @@
+[tox]
+envlist = py{36,37,38,39},fuzz
+
+[testenv]
+setenv = PYTHONPATH = {toxinidir}/src
+skip_install = True
+deps =
+ -r{toxinidir}/test_requirements.txt
+commands =
+ pip install -e .[d]
+ coverage erase
+ coverage run -m pytest tests
+ coverage report
+
+[testenv:fuzz]
+skip_install = True
+deps =
+ -r{toxinidir}/test_requirements.txt
+ hypothesmith
+ lark-parser < 0.10.0
+; lark-parser's version is set due to a bug in hypothesis. Once it solved, that would be fixed.
+commands =
+ pip install -e .[d]
+ coverage erase
+ coverage run fuzz.py
+ coverage report
\ No newline at end of file
From 989ea69bd1d0a02aa6c5794d941c122f7b4958b4 Mon Sep 17 00:00:00 2001
From: Antek S <3324881+bluefish6@users.noreply.github.com>
Date: Tue, 27 Oct 2020 11:52:19 +0100
Subject: [PATCH 083/680] Update readme.md with current version (#1788)
* Update readme.md with current version
* Update version_control_integration.md
---
README.md | 2 +-
docs/version_control_integration.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 47472810114..d34ce146e0d 100644
--- a/README.md
+++ b/README.md
@@ -372,7 +372,7 @@ Use [pre-commit](https://pre-commit.com/). Once you
```yaml
repos:
- repo: https://github.com/psf/black
- rev: 19.10b0 # Replace by any tag/version: https://github.com/psf/black/tags
+ rev: 20.8b1 # Replace by any tag/version: https://github.com/psf/black/tags
hooks:
- id: black
language_version: python3 # Should be a command that runs python3.6+
diff --git a/docs/version_control_integration.md b/docs/version_control_integration.md
index 25dac308c47..2d8bc172eba 100644
--- a/docs/version_control_integration.md
+++ b/docs/version_control_integration.md
@@ -9,7 +9,7 @@ Use [pre-commit](https://pre-commit.com/). Once you
```yaml
repos:
- repo: https://github.com/psf/black
- rev: 19.10b0 # Replace by any tag/version: https://github.com/psf/black/tags
+ rev: 20.8b1 # Replace by any tag/version: https://github.com/psf/black/tags
hooks:
- id: black
language_version: python3 # Should be a command that runs python3.6+
From cabeb5b5457b8fd29d608eca944be6074bd3f31f Mon Sep 17 00:00:00 2001
From: Bibo-Joshi
Date: Tue, 27 Oct 2020 21:59:43 +0100
Subject: [PATCH 084/680] Document some culprits with pre-commit (#1783)
* Document some culprits with pre-commit
* make pre-commit happy
* don't use monospace for black & pre-commit
Co-authored-by: Hugo van Kemenade
* make pre-commit happy again
Co-authored-by: Hugo van Kemenade
---
docs/version_control_integration.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/docs/version_control_integration.md b/docs/version_control_integration.md
index 2d8bc172eba..0e09854a2e5 100644
--- a/docs/version_control_integration.md
+++ b/docs/version_control_integration.md
@@ -23,6 +23,11 @@ for your project. See _Black_'s own
[pyproject.toml](https://github.com/psf/black/blob/master/pyproject.toml) for an
example.
+When using the `--diff` flag with `pre-commit`, you must also use the `--check` flag.
+When you want to run _Black_ only on specific files in pre-commit, either use
+pre-commit's own `files` and `exclude` or, when using _Black_'s `--include`, set
+`--force-exclude` to the negated regex of `--include`.
+
If you're already using Python 3.7, switch the `language_version` accordingly. Finally,
`stable` is a branch that tracks the latest release on PyPI. If you'd rather run on
master, this is also an option.
From e6cd10e7615f4df537e2eaefcf3904a4feecad1f Mon Sep 17 00:00:00 2001
From: Sagi Shadur
Date: Fri, 30 Oct 2020 17:12:04 +0200
Subject: [PATCH 085/680] Extract formatting tests (#1785)
* Update test versions
* Use parametrize to remove tests duplications
* Extract sources format tests
* Fix mypy errors
* Fix .travis.yml
---
.travis.yml | 4 +-
test_requirements.txt | 9 +-
tests/test_black.py | 416 ++----------------------------------------
tests/test_format.py | 98 ++++++++++
tests/util.py | 48 ++++-
5 files changed, 160 insertions(+), 415 deletions(-)
create mode 100644 tests/test_format.py
diff --git a/.travis.yml b/.travis.yml
index 86cf24df51b..f035343c6fb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,9 +4,9 @@ cache:
directories:
- $HOME/.cache/pre-commit
env:
- - TEST_CMD="coverage run -m unittest"
+ - TEST_CMD="tox -e py"
install:
- - pip install coverage coveralls pre-commit
+ - pip install coverage coveralls pre-commit tox
- pip install -e '.[d]'
script:
- $TEST_CMD
diff --git a/test_requirements.txt b/test_requirements.txt
index 999d05c0106..3e65cdb669f 100644
--- a/test_requirements.txt
+++ b/test_requirements.txt
@@ -1,4 +1,5 @@
-pytest >= 6.0.1
-pytest-mock >= 3.2.0
-pytest-cases >= 2.1.2
-coverage >= 5.2.1
\ No newline at end of file
+pytest >= 6.1.1
+pytest-mock >= 3.3.1
+pytest-cases >= 2.3.0
+coverage >= 5.3
+parameterized >= 0.7.4
\ No newline at end of file
diff --git a/tests/test_black.py b/tests/test_black.py
index 7927b368b5d..b0cf6ed5caa 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -5,7 +5,6 @@
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager
from dataclasses import replace
-from functools import partial
import inspect
from io import BytesIO, TextIOWrapper
import os
@@ -38,13 +37,19 @@
from pathspec import PathSpec
# Import other test classes
-from tests.util import THIS_DIR, read_data, DETERMINISTIC_HEADER
+from tests.util import (
+ THIS_DIR,
+ read_data,
+ DETERMINISTIC_HEADER,
+ BlackBaseTestCase,
+ DEFAULT_MODE,
+ fs,
+ ff,
+ dump_to_stderr,
+)
from .test_primer import PrimerCLITests # noqa: F401
-DEFAULT_MODE = black.FileMode(experimental_string_processing=True)
-ff = partial(black.format_file_in_place, mode=DEFAULT_MODE, fast=True)
-fs = partial(black.format_str, mode=DEFAULT_MODE)
THIS_FILE = Path(__file__)
PY36_VERSIONS = {
TargetVersion.PY36,
@@ -57,10 +62,6 @@
R = TypeVar("R")
-def dump_to_stderr(*output: str) -> str:
- return "\n" + "\n".join(output) + "\n"
-
-
@contextmanager
def cache_dir(exists: bool = True) -> Iterator[Path]:
with TemporaryDirectory() as workspace:
@@ -122,29 +123,7 @@ def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None
sys.stderr = hold_stderr
-class BlackTestCase(unittest.TestCase):
- maxDiff = None
- _diffThreshold = 2 ** 20
-
- def assertFormatEqual(self, expected: str, actual: str) -> None:
- if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
- bdv: black.DebugVisitor[Any]
- black.out("Expected tree:", fg="green")
- try:
- exp_node = black.lib2to3_parse(expected)
- bdv = black.DebugVisitor()
- list(bdv.visit(exp_node))
- except Exception as ve:
- black.err(str(ve))
- black.out("Actual tree:", fg="red")
- try:
- exp_node = black.lib2to3_parse(actual)
- bdv = black.DebugVisitor()
- list(bdv.visit(exp_node))
- except Exception as ve:
- black.err(str(ve))
- self.assertMultiLineEqual(expected, actual)
-
+class BlackTestCase(BlackBaseTestCase):
def invokeBlack(
self, args: List[str], exit_code: int = 0, ignore_config: bool = True
) -> None:
@@ -163,16 +142,6 @@ def invokeBlack(
),
)
- @patch("black.dump_to_file", dump_to_stderr)
- def checkSourceFile(self, name: str, mode: black.FileMode = DEFAULT_MODE) -> None:
- path = THIS_DIR.parent / name
- source, expected = read_data(str(path), data=False)
- actual = fs(source, mode=mode)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, mode)
- self.assertFalse(ff(path))
-
@patch("black.dump_to_file", dump_to_stderr)
def test_empty(self) -> None:
source = expected = ""
@@ -192,48 +161,6 @@ def test_empty_ff(self) -> None:
os.unlink(tmp_file)
self.assertFormatEqual(expected, actual)
- def test_run_on_test_black(self) -> None:
- self.checkSourceFile("tests/test_black.py")
-
- def test_run_on_test_blackd(self) -> None:
- self.checkSourceFile("tests/test_blackd.py")
-
- def test_black(self) -> None:
- self.checkSourceFile("src/black/__init__.py")
-
- def test_pygram(self) -> None:
- self.checkSourceFile("src/blib2to3/pygram.py")
-
- def test_pytree(self) -> None:
- self.checkSourceFile("src/blib2to3/pytree.py")
-
- def test_conv(self) -> None:
- self.checkSourceFile("src/blib2to3/pgen2/conv.py")
-
- def test_driver(self) -> None:
- self.checkSourceFile("src/blib2to3/pgen2/driver.py")
-
- def test_grammar(self) -> None:
- self.checkSourceFile("src/blib2to3/pgen2/grammar.py")
-
- def test_literals(self) -> None:
- self.checkSourceFile("src/blib2to3/pgen2/literals.py")
-
- def test_parse(self) -> None:
- self.checkSourceFile("src/blib2to3/pgen2/parse.py")
-
- def test_pgen(self) -> None:
- self.checkSourceFile("src/blib2to3/pgen2/pgen.py")
-
- def test_tokenize(self) -> None:
- self.checkSourceFile("src/blib2to3/pgen2/tokenize.py")
-
- def test_token(self) -> None:
- self.checkSourceFile("src/blib2to3/pgen2/token.py")
-
- def test_setup(self) -> None:
- self.checkSourceFile("setup.py")
-
def test_piping(self) -> None:
source, expected = read_data("src/black/__init__", data=False)
result = BlackRunner().invoke(
@@ -291,22 +218,6 @@ def test_piping_diff_with_color(self) -> None:
self.assertIn("\033[31m", actual)
self.assertIn("\033[0m", actual)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_function(self) -> None:
- source, expected = read_data("function")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_function2(self) -> None:
- source, expected = read_data("function2")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
@patch("black.dump_to_file", dump_to_stderr)
def _test_wip(self) -> None:
source, expected = read_data("wip")
@@ -322,14 +233,6 @@ def _test_wip(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, black.FileMode())
- @patch("black.dump_to_file", dump_to_stderr)
- def test_function_trailing_comma(self) -> None:
- source, expected = read_data("function_trailing_comma")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
@unittest.expectedFailure
@patch("black.dump_to_file", dump_to_stderr)
def test_trailing_comma_optional_parens_stability1(self) -> None:
@@ -351,14 +254,6 @@ def test_trailing_comma_optional_parens_stability3(self) -> None:
actual = fs(source)
black.assert_stable(source, actual, DEFAULT_MODE)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_expression(self) -> None:
- source, expected = read_data("expression")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
@patch("black.dump_to_file", dump_to_stderr)
def test_pep_572(self) -> None:
source, expected = read_data("pep_572")
@@ -434,14 +329,6 @@ def test_expression_diff_with_color(self) -> None:
self.assertIn("\033[31m", actual)
self.assertIn("\033[0m", actual)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_fstring(self) -> None:
- source, expected = read_data("fstring")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
@patch("black.dump_to_file", dump_to_stderr)
def test_pep_570(self) -> None:
source, expected = read_data("pep_570")
@@ -472,14 +359,6 @@ def test_string_quotes(self) -> None:
black.assert_equivalent(source, not_normalized)
black.assert_stable(source, not_normalized, mode=mode)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_docstring(self) -> None:
- source, expected = read_data("docstring")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
@patch("black.dump_to_file", dump_to_stderr)
def test_docstring_no_string_normalization(self) -> None:
"""Like test_docstring but with string normalization off."""
@@ -490,14 +369,6 @@ def test_docstring_no_string_normalization(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, mode)
- def test_long_strings(self) -> None:
- """Tests for splitting long strings."""
- source, expected = read_data("long_strings")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
def test_long_strings_flag_disabled(self) -> None:
"""Tests for turning off the string processing logic."""
source, expected = read_data("long_strings_flag_disabled")
@@ -506,162 +377,6 @@ def test_long_strings_flag_disabled(self) -> None:
self.assertFormatEqual(expected, actual)
black.assert_stable(expected, actual, mode)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_long_strings__edge_case(self) -> None:
- """Edge-case tests for splitting long strings."""
- source, expected = read_data("long_strings__edge_case")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_long_strings__regression(self) -> None:
- """Regression tests for splitting long strings."""
- source, expected = read_data("long_strings__regression")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_slices(self) -> None:
- source, expected = read_data("slices")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_percent_precedence(self) -> None:
- source, expected = read_data("percent_precedence")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_comments(self) -> None:
- source, expected = read_data("comments")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_comments2(self) -> None:
- source, expected = read_data("comments2")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_comments3(self) -> None:
- source, expected = read_data("comments3")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_comments4(self) -> None:
- source, expected = read_data("comments4")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_comments5(self) -> None:
- source, expected = read_data("comments5")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_comments6(self) -> None:
- source, expected = read_data("comments6")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_comments7(self) -> None:
- source, expected = read_data("comments7")
- mode = replace(DEFAULT_MODE, target_versions={black.TargetVersion.PY38})
- actual = fs(source, mode=mode)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_comment_after_escaped_newline(self) -> None:
- source, expected = read_data("comment_after_escaped_newline")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_cantfit(self) -> None:
- source, expected = read_data("cantfit")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_import_spacing(self) -> None:
- source, expected = read_data("import_spacing")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_composition(self) -> None:
- source, expected = read_data("composition")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_composition_no_trailing_comma(self) -> None:
- source, expected = read_data("composition_no_trailing_comma")
- mode = replace(DEFAULT_MODE, target_versions={black.TargetVersion.PY38})
- actual = fs(source, mode=mode)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_empty_lines(self) -> None:
- source, expected = read_data("empty_lines")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_remove_parens(self) -> None:
- source, expected = read_data("remove_parens")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_string_prefixes(self) -> None:
- source, expected = read_data("string_prefixes")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
@patch("black.dump_to_file", dump_to_stderr)
def test_numeric_literals(self) -> None:
source, expected = read_data("numeric_literals")
@@ -680,21 +395,6 @@ def test_numeric_literals_ignoring_underscores(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, mode)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_numeric_literals_py2(self) -> None:
- source, expected = read_data("numeric_literals_py2")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_python2(self) -> None:
- source, expected = read_data("python2")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
@patch("black.dump_to_file", dump_to_stderr)
def test_python2_print_function(self) -> None:
source, expected = read_data("python2_print_function")
@@ -704,14 +404,6 @@ def test_python2_print_function(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, mode)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_python2_unicode_literals(self) -> None:
- source, expected = read_data("python2_unicode_literals")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
@patch("black.dump_to_file", dump_to_stderr)
def test_stub(self) -> None:
mode = replace(DEFAULT_MODE, is_pyi=True)
@@ -770,78 +462,6 @@ def test_python39(self) -> None:
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_fmtonoff(self) -> None:
- source, expected = read_data("fmtonoff")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_fmtonoff2(self) -> None:
- source, expected = read_data("fmtonoff2")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_fmtonoff3(self) -> None:
- source, expected = read_data("fmtonoff3")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_fmtonoff4(self) -> None:
- source, expected = read_data("fmtonoff4")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_remove_empty_parentheses_after_class(self) -> None:
- source, expected = read_data("class_blank_parentheses")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_new_line_between_class_and_code(self) -> None:
- source, expected = read_data("class_methods_new_line")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_bracket_match(self) -> None:
- source, expected = read_data("bracketmatch")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_tuple_assign(self) -> None:
- source, expected = read_data("tupleassign")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
- @patch("black.dump_to_file", dump_to_stderr)
- def test_beginning_backslash(self) -> None:
- source, expected = read_data("beginning_backslash")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
def test_tab_comment_indentation(self) -> None:
contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
@@ -1569,13 +1189,6 @@ def test_read_cache_line_lengths(self) -> None:
two = black.read_cache(short_mode)
self.assertNotIn(path, two)
- def test_tricky_unicode_symbols(self) -> None:
- source, expected = read_data("tricky_unicode_symbols")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
def test_single_file_force_pyi(self) -> None:
pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
contents, expected = read_data("force_pyi")
@@ -1672,13 +1285,6 @@ def test_multi_file_force_py36(self) -> None:
self.assertIn(path, pyi_cache)
self.assertNotIn(path, normal_cache)
- def test_collections(self) -> None:
- source, expected = read_data("collections")
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, DEFAULT_MODE)
-
def test_pipe_force_py36(self) -> None:
source, expected = read_data("force_py36")
result = CliRunner().invoke(
diff --git a/tests/test_format.py b/tests/test_format.py
new file mode 100644
index 00000000000..e4677707e3c
--- /dev/null
+++ b/tests/test_format.py
@@ -0,0 +1,98 @@
+from unittest.mock import patch
+
+import black
+from parameterized import parameterized
+
+from tests.util import (
+ BlackBaseTestCase,
+ fs,
+ ff,
+ DEFAULT_MODE,
+ dump_to_stderr,
+ read_data,
+ THIS_DIR,
+)
+
+SIMPLE_CASES = [
+ "beginning_backslash",
+ "bracketmatch",
+ "cantfit",
+ "class_blank_parentheses",
+ "class_methods_new_line",
+ "collections",
+ "comments",
+ "comments2",
+ "comments3",
+ "comments4",
+ "comments5",
+ "comments6",
+ "comments7",
+ "comment_after_escaped_newline",
+ "composition",
+ "composition_no_trailing_comma",
+ "docstring",
+ "empty_lines",
+ "expression",
+ "fmtonoff",
+ "fmtonoff2",
+ "fmtonoff3",
+ "fmtonoff4",
+ "fstring",
+ "function",
+ "function2",
+ "function_trailing_comma",
+ "import_spacing",
+ "long_strings",
+ "long_strings__edge_case",
+ "long_strings__regression",
+ "numeric_literals_py2",
+ "percent_precedence",
+ "python2",
+ "python2_unicode_literals",
+ "remove_parens",
+ "slices",
+ "string_prefixes",
+ "tricky_unicode_symbols",
+ "tupleassign",
+]
+
+
+SOURCES = [
+ "tests/test_black.py",
+ "tests/test_format.py",
+ "tests/test_blackd.py",
+ "src/black/__init__.py",
+ "src/blib2to3/pygram.py",
+ "src/blib2to3/pytree.py",
+ "src/blib2to3/pgen2/conv.py",
+ "src/blib2to3/pgen2/driver.py",
+ "src/blib2to3/pgen2/grammar.py",
+ "src/blib2to3/pgen2/literals.py",
+ "src/blib2to3/pgen2/parse.py",
+ "src/blib2to3/pgen2/pgen.py",
+ "src/blib2to3/pgen2/tokenize.py",
+ "src/blib2to3/pgen2/token.py",
+ "setup.py",
+]
+
+
+class TestSimpleFormat(BlackBaseTestCase):
+ @parameterized.expand(SIMPLE_CASES)
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_simple_format(self, filename: str) -> None:
+ source, expected = read_data(filename)
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @parameterized.expand(SOURCES)
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_source_is_formatted(self, filename: str) -> None:
+ path = THIS_DIR.parent / filename
+ source, expected = read_data(str(path), data=False)
+ actual = fs(source, mode=DEFAULT_MODE)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+ self.assertFalse(ff(path))
diff --git a/tests/util.py b/tests/util.py
index 9c3d3cbc99d..da65ed0cc93 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -1,7 +1,10 @@
+import os
import unittest
from contextlib import contextmanager
from pathlib import Path
-from typing import List, Tuple, Iterator
+from typing import List, Tuple, Iterator, Any
+import black
+from functools import partial
THIS_DIR = Path(__file__).parent
PROJECT_ROOT = THIS_DIR.parent
@@ -9,6 +12,39 @@
DETERMINISTIC_HEADER = "[Deterministic header]"
+DEFAULT_MODE = black.FileMode(experimental_string_processing=True)
+ff = partial(black.format_file_in_place, mode=DEFAULT_MODE, fast=True)
+fs = partial(black.format_str, mode=DEFAULT_MODE)
+
+
+def dump_to_stderr(*output: str) -> str:
+ return "\n" + "\n".join(output) + "\n"
+
+
+class BlackBaseTestCase(unittest.TestCase):
+ maxDiff = None
+ _diffThreshold = 2 ** 20
+
+ def assertFormatEqual(self, expected: str, actual: str) -> None:
+ if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
+ bdv: black.DebugVisitor[Any]
+ black.out("Expected tree:", fg="green")
+ try:
+ exp_node = black.lib2to3_parse(expected)
+ bdv = black.DebugVisitor()
+ list(bdv.visit(exp_node))
+ except Exception as ve:
+ black.err(str(ve))
+ black.out("Actual tree:", fg="red")
+ try:
+ exp_node = black.lib2to3_parse(actual)
+ bdv = black.DebugVisitor()
+ list(bdv.visit(exp_node))
+ except Exception as ve:
+ black.err(str(ve))
+ self.assertMultiLineEqual(expected, actual)
+
+
@contextmanager
def skip_if_exception(e: str) -> Iterator[None]:
try:
@@ -24,11 +60,15 @@ def read_data(name: str, data: bool = True) -> Tuple[str, str]:
"""read_data('test_name') -> 'input', 'output'"""
if not name.endswith((".py", ".pyi", ".out", ".diff")):
name += ".py"
- _input: List[str] = []
- _output: List[str] = []
base_dir = THIS_DIR / "data" if data else PROJECT_ROOT
- with open(base_dir / name, "r", encoding="utf8") as test:
+ return read_data_from_file(base_dir / name)
+
+
+def read_data_from_file(file_name: Path) -> Tuple[str, str]:
+ with open(file_name, "r", encoding="utf8") as test:
lines = test.readlines()
+ _input: List[str] = []
+ _output: List[str] = []
result = _input
for line in lines:
line = line.replace(EMPTY_LINE, "")
From b64fd2bbec420ff71f7592f40816ae7de2fa4a15 Mon Sep 17 00:00:00 2001
From: Shota Ray Imaki
Date: Sat, 31 Oct 2020 00:13:55 +0900
Subject: [PATCH 086/680] Add compatible configuration files. (psf#1789)
(#1792)
* Add compatible configuration files. (psf#1789)
* Simplify isort configuration files. (#1789)
---
docs/compatible_configs.md | 3 +++
docs/compatible_configs/flake8/.flake8 | 3 +++
docs/compatible_configs/flake8/setup.cfg | 3 +++
docs/compatible_configs/flake8/tox.ini | 3 +++
docs/compatible_configs/isort/.editorconfig | 2 ++
docs/compatible_configs/isort/.isort.cfg | 2 ++
docs/compatible_configs/isort/pyproject.toml | 2 ++
docs/compatible_configs/isort/setup.cfg | 2 ++
docs/compatible_configs/pylint/pylintrc | 5 +++++
docs/compatible_configs/pylint/pyproject.toml | 5 +++++
docs/compatible_configs/pylint/setup.cfg | 5 +++++
11 files changed, 35 insertions(+)
create mode 100644 docs/compatible_configs/flake8/.flake8
create mode 100644 docs/compatible_configs/flake8/setup.cfg
create mode 100644 docs/compatible_configs/flake8/tox.ini
create mode 100644 docs/compatible_configs/isort/.editorconfig
create mode 100644 docs/compatible_configs/isort/.isort.cfg
create mode 100644 docs/compatible_configs/isort/pyproject.toml
create mode 100644 docs/compatible_configs/isort/setup.cfg
create mode 100644 docs/compatible_configs/pylint/pylintrc
create mode 100644 docs/compatible_configs/pylint/pyproject.toml
create mode 100644 docs/compatible_configs/pylint/setup.cfg
diff --git a/docs/compatible_configs.md b/docs/compatible_configs.md
index 990820a6771..c6724e77e57 100644
--- a/docs/compatible_configs.md
+++ b/docs/compatible_configs.md
@@ -10,6 +10,9 @@ tools out there.
(e.g. `pyproject.toml`). The provided examples are to only configure their corresponding
tools, using **their** supported file formats.
+Compatible configuration files can be
+[found here](https://github.com/psf/black/blob/master/docs/compatible_configs/).
+
## isort
[isort](https://pypi.org/p/isort/) helps to sort and format imports in your Python code.
diff --git a/docs/compatible_configs/flake8/.flake8 b/docs/compatible_configs/flake8/.flake8
new file mode 100644
index 00000000000..8dd399ab55b
--- /dev/null
+++ b/docs/compatible_configs/flake8/.flake8
@@ -0,0 +1,3 @@
+[flake8]
+max-line-length = 88
+extend-ignore = E203
diff --git a/docs/compatible_configs/flake8/setup.cfg b/docs/compatible_configs/flake8/setup.cfg
new file mode 100644
index 00000000000..8dd399ab55b
--- /dev/null
+++ b/docs/compatible_configs/flake8/setup.cfg
@@ -0,0 +1,3 @@
+[flake8]
+max-line-length = 88
+extend-ignore = E203
diff --git a/docs/compatible_configs/flake8/tox.ini b/docs/compatible_configs/flake8/tox.ini
new file mode 100644
index 00000000000..8dd399ab55b
--- /dev/null
+++ b/docs/compatible_configs/flake8/tox.ini
@@ -0,0 +1,3 @@
+[flake8]
+max-line-length = 88
+extend-ignore = E203
diff --git a/docs/compatible_configs/isort/.editorconfig b/docs/compatible_configs/isort/.editorconfig
new file mode 100644
index 00000000000..edc849a54a6
--- /dev/null
+++ b/docs/compatible_configs/isort/.editorconfig
@@ -0,0 +1,2 @@
+[*.py]
+profile = black
diff --git a/docs/compatible_configs/isort/.isort.cfg b/docs/compatible_configs/isort/.isort.cfg
new file mode 100644
index 00000000000..f238bf7ea13
--- /dev/null
+++ b/docs/compatible_configs/isort/.isort.cfg
@@ -0,0 +1,2 @@
+[settings]
+profile = black
diff --git a/docs/compatible_configs/isort/pyproject.toml b/docs/compatible_configs/isort/pyproject.toml
new file mode 100644
index 00000000000..cea9915bebf
--- /dev/null
+++ b/docs/compatible_configs/isort/pyproject.toml
@@ -0,0 +1,2 @@
+[tool.isort]
+profile = black
diff --git a/docs/compatible_configs/isort/setup.cfg b/docs/compatible_configs/isort/setup.cfg
new file mode 100644
index 00000000000..c76db01ff4e
--- /dev/null
+++ b/docs/compatible_configs/isort/setup.cfg
@@ -0,0 +1,2 @@
+[isort]
+profile = black
diff --git a/docs/compatible_configs/pylint/pylintrc b/docs/compatible_configs/pylint/pylintrc
new file mode 100644
index 00000000000..7abddd2c330
--- /dev/null
+++ b/docs/compatible_configs/pylint/pylintrc
@@ -0,0 +1,5 @@
+[MESSAGES CONTROL]
+disable = C0330, C0326
+
+[format]
+max-line-length = 88
diff --git a/docs/compatible_configs/pylint/pyproject.toml b/docs/compatible_configs/pylint/pyproject.toml
new file mode 100644
index 00000000000..49ad7a2c771
--- /dev/null
+++ b/docs/compatible_configs/pylint/pyproject.toml
@@ -0,0 +1,5 @@
+[tool.pylint.messages_control]
+disable = "C0330, C0326"
+
+[tool.pylint.format]
+max-line-length = "88"
diff --git a/docs/compatible_configs/pylint/setup.cfg b/docs/compatible_configs/pylint/setup.cfg
new file mode 100644
index 00000000000..3ada24530ea
--- /dev/null
+++ b/docs/compatible_configs/pylint/setup.cfg
@@ -0,0 +1,5 @@
+[pylint]
+max-line-length = 88
+
+[pylint.messages_control]
+disable = C0330, C0326
From 5d978df8b54d4fb102142508353a04932a4b5b04 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 30 Oct 2020 08:15:09 -0700
Subject: [PATCH 087/680] Bump cryptography from 3.1 to 3.2 (#1791)
Bumps [cryptography](https://github.com/pyca/cryptography) from 3.1 to 3.2.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/3.1...3.2)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
Pipfile.lock | 355 +++++++++++++++++++++++++--------------------------
1 file changed, 172 insertions(+), 183 deletions(-)
diff --git a/Pipfile.lock b/Pipfile.lock
index e09d69ea83d..95b7613114a 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -53,7 +53,6 @@
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
- "markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
@@ -61,7 +60,6 @@
"sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
"sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.2.0"
},
"black": {
@@ -92,16 +90,13 @@
"sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"
],
"index": "pypi",
- "python_version <": "3.7",
- "version": "==0.6",
- "version >": "0.6"
+ "version": "==0.6"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"multidict": {
@@ -124,7 +119,6 @@
"sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255",
"sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"
],
- "markers": "python_version >= '3.5'",
"version": "==4.7.6"
},
"mypy-extensions": {
@@ -170,26 +164,10 @@
"index": "pypi",
"version": "==2020.7.14"
},
- "setuptools-scm": {
- "hashes": [
- "sha256:09c659d1d6680811c43f476a33c6d3d9872416580786e96bd29ea03e6a818e41",
- "sha256:69258e2eeba5f7ce1ed7a5f109519580fa3578250f8e4d6684859f86d1b15826",
- "sha256:731550a27e3901ee501c3bf99140b5436b8eeba341a9d19911cf364b8d573293",
- "sha256:892e63b4983f9e30f9e8bf89ad03d2a02a47e6e5ced09b03ae6fe952ade8a579",
- "sha256:a8994582e716ec690f33fec70cca0f85bd23ec974e3f783233e4879090a7faa8",
- "sha256:b42c150c34d6120babf3646abd7513e032be2e230b3d2034f27404c65aa0c977",
- "sha256:eaaec16b7af25c5f532b5af2332213bb6223d15cca4518f6dbc4c055641c86fd",
- "sha256:efc928d6a64162c88cdc85fa4b84adfbd6dbf9f9b04319bc495eb16dcfaae00a"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==4.1.2"
- },
"toml": {
"hashes": [
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"index": "pypi",
"version": "==0.10.1"
@@ -198,9 +176,11 @@
"hashes": [
"sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
"sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
+ "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d",
"sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
"sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
"sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
+ "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c",
"sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
"sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
"sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
@@ -209,12 +189,16 @@
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
+ "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c",
"sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
+ "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395",
"sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
"sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
"sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
+ "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072",
+ "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91",
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
],
@@ -232,26 +216,41 @@
},
"yarl": {
"hashes": [
- "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409",
- "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593",
- "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2",
- "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8",
- "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d",
- "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692",
- "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02",
- "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a",
- "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8",
- "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6",
- "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511",
- "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e",
- "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a",
- "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb",
- "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f",
- "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317",
- "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"
- ],
- "markers": "python_version >= '3.5'",
- "version": "==1.5.1"
+ "sha256:03b7a44384ad60be1b7be93c2a24dc74895f8d767ea0bce15b2f6fc7695a3843",
+ "sha256:076157404db9db4bb3fa9db22db319bbb36d075eeab19ba018ce20ae0cacf037",
+ "sha256:1c05ae3d5ea4287470046a2c2754f0a4c171b84ea72c8a691f776eb1753dfb91",
+ "sha256:2467baf8233f7c64048df37e11879c553943ffe7f373e689711ec2807ea13805",
+ "sha256:2bb2e21cf062dfbe985c3cd4618bae9f25271efcad9e7be1277861247eee9839",
+ "sha256:311effab3b3828ab34f0e661bb57ff422f67d5c33056298bda4c12195251f8dd",
+ "sha256:3526cb5905907f0e42bee7ef57ae4a5f02bc27dcac27859269e2bba0caa4c2b6",
+ "sha256:39b1e586f34b1d2512c9b39aa3cf24c870c972d525e36edc9ee19065db4737bb",
+ "sha256:4bed5cd7c8e69551eb19df15295ba90e62b9a6a1149c76eb4a9bab194402a156",
+ "sha256:51c6d3cf7a1f1fbe134bb92f33b7affd94d6de24cd64b466eb12de52120fb8c6",
+ "sha256:59f78b5da34ddcffb663b772f7619e296518712e022e57fc5d9f921818e2ab7c",
+ "sha256:6f29115b0c330da25a04f48612d75333bca04521181a666ca0b8761005a99150",
+ "sha256:73d4e1e1ef5e52d526c92f07d16329e1678612c6a81dd8101fdcae11a72de15c",
+ "sha256:9b48d31f8d881713fd461abfe7acbb4dcfeb47cec3056aa83f2fbcd2244577f7",
+ "sha256:a1fd575dd058e10ad4c35065e7c3007cc74d142f622b14e168d8a273a2fa8713",
+ "sha256:b3dd1052afd436ba737e61f5d3bed1f43a7f9a33fc58fbe4226eb919a7006019",
+ "sha256:b99c25ed5c355b35d1e6dae87ac7297a4844a57dc5766b173b88b6163a36eb0d",
+ "sha256:c056e86bff5a0b566e0d9fab4f67e83b12ae9cbcd250d334cbe2005bbe8c96f2",
+ "sha256:c45b49b59a5724869899798e1bbd447ac486215269511d3b76b4c235a1b766b6",
+ "sha256:cd623170c729a865037828e3f99f8ebdb22a467177a539680dfc5670b74c84e2",
+ "sha256:d25d3311794e6c71b608d7c47651c8f65eea5ab15358a27f29330b3475e8f8e5",
+ "sha256:d695439c201ed340745250f9eb4dfe8d32bf1e680c16477107b8f3ce4bff4fdb",
+ "sha256:d77f6c9133d2aabb290a7846aaa74ec14d7b5ab35b01591fac5a70c4a8c959a2",
+ "sha256:d894a2442d2cd20a3b0b0dce5a353d316c57d25a2b445e03f7eac90eee27b8af",
+ "sha256:db643ce2b58a4bd11a82348225c53c76ecdd82bb37cf4c085e6df1b676f4038c",
+ "sha256:e3a0c43a26dfed955b2a06fdc4d51d2c51bc2200aff8ce8faf14e676ea8c8862",
+ "sha256:e77bf79ad1ccae672eab22453838382fe9029fc27c8029e84913855512a587d8",
+ "sha256:f2f0174cb15435957d3b751093f89aede77df59a499ab7516bbb633b77ead13a",
+ "sha256:f3031c78edf10315abe232254e6a36b65afe65fded41ee54ed7976d0b2cdf0da",
+ "sha256:f4c007156732866aa4507d619fe6f8f2748caabed4f66b276ccd97c82572620c",
+ "sha256:f4f27ff3dd80bc7c402def211a47291ea123d59a23f59fe18fc0e81e3e71f385",
+ "sha256:f57744fc61e118b5d114ae8077d8eb9df4d2d2c11e2af194e21f0c11ed9dcf6c",
+ "sha256:f835015a825980b65356e9520979a1564c56efea7da7d4b68a14d4a07a3a7336"
+ ],
+ "version": "==1.6.2"
}
},
"develop": {
@@ -301,7 +300,6 @@
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
- "markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
@@ -309,7 +307,6 @@
"sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
"sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.2.0"
},
"babel": {
@@ -317,7 +314,6 @@
"sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
"sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.0"
},
"black": {
@@ -332,7 +328,6 @@
"sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080",
"sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==3.2.1"
},
"certifi": {
@@ -344,43 +339,50 @@
},
"cffi": {
"hashes": [
- "sha256:0da50dcbccd7cb7e6c741ab7912b2eff48e85af217d72b57f80ebc616257125e",
- "sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c",
- "sha256:15419020b0e812b40d96ec9d369b2bc8109cc3295eac6e013d3261343580cc7e",
- "sha256:15a5f59a4808f82d8ec7364cbace851df591c2d43bc76bcbe5c4543a7ddd1bf1",
- "sha256:23e44937d7695c27c66a54d793dd4b45889a81b35c0751ba91040fe825ec59c4",
- "sha256:29c4688ace466a365b85a51dcc5e3c853c1d283f293dfcc12f7a77e498f160d2",
- "sha256:57214fa5430399dffd54f4be37b56fe22cedb2b98862550d43cc085fb698dc2c",
- "sha256:577791f948d34d569acb2d1add5831731c59d5a0c50a6d9f629ae1cefd9ca4a0",
- "sha256:6539314d84c4d36f28d73adc1b45e9f4ee2a89cdc7e5d2b0a6dbacba31906798",
- "sha256:65867d63f0fd1b500fa343d7798fa64e9e681b594e0a07dc934c13e76ee28fb1",
- "sha256:672b539db20fef6b03d6f7a14b5825d57c98e4026401fce838849f8de73fe4d4",
- "sha256:6843db0343e12e3f52cc58430ad559d850a53684f5b352540ca3f1bc56df0731",
- "sha256:7057613efefd36cacabbdbcef010e0a9c20a88fc07eb3e616019ea1692fa5df4",
- "sha256:76ada88d62eb24de7051c5157a1a78fd853cca9b91c0713c2e973e4196271d0c",
- "sha256:837398c2ec00228679513802e3744d1e8e3cb1204aa6ad408b6aff081e99a487",
- "sha256:8662aabfeab00cea149a3d1c2999b0731e70c6b5bac596d95d13f643e76d3d4e",
- "sha256:95e9094162fa712f18b4f60896e34b621df99147c2cee216cfa8f022294e8e9f",
- "sha256:99cc66b33c418cd579c0f03b77b94263c305c389cb0c6972dac420f24b3bf123",
- "sha256:9b219511d8b64d3fa14261963933be34028ea0e57455baf6781fe399c2c3206c",
- "sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b",
- "sha256:b9aa9d8818c2e917fa2c105ad538e222a5bce59777133840b93134022a7ce650",
- "sha256:bf44a9a0141a082e89c90e8d785b212a872db793a0080c20f6ae6e2a0ebf82ad",
- "sha256:c0b48b98d79cf795b0916c57bebbc6d16bb43b9fc9b8c9f57f4cf05881904c75",
- "sha256:da9d3c506f43e220336433dffe643fbfa40096d408cb9b7f2477892f369d5f82",
- "sha256:e4082d832e36e7f9b2278bc774886ca8207346b99f278e54c9de4834f17232f7",
- "sha256:e4b9b7af398c32e408c00eb4e0d33ced2f9121fd9fb978e6c1b57edd014a7d15",
- "sha256:e613514a82539fc48291d01933951a13ae93b6b444a88782480be32245ed4afa",
- "sha256:f5033952def24172e60493b68717792e3aebb387a8d186c43c020d9363ee7281"
- ],
- "version": "==1.14.2"
+ "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d",
+ "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b",
+ "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4",
+ "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f",
+ "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3",
+ "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579",
+ "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537",
+ "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e",
+ "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05",
+ "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171",
+ "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca",
+ "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522",
+ "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c",
+ "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc",
+ "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d",
+ "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808",
+ "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828",
+ "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869",
+ "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d",
+ "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9",
+ "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0",
+ "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc",
+ "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15",
+ "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c",
+ "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a",
+ "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3",
+ "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1",
+ "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768",
+ "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d",
+ "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b",
+ "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e",
+ "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d",
+ "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730",
+ "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394",
+ "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1",
+ "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"
+ ],
+ "version": "==1.14.3"
},
"cfgv": {
"hashes": [
"sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d",
"sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"
],
- "markers": "python_full_version >= '3.6.1'",
"version": "==3.2.0"
},
"chardet": {
@@ -400,11 +402,10 @@
},
"colorama": {
"hashes": [
- "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff",
- "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"
+ "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
+ "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==0.4.3"
+ "version": "==0.4.4"
},
"commonmark": {
"hashes": [
@@ -455,31 +456,31 @@
},
"cryptography": {
"hashes": [
- "sha256:10c9775a3f31610cf6b694d1fe598f2183441de81cedcf1814451ae53d71b13a",
- "sha256:180c9f855a8ea280e72a5d61cf05681b230c2dce804c48e9b2983f491ecc44ed",
- "sha256:247df238bc05c7d2e934a761243bfdc67db03f339948b1e2e80c75d41fc7cc36",
- "sha256:26409a473cc6278e4c90f782cd5968ebad04d3911ed1c402fc86908c17633e08",
- "sha256:2a27615c965173c4c88f2961cf18115c08fedfb8bdc121347f26e8458dc6d237",
- "sha256:2e26223ac636ca216e855748e7d435a1bf846809ed12ed898179587d0cf74618",
- "sha256:321761d55fb7cb256b771ee4ed78e69486a7336be9143b90c52be59d7657f50f",
- "sha256:4005b38cd86fc51c955db40b0f0e52ff65340874495af72efabb1bb8ca881695",
- "sha256:4b9e96543d0784acebb70991ebc2dbd99aa287f6217546bb993df22dd361d41c",
- "sha256:548b0818e88792318dc137d8b1ec82a0ab0af96c7f0603a00bb94f896fbf5e10",
- "sha256:725875681afe50b41aee7fdd629cedbc4720bab350142b12c55c0a4d17c7416c",
- "sha256:7a63e97355f3cd77c94bd98c59cb85fe0efd76ea7ef904c9b0316b5bbfde6ed1",
- "sha256:94191501e4b4009642be21dde2a78bd3c2701a81ee57d3d3d02f1d99f8b64a9e",
- "sha256:969ae512a250f869c1738ca63be843488ff5cc031987d302c1f59c7dbe1b225f",
- "sha256:9f734423eb9c2ea85000aa2476e0d7a58e021bc34f0a373ac52a5454cd52f791",
- "sha256:b45ab1c6ece7c471f01c56f5d19818ca797c34541f0b2351635a5c9fe09ac2e0",
- "sha256:cc6096c86ec0de26e2263c228fb25ee01c3ff1346d3cfc219d67d49f303585af",
- "sha256:dc3f437ca6353979aace181f1b790f0fc79e446235b14306241633ab7d61b8f8",
- "sha256:e7563eb7bc5c7e75a213281715155248cceba88b11cb4b22957ad45b85903761",
- "sha256:e7dad66a9e5684a40f270bd4aee1906878193ae50a4831922e454a2a457f1716",
- "sha256:eb80a288e3cfc08f679f95da72d2ef90cb74f6d8a8ba69d2f215c5e110b2ca32",
- "sha256:fa7fbcc40e2210aca26c7ac8a39467eae444d90a2c346cbcffd9133a166bcc67"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==3.1"
+ "sha256:22f8251f68953553af4f9c11ec5f191198bc96cff9f0ac5dd5ff94daede0ee6d",
+ "sha256:284e275e3c099a80831f9898fb5c9559120d27675c3521278faba54e584a7832",
+ "sha256:3e17d02941c0f169c5b877597ca8be895fca0e5e3eb882526a74aa4804380a98",
+ "sha256:52a47e60953679eea0b4d490ca3c241fb1b166a7b161847ef4667dfd49e7699d",
+ "sha256:57b8c1ed13b8aa386cabbfde3be175d7b155682470b0e259fecfe53850967f8a",
+ "sha256:6a8f64ed096d13f92d1f601a92d9fd1f1025dc73a2ca1ced46dcf5e0d4930943",
+ "sha256:6e8a3c7c45101a7eeee93102500e1b08f2307c717ff553fcb3c1127efc9b6917",
+ "sha256:7ef41304bf978f33cfb6f43ca13bb0faac0c99cda33693aa20ad4f5e34e8cb8f",
+ "sha256:87c2fffd61e934bc0e2c927c3764c20b22d7f5f7f812ee1a477de4c89b044ca6",
+ "sha256:88069392cd9a1e68d2cfd5c3a2b0d72a44ef3b24b8977a4f7956e9e3c4c9477a",
+ "sha256:8a0866891326d3badb17c5fd3e02c926b635e8923fa271b4813cd4d972a57ff3",
+ "sha256:8f0fd8b0751d75c4483c534b209e39e918f0d14232c0d8a2a76e687f64ced831",
+ "sha256:9a07e6d255053674506091d63ab4270a119e9fc83462c7ab1dbcb495b76307af",
+ "sha256:9a8580c9afcdcddabbd064c0a74f337af74ff4529cdf3a12fa2e9782d677a2e5",
+ "sha256:bd80bc156d3729b38cb227a5a76532aef693b7ac9e395eea8063ee50ceed46a5",
+ "sha256:d1cbc3426e6150583b22b517ef3720036d7e3152d428c864ff0f3fcad2b97591",
+ "sha256:e15ac84dcdb89f92424cbaca4b0b34e211e7ce3ee7b0ec0e4f3c55cee65fae5a",
+ "sha256:e4789b84f8dedf190148441f7c5bfe7244782d9cbb194a36e17b91e7d3e1cca9",
+ "sha256:f01c9116bfb3ad2831e125a73dcd957d173d6ddca7701528eff1e7d97972872c",
+ "sha256:f0e3986f6cce007216b23c490f093f35ce2068f3c244051e559f647f6731b7ae",
+ "sha256:f2aa3f8ba9e2e3fd49bd3de743b976ab192fbf0eb0348cebde5d2a9de0090a9f",
+ "sha256:fb70a4cedd69dc52396ee114416a3656e011fb0311fca55eb55c7be6ed9c8aef"
+ ],
+ "index": "pypi",
+ "version": "==3.2"
},
"distlib": {
"hashes": [
@@ -491,7 +492,6 @@
"docutils": {
"hashes": [
"sha256:54a349c622ff31c91cbec43b0b512f113b5b24daf00e2ea530bb1bd9aac14849",
- "sha256:ba4584f9107571ced0d2c7f56a5499c696215ba90797849c92d395979da68521",
"sha256:d2ddba74835cb090a1b627d3de4e7835c628d07ee461f7b4480f51af2fe4d448"
],
"index": "pypi",
@@ -530,18 +530,16 @@
},
"identify": {
"hashes": [
- "sha256:c770074ae1f19e08aadbda1c886bc6d0cb55ffdc503a8c0fe8699af2fc9664ae",
- "sha256:d02d004568c5a01261839a05e91705e3e9f5c57a3551648f9b3fb2b9c62c0f62"
+ "sha256:3139bf72d81dfd785b0a464e2776bd59bdc725b4cc10e6cf46b56a0db931c82e",
+ "sha256:969d844b7a85d32a5f9ac4e163df6e846d73c87c8b75847494ee8f4bd2186421"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==1.5.3"
+ "version": "==1.5.6"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"imagesize": {
@@ -549,7 +547,6 @@
"sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
"sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.0"
},
"jeepney": {
@@ -565,7 +562,6 @@
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.11.2"
},
"keyring": {
@@ -573,7 +569,6 @@
"sha256:4e34ea2fdec90c1c43d6610b5a5fafa1b9097db1802948e90caf5763974b8f8d",
"sha256:9aeadd006a852b78f4b4ef7c7556c2774d2432bbef8ee538a3e9089ac8b11466"
],
- "markers": "python_version >= '3.6'",
"version": "==21.4.0"
},
"markupsafe": {
@@ -612,7 +607,6 @@
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1"
},
"mccabe": {
@@ -642,7 +636,6 @@
"sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255",
"sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"
],
- "markers": "python_version >= '3.5'",
"version": "==4.7.6"
},
"mypy": {
@@ -685,7 +678,6 @@
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.4"
},
"pathspec": {
@@ -698,10 +690,10 @@
},
"pkginfo": {
"hashes": [
- "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb",
- "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32"
+ "sha256:a6a4ac943b496745cec21f14f021bbd869d5e9b4f6ec06918cffea5a2f4b9193",
+ "sha256:ce14d7296c673dc4c61c759a0b6c14bae34e34eb819c0017bb6ca5b7292c56e9"
],
- "version": "==1.5.0.1"
+ "version": "==1.6.1"
},
"pre-commit": {
"hashes": [
@@ -716,7 +708,6 @@
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.6.0"
},
"pycparser": {
@@ -724,7 +715,6 @@
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.20"
},
"pyflakes": {
@@ -732,23 +722,20 @@
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
"sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.2.0"
},
"pygments": {
"hashes": [
- "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998",
- "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"
+ "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0",
+ "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"
],
- "markers": "python_version >= '3.5'",
- "version": "==2.7.1"
+ "version": "==2.7.2"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
- "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytz": {
@@ -822,7 +809,6 @@
"sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
"sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.24.0"
},
"requests-toolbelt": {
@@ -849,16 +835,10 @@
},
"setuptools-scm": {
"hashes": [
- "sha256:09c659d1d6680811c43f476a33c6d3d9872416580786e96bd29ea03e6a818e41",
"sha256:69258e2eeba5f7ce1ed7a5f109519580fa3578250f8e4d6684859f86d1b15826",
- "sha256:731550a27e3901ee501c3bf99140b5436b8eeba341a9d19911cf364b8d573293",
- "sha256:892e63b4983f9e30f9e8bf89ad03d2a02a47e6e5ced09b03ae6fe952ade8a579",
- "sha256:a8994582e716ec690f33fec70cca0f85bd23ec974e3f783233e4879090a7faa8",
- "sha256:b42c150c34d6120babf3646abd7513e032be2e230b3d2034f27404c65aa0c977",
- "sha256:eaaec16b7af25c5f532b5af2332213bb6223d15cca4518f6dbc4c055641c86fd",
- "sha256:efc928d6a64162c88cdc85fa4b84adfbd6dbf9f9b04319bc495eb16dcfaae00a"
- ],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "sha256:a8994582e716ec690f33fec70cca0f85bd23ec974e3f783233e4879090a7faa8"
+ ],
+ "index": "pypi",
"version": "==4.1.2"
},
"six": {
@@ -866,7 +846,6 @@
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"snowballstemmer": {
@@ -889,7 +868,6 @@
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
"sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
],
- "markers": "python_version >= '3.5'",
"version": "==1.0.2"
},
"sphinxcontrib-devhelp": {
@@ -897,7 +875,6 @@
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
],
- "markers": "python_version >= '3.5'",
"version": "==1.0.2"
},
"sphinxcontrib-htmlhelp": {
@@ -905,7 +882,6 @@
"sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
"sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
],
- "markers": "python_version >= '3.5'",
"version": "==1.0.3"
},
"sphinxcontrib-jsmath": {
@@ -913,7 +889,6 @@
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
],
- "markers": "python_version >= '3.5'",
"version": "==1.0.1"
},
"sphinxcontrib-qthelp": {
@@ -921,7 +896,6 @@
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
],
- "markers": "python_version >= '3.5'",
"version": "==1.0.3"
},
"sphinxcontrib-serializinghtml": {
@@ -929,26 +903,22 @@
"sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
"sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
],
- "markers": "python_version >= '3.5'",
"version": "==1.1.4"
},
"toml": {
"hashes": [
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88",
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
"index": "pypi",
"version": "==0.10.1"
},
"tqdm": {
"hashes": [
- "sha256:8f3c5815e3b5e20bc40463fa6b42a352178859692a68ffaa469706e6d38342a5",
- "sha256:faf9c671bd3fad5ebaeee366949d969dca2b2be32c872a7092a1e1a9048d105b"
+ "sha256:9ad44aaf0fc3697c06f6e05c7cf025dd66bc7bcb7613c66d85f4464c47ac8fad",
+ "sha256:ef54779f1c09f346b2b5a8e5c61f96fbcb639929e640e59f8cf810794f406432"
],
- "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==4.49.0"
+ "version": "==4.51.0"
},
"twine": {
"hashes": [
@@ -962,9 +932,11 @@
"hashes": [
"sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
"sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
+ "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d",
"sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
"sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
"sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
+ "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c",
"sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
"sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
"sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
@@ -973,12 +945,16 @@
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
+ "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c",
"sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
+ "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395",
"sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
"sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
"sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
+ "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072",
+ "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91",
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
],
@@ -996,19 +972,17 @@
},
"urllib3": {
"hashes": [
- "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a",
- "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"
+ "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2",
+ "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
- "version": "==1.25.10"
+ "version": "==1.25.11"
},
"virtualenv": {
"hashes": [
- "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc",
- "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b"
+ "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2",
+ "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==20.0.31"
+ "version": "==20.1.0"
},
"webencodings": {
"hashes": [
@@ -1027,26 +1001,41 @@
},
"yarl": {
"hashes": [
- "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409",
- "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593",
- "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2",
- "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8",
- "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d",
- "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692",
- "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02",
- "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a",
- "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8",
- "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6",
- "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511",
- "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e",
- "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a",
- "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb",
- "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f",
- "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317",
- "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6"
- ],
- "markers": "python_version >= '3.5'",
- "version": "==1.5.1"
+ "sha256:03b7a44384ad60be1b7be93c2a24dc74895f8d767ea0bce15b2f6fc7695a3843",
+ "sha256:076157404db9db4bb3fa9db22db319bbb36d075eeab19ba018ce20ae0cacf037",
+ "sha256:1c05ae3d5ea4287470046a2c2754f0a4c171b84ea72c8a691f776eb1753dfb91",
+ "sha256:2467baf8233f7c64048df37e11879c553943ffe7f373e689711ec2807ea13805",
+ "sha256:2bb2e21cf062dfbe985c3cd4618bae9f25271efcad9e7be1277861247eee9839",
+ "sha256:311effab3b3828ab34f0e661bb57ff422f67d5c33056298bda4c12195251f8dd",
+ "sha256:3526cb5905907f0e42bee7ef57ae4a5f02bc27dcac27859269e2bba0caa4c2b6",
+ "sha256:39b1e586f34b1d2512c9b39aa3cf24c870c972d525e36edc9ee19065db4737bb",
+ "sha256:4bed5cd7c8e69551eb19df15295ba90e62b9a6a1149c76eb4a9bab194402a156",
+ "sha256:51c6d3cf7a1f1fbe134bb92f33b7affd94d6de24cd64b466eb12de52120fb8c6",
+ "sha256:59f78b5da34ddcffb663b772f7619e296518712e022e57fc5d9f921818e2ab7c",
+ "sha256:6f29115b0c330da25a04f48612d75333bca04521181a666ca0b8761005a99150",
+ "sha256:73d4e1e1ef5e52d526c92f07d16329e1678612c6a81dd8101fdcae11a72de15c",
+ "sha256:9b48d31f8d881713fd461abfe7acbb4dcfeb47cec3056aa83f2fbcd2244577f7",
+ "sha256:a1fd575dd058e10ad4c35065e7c3007cc74d142f622b14e168d8a273a2fa8713",
+ "sha256:b3dd1052afd436ba737e61f5d3bed1f43a7f9a33fc58fbe4226eb919a7006019",
+ "sha256:b99c25ed5c355b35d1e6dae87ac7297a4844a57dc5766b173b88b6163a36eb0d",
+ "sha256:c056e86bff5a0b566e0d9fab4f67e83b12ae9cbcd250d334cbe2005bbe8c96f2",
+ "sha256:c45b49b59a5724869899798e1bbd447ac486215269511d3b76b4c235a1b766b6",
+ "sha256:cd623170c729a865037828e3f99f8ebdb22a467177a539680dfc5670b74c84e2",
+ "sha256:d25d3311794e6c71b608d7c47651c8f65eea5ab15358a27f29330b3475e8f8e5",
+ "sha256:d695439c201ed340745250f9eb4dfe8d32bf1e680c16477107b8f3ce4bff4fdb",
+ "sha256:d77f6c9133d2aabb290a7846aaa74ec14d7b5ab35b01591fac5a70c4a8c959a2",
+ "sha256:d894a2442d2cd20a3b0b0dce5a353d316c57d25a2b445e03f7eac90eee27b8af",
+ "sha256:db643ce2b58a4bd11a82348225c53c76ecdd82bb37cf4c085e6df1b676f4038c",
+ "sha256:e3a0c43a26dfed955b2a06fdc4d51d2c51bc2200aff8ce8faf14e676ea8c8862",
+ "sha256:e77bf79ad1ccae672eab22453838382fe9029fc27c8029e84913855512a587d8",
+ "sha256:f2f0174cb15435957d3b751093f89aede77df59a499ab7516bbb633b77ead13a",
+ "sha256:f3031c78edf10315abe232254e6a36b65afe65fded41ee54ed7976d0b2cdf0da",
+ "sha256:f4c007156732866aa4507d619fe6f8f2748caabed4f66b276ccd97c82572620c",
+ "sha256:f4f27ff3dd80bc7c402def211a47291ea123d59a23f59fe18fc0e81e3e71f385",
+ "sha256:f57744fc61e118b5d114ae8077d8eb9df4d2d2c11e2af194e21f0c11ed9dcf6c",
+ "sha256:f835015a825980b65356e9520979a1564c56efea7da7d4b68a14d4a07a3a7336"
+ ],
+ "version": "==1.6.2"
}
}
}
From 8c8af4f1612dc29358490a7d04dd4ff2f5d4939d Mon Sep 17 00:00:00 2001
From: Abdullah Selek
Date: Fri, 30 Oct 2020 15:24:18 +0000
Subject: [PATCH 088/680] Start using Python 3.9 on Travis (#1790)
* Start using Python 3.9 on Travis
* Remove allow_failures
---
.travis.yml | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index f035343c6fb..e8d2975c5ff 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -28,9 +28,7 @@ matrix:
- name: "3.8"
python: 3.8
- name: "3.9"
- python: 3.9-dev
- allow_failures:
- - python: 3.9-dev
+ python: 3.9
before_deploy:
- pip install pyinstaller
- pyinstaller --clean -F --add-data src/blib2to3/:blib2to3 src/black/__init__.py
From 6c3f8181854160f3db354f6f6ef3315ef05db8e7 Mon Sep 17 00:00:00 2001
From: Bryan Bugyi
Date: Sat, 31 Oct 2020 13:42:36 -0400
Subject: [PATCH 089/680] Fix bug where black tries to split string on escaped
space (#1799)
Closes #1505.
---
src/black/__init__.py | 17 ++++++++++--
tests/data/long_strings__regression.py | 37 ++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 24e9d4edaaa..e09838d866a 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -3590,7 +3590,8 @@ class StringSplitter(CustomSplitMapMixin, BaseStringSplitter):
MIN_SUBSTR_SIZE characters.
The string will ONLY be split on spaces (i.e. each new substring should
- start with a space).
+ start with a space). Note that the string will NOT be split on a space
+ which is escaped with a backslash.
If the string is an f-string, it will NOT be split in the middle of an
f-expression (e.g. in f"FooBar: {foo() if x else bar()}", {foo() if x
@@ -3930,11 +3931,23 @@ def passes_all_checks(i: Index) -> bool:
section of this classes' docstring would be be met by returning @i.
"""
is_space = string[i] == " "
+
+ is_not_escaped = True
+ j = i - 1
+ while is_valid_index(j) and string[j] == "\\":
+ is_not_escaped = not is_not_escaped
+ j -= 1
+
is_big_enough = (
len(string[i:]) >= self.MIN_SUBSTR_SIZE
and len(string[:i]) >= self.MIN_SUBSTR_SIZE
)
- return is_space and is_big_enough and not breaks_fstring_expression(i)
+ return (
+ is_space
+ and is_not_escaped
+ and is_big_enough
+ and not breaks_fstring_expression(i)
+ )
# First, we check all indices BELOW @max_break_idx.
break_idx = max_break_idx
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
index 8290a4cbc1c..9bfb1eedd5a 100644
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -353,6 +353,24 @@ def xxxxxxx_xxxxxx(xxxx):
key
] = "test" # set some Thrift field to non-None in the struct aa bb cc dd ee
+RE_ONE_BACKSLASH = {
+ "asdf_hjkl_jkl": re.compile(
+ r"(?
Date: Sun, 1 Nov 2020 16:17:23 -0500
Subject: [PATCH 090/680] Automatically build and upload binaries on release
(#1743)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This commit adds a new GitHub Actions workflow that builds self-contained
binaries / executables and uploads them as release assets to the triggering
release. Publishing a release, drafting one doesn't count, will trigger this
workflow.
I personally used GitHub Actions only because it's the CI/CD platform(?)
I am familiar with. Only Windows and Linux binaries are supported since
I don't have any systems running Mac OS.
For Linux, I had originally planned to use the manylinux2010 docker image
the PyPA provides for highly compatible wheel building, but unfortunately
it wasn't feasible due to GitHub Actions and PyInstaller incompatibilities.
As a stopgap the oldest versions of Linux and Windows are used although
Windows Server 2019 isn't that old nor is Ubuntu 16.04! I guess someone
(maybe me) could work out something else if compatibility is big problem.
A few things you should know about the workflow:
- You don't need to set the `GITHUB_TOKEN` secret as it is automatically
provided by GitHub.
- matrix.pathsep is used because PyInstaller configuration's format is OS
dependent for some reason ...
Also it's worth mentioning that Black once had Travis CI and AppVeyor
configuration that did the same thing as this commit. They were committed
in mid 2018 and worked (somewhat) well. Eventually we stopped using AppVeyor
and the refactor to packages broke the Travis CI config. This commit
replaces the still existing and broken Travis CI config wholesale.
Co-authored-by: Anders Fredrik Kiær <31612826+anders-kiaer@users.noreply.github.com>
- Anders told me that I could get the release asset upload URL directly
from the github.event.release payload. I originally planned to use
bruceadams/get-release to get such URL.
---
.github/workflows/upload_binary.yml | 51 +++++++++++++++++++++++++++++
.travis.yml | 14 --------
CHANGES.md | 5 +++
3 files changed, 56 insertions(+), 14 deletions(-)
create mode 100644 .github/workflows/upload_binary.yml
diff --git a/.github/workflows/upload_binary.yml b/.github/workflows/upload_binary.yml
new file mode 100644
index 00000000000..46d92ab2149
--- /dev/null
+++ b/.github/workflows/upload_binary.yml
@@ -0,0 +1,51 @@
+name: Upload self-contained binaries
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: [3.7]
+ os: [ubuntu-16.04, windows-2019]
+ include:
+ - os: windows-2019
+ pathsep: ";"
+ executable_suffix: ".exe"
+ executable_mime: "application/vnd.microsoft.portable-executable"
+ - os: ubuntu-16.04
+ pathsep: ":"
+ executable_suffix: ".elf"
+ executable_mime: "application/x-executable"
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip wheel setuptools
+ python -m pip install .
+ python -m pip install pyinstaller
+
+ - name: Build binary
+ run: |
+ python -m PyInstaller -F --name black${{ matrix.executable_suffix }} --add-data 'src/blib2to3${{ matrix.pathsep }}blib2to3' src/black/__main__.py
+
+ - name: Upload binary as release asset
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: dist/black${{ matrix.executable_suffix }}
+ asset_name: black${{ matrix.executable_suffix }}
+ asset_content_type: ${{ matrix.executable_mime }}
diff --git a/.travis.yml b/.travis.yml
index e8d2975c5ff..e782272e813 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,17 +29,3 @@ matrix:
python: 3.8
- name: "3.9"
python: 3.9
-before_deploy:
- - pip install pyinstaller
- - pyinstaller --clean -F --add-data src/blib2to3/:blib2to3 src/black/__init__.py
-deploy:
- provider: releases
- api_key:
- secure: chYvcmnRqRKtfBcAZRj62rEv0ziWuHMl6MnfQbd1MOVQ4njntI8+CCPk118dW6MWSfwTqyMFy+t9gAgQYhjkLEHMS2aK9Z2wCWki1MkBrkMw5tYoLFvPu0KQ9rIVihxsr93a/am6Oh/Hp+1uuc4zWPUf1ubX+QlCzsxjCzVso1kTJjjdN04UxvkcFR+sY2d9Qyy9WcdifChnLwdmIJKIoVOE7Imm820nzImJHkJh8iSnjBjL98gvPPeC/nWTltsbErvf2mCv4NIjzjQZvHa87c7rSJGbliNrAxCSyyvBX+JNeS8U2fGLE83do0HieyjdPbTuc27e2nsrrihgPh+hXbiJerljclfp5hsJ5qGz5sS9MU1fR7sSLiQQ2v0TYB5RRwd34TgGiLwFAZZmgZOfMUCtefCKvP8qvELMSNd99+msfPEHiuhADF0bKPTbCUa6BgUHNr6woOLmHerjPHd6NI/a8Skz/uQB4xr3spLSmfUmX0fEqyYUDphkGPNH8IsvC1/F2isecW9kOzEWmB5oCmpMTGm4TIf3C01Nx+9PVwB2Z+30hhbfIEBxD4loRFmh/hU5TIQEpneF8yoIfe9EnMaoZbq86xhADZXvLIZvpXUdm1NQZDG6na2S1fwyOUKQsW6BWLcfoZZwZlrXrViD1jBsHBV++s+lxShTeTCszlo=
- file:
- - dist/black
- skip_cleanup: true
- on:
- condition: $TRAVIS_PYTHON_VERSION == '3.6'
- repo: psf/black
- tags: true
diff --git a/CHANGES.md b/CHANGES.md
index dfc54224b41..240abe302a4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -21,6 +21,11 @@
- Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711)
+#### _Packaging_
+
+- Self-contained native _Black_ binaries are now provided for releases via GitHub
+ Releases (#1743)
+
### 20.8b1
#### _Packaging_
From edf1c9dc0f20239b9d5351d8b0607c9e936dd43f Mon Sep 17 00:00:00 2001
From: Bryan Bugyi
Date: Fri, 6 Nov 2020 19:17:23 -0500
Subject: [PATCH 091/680] Fix bug which causes f-expressions to be split
(#1809)
Closes #1807.
---
src/black/__init__.py | 5 +++--
tests/data/long_strings__regression.py | 14 ++++++++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index e09838d866a..7a7456ad78b 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -3611,13 +3611,14 @@ class StringSplitter(CustomSplitMapMixin, BaseStringSplitter):
MIN_SUBSTR_SIZE = 6
# Matches an "f-expression" (e.g. {var}) that might be found in an f-string.
RE_FEXPR = r"""
- (? TMatchResult:
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
index 9bfb1eedd5a..7065b2fcef8 100644
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -371,6 +371,10 @@ def xxxxxxx_xxxxxx(xxxx):
),
}
+# We do NOT split on f-string expressions.
+print(f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. {[f'{i}' for i in range(10)]}")
+x = f"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}."
+
# output
@@ -830,3 +834,13 @@ def xxxxxxx_xxxxxx(xxxx):
r"(?
Date: Mon, 9 Nov 2020 14:58:23 -0500
Subject: [PATCH 092/680] Correctly handle inline tabs in docstrings (#1810)
The `fix_docstring` function expanded all tabs, which caused a
difference in the AST representation when those tabs were inline and not
leading. This changes the function to only expand leading tabs so inline
tabs are preserved.
Fixes #1601.
---
src/black/__init__.py | 26 +++++++++++++++++---
tests/data/docstring.py | 53 ++++++++++++++++++++++++++++++++++++++++-
2 files changed, 75 insertions(+), 4 deletions(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 7a7456ad78b..7e13a5d33f5 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -6744,13 +6744,33 @@ def is_docstring(leaf: Leaf) -> bool:
return False
+def lines_with_leading_tabs_expanded(s: str) -> List[str]:
+ """
+ Splits string into lines and expands only leading tabs (following the normal
+ Python rules)
+ """
+ lines = []
+ for line in s.splitlines():
+ # Find the index of the first non-whitespace character after a string of
+ # whitespace that includes at least one tab
+ match = re.match(r"\s*\t+\s*(\S)", line)
+ if match:
+ first_non_whitespace_idx = match.start(1)
+
+ lines.append(
+ line[:first_non_whitespace_idx].expandtabs()
+ + line[first_non_whitespace_idx:]
+ )
+ else:
+ lines.append(line)
+ return lines
+
+
def fix_docstring(docstring: str, prefix: str) -> str:
# https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation
if not docstring:
return ""
- # Convert tabs to spaces (following the normal Python rules)
- # and split into a list of lines:
- lines = docstring.expandtabs().splitlines()
+ lines = lines_with_leading_tabs_expanded(docstring)
# Determine minimum indentation (first line doesn't count):
indent = sys.maxsize
for line in lines[1:]:
diff --git a/tests/data/docstring.py b/tests/data/docstring.py
index 2d3d73a101c..5c6985d0e08 100644
--- a/tests/data/docstring.py
+++ b/tests/data/docstring.py
@@ -110,6 +110,32 @@ def ignored_docstring():
"""a => \
b"""
+
+def docstring_with_inline_tabs_and_space_indentation():
+ """hey
+
+ tab separated value
+ tab at start of line and then a tab separated value
+ multiple tabs at the beginning and inline
+ mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
+
+ line ends with some tabs
+ """
+
+
+def docstring_with_inline_tabs_and_tab_indentation():
+ """hey
+
+ tab separated value
+ tab at start of line and then a tab separated value
+ multiple tabs at the beginning and inline
+ mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
+
+ line ends with some tabs
+ """
+ pass
+
+
# output
class MyClass:
@@ -222,4 +248,29 @@ def believe_it_or_not_this_is_in_the_py_stdlib():
def ignored_docstring():
"""a => \
-b"""
\ No newline at end of file
+b"""
+
+
+def docstring_with_inline_tabs_and_space_indentation():
+ """hey
+
+ tab separated value
+ tab at start of line and then a tab separated value
+ multiple tabs at the beginning and inline
+ mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
+
+ line ends with some tabs
+ """
+
+
+def docstring_with_inline_tabs_and_tab_indentation():
+ """hey
+
+ tab separated value
+ tab at start of line and then a tab separated value
+ multiple tabs at the beginning and inline
+ mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
+
+ line ends with some tabs
+ """
+ pass
From 7d032fa848c8910007a0a41c1ba61d70d2846f48 Mon Sep 17 00:00:00 2001
From: Casper Weiss Bang
Date: Fri, 13 Nov 2020 16:25:17 +0100
Subject: [PATCH 093/680] Use lowercase hex numbers fixes #1692 (#1775)
* Made hex lower case
* Refactored numeric formatting section
* Redid some refactoring and removed bloat
* Removed additions from test_requirements.txt
* Primer now expects expected changes
* Undid some refactoring
* added to changelog
* Update src/black/__init__.py
Co-authored-by: Zsolt Dollenstein
Co-authored-by: Zsolt Dollenstein
Co-authored-by: Cooper Lees
---
.gitignore | 3 +-
CHANGES.md | 2 +
src/black/__init__.py | 57 +++++++++++++------
src/black_primer/primer.json | 10 ++--
src/blib2to3/pytree.py | 2 +-
test_requirements.txt | 2 +-
tests/data/numeric_literals.py | 4 +-
tests/data/numeric_literals_py2.py | 4 +-
.../data/numeric_literals_skip_underscores.py | 6 +-
9 files changed, 57 insertions(+), 33 deletions(-)
diff --git a/.gitignore b/.gitignore
index 3207e72ae28..30225ec7764 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.venv
.coverage
_build
.DS_Store
@@ -15,4 +16,4 @@ src/_black_version.py
.dmypy.json
*.swp
.hypothesis/
-venv/
\ No newline at end of file
+venv/
diff --git a/CHANGES.md b/CHANGES.md
index 240abe302a4..67697bd7b07 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -21,6 +21,8 @@
- Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711)
+- use lowercase hex strings (#1692)
+
#### _Packaging_
- Self-contained native _Black_ binaries are now provided for releases via GitHub
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 7e13a5d33f5..44edeb0d9f1 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -5192,31 +5192,52 @@ def normalize_numeric_literal(leaf: Leaf) -> None:
# Leave octal and binary literals alone.
pass
elif text.startswith("0x"):
- # Change hex literals to upper case.
- before, after = text[:2], text[2:]
- text = f"{before}{after.upper()}"
+ text = format_hex(text)
elif "e" in text:
- before, after = text.split("e")
- sign = ""
- if after.startswith("-"):
- after = after[1:]
- sign = "-"
- elif after.startswith("+"):
- after = after[1:]
- before = format_float_or_int_string(before)
- text = f"{before}e{sign}{after}"
+ text = format_scientific_notation(text)
elif text.endswith(("j", "l")):
- number = text[:-1]
- suffix = text[-1]
- # Capitalize in "2L" because "l" looks too similar to "1".
- if suffix == "l":
- suffix = "L"
- text = f"{format_float_or_int_string(number)}{suffix}"
+ text = format_long_or_complex_number(text)
else:
text = format_float_or_int_string(text)
leaf.value = text
+def format_hex(text: str) -> str:
+ """
+ Formats a hexadecimal string like "0x12b3"
+
+ Uses lowercase because of similarity between "B" and "8", which
+ can cause security issues.
+ see: https://github.com/psf/black/issues/1692
+ """
+
+ before, after = text[:2], text[2:]
+ return f"{before}{after.lower()}"
+
+
+def format_scientific_notation(text: str) -> str:
+ """Formats a numeric string utilizing scentific notation"""
+ before, after = text.split("e")
+ sign = ""
+ if after.startswith("-"):
+ after = after[1:]
+ sign = "-"
+ elif after.startswith("+"):
+ after = after[1:]
+ before = format_float_or_int_string(before)
+ return f"{before}e{sign}{after}"
+
+
+def format_long_or_complex_number(text: str) -> str:
+ """Formats a long or complex string like `10L` or `10j`"""
+ number = text[:-1]
+ suffix = text[-1]
+ # Capitalize in "2L" because "l" looks too similar to "1".
+ if suffix == "l":
+ suffix = "L"
+ return f"{format_float_or_int_string(number)}{suffix}"
+
+
def format_float_or_int_string(text: str) -> str:
"""Formats a float string like "1.0"."""
if "." not in text:
diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json
index cdc863ca032..32df01571a7 100644
--- a/src/black_primer/primer.json
+++ b/src/black_primer/primer.json
@@ -10,7 +10,7 @@
},
"attrs": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/python-attrs/attrs.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -47,7 +47,7 @@
},
"hypothesis": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/HypothesisWorks/hypothesis.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -63,7 +63,7 @@
},
"pillow": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/python-pillow/Pillow.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -77,7 +77,7 @@
},
"pyramid": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/Pylons/pyramid.git",
"long_checkout": false,
"py_versions": ["all"]
@@ -112,7 +112,7 @@
},
"virtualenv": {
"cli_arguments": [],
- "expect_formatting_changes": false,
+ "expect_formatting_changes": true,
"git_clone_url": "https://github.com/pypa/virtualenv.git",
"long_checkout": false,
"py_versions": ["all"]
diff --git a/src/blib2to3/pytree.py b/src/blib2to3/pytree.py
index 4b841b768e7..6dba3c7bb15 100644
--- a/src/blib2to3/pytree.py
+++ b/src/blib2to3/pytree.py
@@ -34,7 +34,7 @@
import sys
from io import StringIO
-HUGE: int = 0x7FFFFFFF # maximum repeat count, default max
+HUGE: int = 0x7fffffff # maximum repeat count, default max
_type_reprs: Dict[int, Union[Text, int]] = {}
diff --git a/test_requirements.txt b/test_requirements.txt
index 3e65cdb669f..9f69b8edf83 100644
--- a/test_requirements.txt
+++ b/test_requirements.txt
@@ -2,4 +2,4 @@ pytest >= 6.1.1
pytest-mock >= 3.3.1
pytest-cases >= 2.3.0
coverage >= 5.3
-parameterized >= 0.7.4
\ No newline at end of file
+parameterized >= 0.7.4
diff --git a/tests/data/numeric_literals.py b/tests/data/numeric_literals.py
index 254da68d330..06b7f7758ee 100644
--- a/tests/data/numeric_literals.py
+++ b/tests/data/numeric_literals.py
@@ -12,7 +12,7 @@
x = 123456789E123456789
x = 123456789J
x = 123456789.123456789J
-x = 0XB1ACC
+x = 0Xb1aCc
x = 0B1011
x = 0O777
x = 0.000000006
@@ -36,7 +36,7 @@
x = 123456789e123456789
x = 123456789j
x = 123456789.123456789j
-x = 0xB1ACC
+x = 0xb1acc
x = 0b1011
x = 0o777
x = 0.000000006
diff --git a/tests/data/numeric_literals_py2.py b/tests/data/numeric_literals_py2.py
index 8f85c43f265..8b2c7faa306 100644
--- a/tests/data/numeric_literals_py2.py
+++ b/tests/data/numeric_literals_py2.py
@@ -3,7 +3,7 @@
x = 123456789L
x = 123456789l
x = 123456789
-x = 0xb1acc
+x = 0xB1aCc
# output
@@ -13,4 +13,4 @@
x = 123456789L
x = 123456789L
x = 123456789
-x = 0xB1ACC
+x = 0xb1acc
diff --git a/tests/data/numeric_literals_skip_underscores.py b/tests/data/numeric_literals_skip_underscores.py
index e345bb90276..f83e23312f2 100644
--- a/tests/data/numeric_literals_skip_underscores.py
+++ b/tests/data/numeric_literals_skip_underscores.py
@@ -3,7 +3,7 @@
x = 123456789
x = 1_2_3_4_5_6_7
x = 1E+1
-x = 0xb1acc
+x = 0xb1AcC
x = 0.00_00_006
x = 12_34_567J
x = .1_2
@@ -16,8 +16,8 @@
x = 123456789
x = 1_2_3_4_5_6_7
x = 1e1
-x = 0xB1ACC
+x = 0xb1acc
x = 0.00_00_006
x = 12_34_567j
x = 0.1_2
-x = 1_2.0
\ No newline at end of file
+x = 1_2.0
From dea81b7ad5cfa04c3572771c34af823449d0a8f3 Mon Sep 17 00:00:00 2001
From: Thiago Bellini Ribeiro
Date: Fri, 13 Nov 2020 12:26:07 -0300
Subject: [PATCH 094/680] Provide a stdin-filename to allow stdin to respect
force-exclude rules (#1780)
* Provide a stdin-filename to allow stdin to respect exclude/force-exclude rules
This will allow automatic tools to enforce the project's
exclude/force-exclude rules even if they pass the file through stdin to
update its buffer.
This is a similar solution to --stdin-display-name in flake8.
* Update src/black/__init__.py
Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com>
* --stdin-filename should only respect --exclude-filename
* Update README with the new --stdin-filename option
* Write some tests for the new stdin-filename functionality
* Apply suggestions from code review
Co-authored-by: Hugo van Kemenade
* Force stdin output when we asked for stdin even if the file exists
* Add an entry in the changelog regarding --stdin-filename
* Reduce disk reads if possible
Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com>
* Check for is_stdin and p.is_file before checking for p.is_dir()
Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade
---
README.md | 5 ++
docs/change_log.md | 3 +
src/black/__init__.py | 67 +++++++++++++-----
tests/test_black.py | 159 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 217 insertions(+), 17 deletions(-)
diff --git a/README.md b/README.md
index d34ce146e0d..a557a6fc6c8 100644
--- a/README.md
+++ b/README.md
@@ -135,6 +135,11 @@ Options:
matching this regex will be excluded even
when they are passed explicitly as arguments.
+ --stdin-filename TEXT The name of the file when passing it through
+ stdin. Useful to make sure Black will respect
+ --force-exclude option on some editors that
+ rely on using stdin.
+
-q, --quiet Don't emit non-error messages to stderr.
Errors are still emitted; silence those with
2>/dev/null.
diff --git a/docs/change_log.md b/docs/change_log.md
index 1ee35a4d8f9..e6afefbf686 100644
--- a/docs/change_log.md
+++ b/docs/change_log.md
@@ -23,6 +23,9 @@
- Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711)
+- Added `--stdin-filename` argument to allow stdin to respect `--force-exclude` rules.
+ Works very alike to flake8's `--stdin-display-name` (#1780)
+
### 20.8b1
#### _Packaging_
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 44edeb0d9f1..48690573810 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -68,6 +68,7 @@
DEFAULT_EXCLUDES = r"/(\.direnv|\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|\.svn|_build|buck-out|build|dist)/" # noqa: B950
DEFAULT_INCLUDES = r"\.pyi?$"
CACHE_DIR = Path(user_cache_dir("black", version=__version__))
+STDIN_PLACEHOLDER = "__BLACK_STDIN_FILENAME__"
STRING_PREFIX_CHARS: Final = "furbFURB" # All possible string prefix characters.
@@ -457,6 +458,15 @@ def target_version_option_callback(
"excluded even when they are passed explicitly as arguments."
),
)
+@click.option(
+ "--stdin-filename",
+ type=str,
+ help=(
+ "The name of the file when passing it through stdin. Useful to make "
+ "sure Black will respect --force-exclude option on some "
+ "editors that rely on using stdin."
+ ),
+)
@click.option(
"-q",
"--quiet",
@@ -516,6 +526,7 @@ def main(
include: str,
exclude: str,
force_exclude: Optional[str],
+ stdin_filename: Optional[str],
src: Tuple[str, ...],
config: Optional[str],
) -> None:
@@ -548,6 +559,7 @@ def main(
exclude=exclude,
force_exclude=force_exclude,
report=report,
+ stdin_filename=stdin_filename,
)
path_empty(
@@ -587,6 +599,7 @@ def get_sources(
exclude: str,
force_exclude: Optional[str],
report: "Report",
+ stdin_filename: Optional[str],
) -> Set[Path]:
"""Compute the set of files to be formatted."""
try:
@@ -613,22 +626,14 @@ def get_sources(
gitignore = get_gitignore(root)
for s in src:
- p = Path(s)
- if p.is_dir():
- sources.update(
- gen_python_files(
- p.iterdir(),
- root,
- include_regex,
- exclude_regex,
- force_exclude_regex,
- report,
- gitignore,
- )
- )
- elif s == "-":
- sources.add(p)
- elif p.is_file():
+ if s == "-" and stdin_filename:
+ p = Path(stdin_filename)
+ is_stdin = True
+ else:
+ p = Path(s)
+ is_stdin = False
+
+ if is_stdin or p.is_file():
normalized_path = normalize_path_maybe_ignore(p, root, report)
if normalized_path is None:
continue
@@ -643,6 +648,23 @@ def get_sources(
report.path_ignored(p, "matches the --force-exclude regular expression")
continue
+ if is_stdin:
+ p = Path(f"{STDIN_PLACEHOLDER}{str(p)}")
+
+ sources.add(p)
+ elif p.is_dir():
+ sources.update(
+ gen_python_files(
+ p.iterdir(),
+ root,
+ include_regex,
+ exclude_regex,
+ force_exclude_regex,
+ report,
+ gitignore,
+ )
+ )
+ elif s == "-":
sources.add(p)
else:
err(f"invalid path: {s}")
@@ -670,7 +692,18 @@ def reformat_one(
"""
try:
changed = Changed.NO
- if not src.is_file() and str(src) == "-":
+
+ if str(src) == "-":
+ is_stdin = True
+ elif str(src).startswith(STDIN_PLACEHOLDER):
+ is_stdin = True
+ # Use the original name again in case we want to print something
+ # to the user
+ src = Path(str(src)[len(STDIN_PLACEHOLDER) :])
+ else:
+ is_stdin = False
+
+ if is_stdin:
if format_stdin_to_stdout(fast=fast, write_back=write_back, mode=mode):
changed = Changed.YES
else:
diff --git a/tests/test_black.py b/tests/test_black.py
index b0cf6ed5caa..a688c8780ef 100644
--- a/tests/test_black.py
+++ b/tests/test_black.py
@@ -1336,10 +1336,169 @@ def test_exclude_for_issue_1572(self) -> None:
exclude=exclude,
force_exclude=None,
report=report,
+ stdin_filename=None,
)
)
self.assertEqual(sorted(expected), sorted(sources))
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin(self) -> None:
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ expected = [Path("-")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=include,
+ exclude=exclude,
+ force_exclude=None,
+ report=report,
+ stdin_filename=None,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin_filename(self) -> None:
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ stdin_filename = str(THIS_DIR / "data/collections.py")
+ expected = [Path(f"__BLACK_STDIN_FILENAME__{stdin_filename}")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=include,
+ exclude=exclude,
+ force_exclude=None,
+ report=report,
+ stdin_filename=stdin_filename,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin_filename_and_exclude(self) -> None:
+ # Exclude shouldn't exclude stdin_filename since it is mimicing the
+ # file being passed directly. This is the same as
+ # test_exclude_for_issue_1572
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ stdin_filename = str(path / "b/exclude/a.py")
+ expected = [Path(f"__BLACK_STDIN_FILENAME__{stdin_filename}")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=include,
+ exclude=exclude,
+ force_exclude=None,
+ report=report,
+ stdin_filename=stdin_filename,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_get_sources_with_stdin_filename_and_force_exclude(self) -> None:
+ # Force exclude should exclude the file when passing it through
+ # stdin_filename
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = ""
+ force_exclude = r"/exclude/|a\.py"
+ src = "-"
+ report = black.Report()
+ stdin_filename = str(path / "b/exclude/a.py")
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=include,
+ exclude="",
+ force_exclude=force_exclude,
+ report=report,
+ stdin_filename=stdin_filename,
+ )
+ )
+ self.assertEqual([], sorted(sources))
+
+ def test_reformat_one_with_stdin(self) -> None:
+ with patch(
+ "black.format_stdin_to_stdout",
+ return_value=lambda *args, **kwargs: black.Changed.YES,
+ ) as fsts:
+ report = MagicMock()
+ path = Path("-")
+ black.reformat_one(
+ path,
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ report=report,
+ )
+ fsts.assert_called_once()
+ report.done.assert_called_with(path, black.Changed.YES)
+
+ def test_reformat_one_with_stdin_filename(self) -> None:
+ with patch(
+ "black.format_stdin_to_stdout",
+ return_value=lambda *args, **kwargs: black.Changed.YES,
+ ) as fsts:
+ report = MagicMock()
+ p = "foo.py"
+ path = Path(f"__BLACK_STDIN_FILENAME__{p}")
+ expected = Path(p)
+ black.reformat_one(
+ path,
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ report=report,
+ )
+ fsts.assert_called_once()
+ # __BLACK_STDIN_FILENAME__ should have been striped
+ report.done.assert_called_with(expected, black.Changed.YES)
+
+ def test_reformat_one_with_stdin_and_existing_path(self) -> None:
+ with patch(
+ "black.format_stdin_to_stdout",
+ return_value=lambda *args, **kwargs: black.Changed.YES,
+ ) as fsts:
+ report = MagicMock()
+ # Even with an existing file, since we are forcing stdin, black
+ # should output to stdout and not modify the file inplace
+ p = Path(str(THIS_DIR / "data/collections.py"))
+ # Make sure is_file actually returns True
+ self.assertTrue(p.is_file())
+ path = Path(f"__BLACK_STDIN_FILENAME__{p}")
+ expected = Path(p)
+ black.reformat_one(
+ path,
+ fast=True,
+ write_back=black.WriteBack.YES,
+ mode=DEFAULT_MODE,
+ report=report,
+ )
+ fsts.assert_called_once()
+ # __BLACK_STDIN_FILENAME__ should have been striped
+ report.done.assert_called_with(expected, black.Changed.YES)
+
def test_gitignore_exclude(self) -> None:
path = THIS_DIR / "data" / "include_exclude_tests"
include = re.compile(r"\.pyi?$")
From 4d03716eaea58ee38d77cf2bba0a72b7a27ec9fa Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Tue, 24 Nov 2020 04:39:25 -0500
Subject: [PATCH 095/680] Allow same RHS expressions in annotated assignments
as in regular assignments (#1835)
---
CHANGES.md | 3 +++
src/blib2to3/Grammar.txt | 2 +-
tests/data/python38.py | 18 ++++++++++++++++++
3 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/CHANGES.md b/CHANGES.md
index 67697bd7b07..ca8a0472a3b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -21,6 +21,9 @@
- Added support for PEP 614 relaxed decorator syntax on python 3.9 (#1711)
+- Added parsing support for unparenthesized tuples and yield expressions in annotated
+ assignments (#1835)
+
- use lowercase hex strings (#1692)
#### _Packaging_
diff --git a/src/blib2to3/Grammar.txt b/src/blib2to3/Grammar.txt
index eafaee84cb3..69b9af96608 100644
--- a/src/blib2to3/Grammar.txt
+++ b/src/blib2to3/Grammar.txt
@@ -77,7 +77,7 @@ small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | exec_stmt | assert_stmt)
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
-annassign: ':' test ['=' test]
+annassign: ':' test ['=' (yield_expr|testlist_star_expr)]
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '>>=' | '**=' | '//=')
diff --git a/tests/data/python38.py b/tests/data/python38.py
index 1a7f76167d9..63b0588bc27 100644
--- a/tests/data/python38.py
+++ b/tests/data/python38.py
@@ -11,6 +11,14 @@ def starred_yield():
yield "value1", *my_list
+# all right hand side expressions allowed in regular assignments are now also allowed in
+# annotated assignments
+a : Tuple[ str, int] = "1", 2
+a: Tuple[int , ... ] = b, *c, d
+def t():
+ a : str = yield "a"
+
+
# output
@@ -25,3 +33,13 @@ def starred_return():
def starred_yield():
my_list = ["value2", "value3"]
yield "value1", *my_list
+
+
+# all right hand side expressions allowed in regular assignments are now also allowed in
+# annotated assignments
+a: Tuple[str, int] = "1", 2
+a: Tuple[int, ...] = b, *c, d
+
+
+def t():
+ a: str = yield "a"
From 7f75fe3669ebf0627b1b0476a6d02047e909b959 Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Wed, 25 Nov 2020 19:26:35 -0500
Subject: [PATCH 096/680] Switch back to Python 3.8 for ReadTheDocs (#1839)
ReadTheDocs doesn't support Python 3.9 yet.
---
readthedocs.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/readthedocs.yml b/readthedocs.yml
index 32bcf1fa50e..15065033d0f 100644
--- a/readthedocs.yml
+++ b/readthedocs.yml
@@ -1,6 +1,6 @@
version: 2
python:
- version: 3.9
+ version: 3.8
install:
- requirements: docs/requirements.txt
- method: setuptools
From 2989dc1bf822b1b2a6bd250cea37bbf20c237764 Mon Sep 17 00:00:00 2001
From: Noel Evans
Date: Wed, 9 Dec 2020 23:40:45 +0000
Subject: [PATCH 097/680] vim plugin: Add quiet flag so non-error actions go
unreported (#1733)
---
docs/editor_integration.md | 1 +
plugin/black.vim | 11 +++++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/docs/editor_integration.md b/docs/editor_integration.md
index 21a6865d60e..037b265b9a0 100644
--- a/docs/editor_integration.md
+++ b/docs/editor_integration.md
@@ -123,6 +123,7 @@ Configuration:
- `g:black_linelength` (defaults to `88`)
- `g:black_skip_string_normalization` (defaults to `0`)
- `g:black_virtualenv` (defaults to `~/.vim/black` or `~/.local/share/nvim/black`)
+- `g:black_quiet` (defaults to `0`)
To install with [vim-plug](https://github.com/junegunn/vim-plug):
diff --git a/plugin/black.vim b/plugin/black.vim
index 3dd3f2151c3..c5f0313f4ac 100644
--- a/plugin/black.vim
+++ b/plugin/black.vim
@@ -48,6 +48,9 @@ if !exists("g:black_string_normalization")
let g:black_string_normalization = 1
endif
endif
+if !exists("g:black_quiet")
+ let g:black_quiet = 0
+endif
python3 << EndPython3
import collections
@@ -74,6 +77,7 @@ FLAGS = [
Flag(name="line_length", cast=int),
Flag(name="fast", cast=strtobool),
Flag(name="string_normalization", cast=strtobool),
+ Flag(name="quiet", cast=strtobool),
]
@@ -156,6 +160,7 @@ def Black():
string_normalization=configs["string_normalization"],
is_pyi=vim.current.buffer.name.endswith('.pyi'),
)
+ quiet = configs["quiet"]
buffer_str = '\n'.join(vim.current.buffer) + '\n'
try:
@@ -165,7 +170,8 @@ def Black():
mode=mode,
)
except black.NothingChanged:
- print(f'Already well formatted, good job. (took {time.time() - start:.4f}s)')
+ if not quiet:
+ print(f'Already well formatted, good job. (took {time.time() - start:.4f}s)')
except Exception as exc:
print(exc)
else:
@@ -183,7 +189,8 @@ def Black():
window.cursor = cursor
except vim.error:
window.cursor = (len(window.buffer), 0)
- print(f'Reformatted in {time.time() - start:.4f}s.')
+ if not quiet:
+ print(f'Reformatted in {time.time() - start:.4f}s.')
def get_configs():
path_pyproject_toml = black.find_pyproject_toml(vim.eval("fnamemodify(getcwd(), ':t')"))
From a522aa45c65bb896637d0add27b4c2a9fec6b976 Mon Sep 17 00:00:00 2001
From: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
Date: Sun, 13 Dec 2020 23:20:25 -0800
Subject: [PATCH 098/680] Remove all trace of flake8-mypy (#1867)
flake8-mypy is long dead and shouldn't be used, see
https://github.com/ambv/flake8-mypy. We appear to use pre-commit to run
mypy now anyway.
I ran `pipenv uninstall flake8-mypy`, which seems to have made several
changes to Pipfile.lock. Let me know if there's a better way to do this.
Co-authored-by: hauntsaninja <>
---
Pipfile | 1 -
Pipfile.lock | 842 +++++++++++++++++++++++++++++----------------------
mypy.ini | 1 -
3 files changed, 473 insertions(+), 371 deletions(-)
diff --git a/Pipfile b/Pipfile
index 1ced1ed096b..ba596b3d738 100644
--- a/Pipfile
+++ b/Pipfile
@@ -9,7 +9,6 @@ coverage = "*"
docutils = "==0.15" # not a direct dependency, see https://github.com/pypa/pipenv/issues/3865
flake8 = "*"
flake8-bugbear = "*"
-flake8-mypy = "*"
mypy = ">=0.782"
pre-commit = "*"
readme_renderer = "*"
diff --git a/Pipfile.lock b/Pipfile.lock
index 95b7613114a..a5c38aa0777 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "46390803c9b9e1b77a1b4a29de602d864dea188488d3aee6361030c91529611c"
+ "sha256": "21836c0a63b6e3e1eacd0adec7dea61d2d5989e38225edd976ff144e499f0426"
},
"pipfile-spec": 6,
"requires": {},
@@ -16,21 +16,46 @@
"default": {
"aiohttp": {
"hashes": [
- "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e",
- "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326",
- "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a",
- "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654",
- "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a",
- "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4",
- "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17",
- "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec",
- "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd",
- "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48",
- "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59",
- "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965"
+ "sha256:0b795072bb1bf87b8620120a6373a3c61bfcb8da7e5c2377f4bb23ff4f0b62c9",
+ "sha256:0d438c8ca703b1b714e82ed5b7a4412c82577040dadff479c08405e2a715564f",
+ "sha256:16a3cb5df5c56f696234ea9e65e227d1ebe9c18aa774d36ff42f532139066a5f",
+ "sha256:1edfd82a98c5161497bbb111b2b70c0813102ad7e0aa81cbeb34e64c93863005",
+ "sha256:2406dc1dda01c7f6060ab586e4601f18affb7a6b965c50a8c90ff07569cf782a",
+ "sha256:2858b2504c8697beb9357be01dc47ef86438cc1cb36ecb6991796d19475faa3e",
+ "sha256:2a7b7640167ab536c3cb90cfc3977c7094f1c5890d7eeede8b273c175c3910fd",
+ "sha256:3228b7a51e3ed533f5472f54f70fd0b0a64c48dc1649a0f0e809bec312934d7a",
+ "sha256:328b552513d4f95b0a2eea4c8573e112866107227661834652a8984766aa7656",
+ "sha256:39f4b0a6ae22a1c567cb0630c30dd082481f95c13ca528dc501a7766b9c718c0",
+ "sha256:3b0036c978cbcc4a4512278e98e3e6d9e6b834dc973206162eddf98b586ef1c6",
+ "sha256:3ea8c252d8df5e9166bcf3d9edced2af132f4ead8ac422eac723c5781063709a",
+ "sha256:41608c0acbe0899c852281978492f9ce2c6fbfaf60aff0cefc54a7c4516b822c",
+ "sha256:59d11674964b74a81b149d4ceaff2b674b3b0e4d0f10f0be1533e49c4a28408b",
+ "sha256:5e479df4b2d0f8f02133b7e4430098699450e1b2a826438af6bec9a400530957",
+ "sha256:684850fb1e3e55c9220aad007f8386d8e3e477c4ec9211ae54d968ecdca8c6f9",
+ "sha256:6ccc43d68b81c424e46192a778f97da94ee0630337c9bbe5b2ecc9b0c1c59001",
+ "sha256:6d42debaf55450643146fabe4b6817bb2a55b23698b0434107e892a43117285e",
+ "sha256:710376bf67d8ff4500a31d0c207b8941ff4fba5de6890a701d71680474fe2a60",
+ "sha256:756ae7efddd68d4ea7d89c636b703e14a0c686688d42f588b90778a3c2fc0564",
+ "sha256:77149002d9386fae303a4a162e6bce75cc2161347ad2ba06c2f0182561875d45",
+ "sha256:78e2f18a82b88cbc37d22365cf8d2b879a492faedb3f2975adb4ed8dfe994d3a",
+ "sha256:7d9b42127a6c0bdcc25c3dcf252bb3ddc70454fac593b1b6933ae091396deb13",
+ "sha256:8389d6044ee4e2037dca83e3f6994738550f6ee8cfb746762283fad9b932868f",
+ "sha256:9c1a81af067e72261c9cbe33ea792893e83bc6aa987bfbd6fdc1e5e7b22777c4",
+ "sha256:c1e0920909d916d3375c7a1fdb0b1c78e46170e8bb42792312b6eb6676b2f87f",
+ "sha256:c68fdf21c6f3573ae19c7ee65f9ff185649a060c9a06535e9c3a0ee0bbac9235",
+ "sha256:c733ef3bdcfe52a1a75564389bad4064352274036e7e234730526d155f04d914",
+ "sha256:c9c58b0b84055d8bc27b7df5a9d141df4ee6ff59821f922dd73155861282f6a3",
+ "sha256:d03abec50df423b026a5aa09656bd9d37f1e6a49271f123f31f9b8aed5dc3ea3",
+ "sha256:d2cfac21e31e841d60dc28c0ec7d4ec47a35c608cb8906435d47ef83ffb22150",
+ "sha256:dcc119db14757b0c7bce64042158307b9b1c76471e655751a61b57f5a0e4d78e",
+ "sha256:df3a7b258cc230a65245167a202dd07320a5af05f3d41da1488ba0fa05bc9347",
+ "sha256:df48a623c58180874d7407b4d9ec06a19b84ed47f60a3884345b1a5099c1818b",
+ "sha256:e1b95972a0ae3f248a899cdbac92ba2e01d731225f566569311043ce2226f5e7",
+ "sha256:f326b3c1bbfda5b9308252ee0dcb30b612ee92b0e105d4abec70335fab5b1245",
+ "sha256:f411cb22115cb15452d099fec0ee636b06cf81bfb40ed9c02d30c8dc2bc2e3d1"
],
"index": "pypi",
- "version": "==3.6.2"
+ "version": "==3.7.3"
},
"aiohttp-cors": {
"hashes": [
@@ -53,14 +78,16 @@
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
+ "markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
"hashes": [
- "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
- "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
+ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
+ "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
],
- "version": "==20.2.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.3.0"
},
"black": {
"editable": true,
@@ -90,36 +117,60 @@
"sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"
],
"index": "pypi",
- "version": "==0.6"
+ "python_version <": "3.7",
+ "version": "==0.6",
+ "version >": "0.6"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"multidict": {
"hashes": [
- "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a",
- "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000",
- "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2",
- "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507",
- "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5",
- "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7",
- "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d",
- "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463",
- "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19",
- "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3",
- "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b",
- "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c",
- "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87",
- "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7",
- "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430",
- "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255",
- "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"
- ],
- "version": "==4.7.6"
+ "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a",
+ "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93",
+ "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632",
+ "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656",
+ "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79",
+ "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7",
+ "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d",
+ "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5",
+ "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224",
+ "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26",
+ "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea",
+ "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348",
+ "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6",
+ "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76",
+ "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1",
+ "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f",
+ "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952",
+ "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a",
+ "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37",
+ "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9",
+ "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359",
+ "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8",
+ "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da",
+ "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3",
+ "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d",
+ "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf",
+ "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841",
+ "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d",
+ "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93",
+ "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f",
+ "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647",
+ "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635",
+ "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456",
+ "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda",
+ "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5",
+ "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281",
+ "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==5.1.0"
},
"mypy-extensions": {
"hashes": [
@@ -131,46 +182,80 @@
},
"pathspec": {
"hashes": [
- "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
- "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
+ "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd",
+ "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"
],
"index": "pypi",
- "version": "==0.8.0"
+ "version": "==0.8.1"
},
"regex": {
"hashes": [
- "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204",
- "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162",
- "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f",
- "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb",
- "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6",
- "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7",
- "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88",
- "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99",
- "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644",
- "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a",
- "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840",
- "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067",
- "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd",
- "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4",
- "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e",
- "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89",
- "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e",
- "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc",
- "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf",
- "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341",
- "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"
+ "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538",
+ "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4",
+ "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc",
+ "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa",
+ "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444",
+ "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1",
+ "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af",
+ "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8",
+ "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9",
+ "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88",
+ "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba",
+ "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364",
+ "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e",
+ "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7",
+ "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0",
+ "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31",
+ "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683",
+ "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee",
+ "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b",
+ "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884",
+ "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c",
+ "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e",
+ "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562",
+ "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85",
+ "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c",
+ "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6",
+ "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d",
+ "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b",
+ "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70",
+ "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b",
+ "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b",
+ "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f",
+ "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0",
+ "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5",
+ "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5",
+ "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f",
+ "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e",
+ "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512",
+ "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d",
+ "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917",
+ "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"
],
"index": "pypi",
- "version": "==2020.7.14"
+ "version": "==2020.11.13"
+ },
+ "setuptools-scm": {
+ "hashes": [
+ "sha256:1fc4e25df445351d172bb4788f4d07f9e9ce0e8b7dee6b19584e46110172ca13",
+ "sha256:48b31488d089270500f120efea723968c01abd85fd4876043a3b7c7ef7d0b761",
+ "sha256:62fa535edb31ece9fa65dc9dcb3056145b8020c8c26c0ef1018aef33db95c40d",
+ "sha256:b928021c4381f96d577847d540d6e03065f8f8851c768a0c9bc552d463bae0d4",
+ "sha256:c85b6b46d0edd40d2301038cdea96bb6adc14d62ef943e75afb08b3e7bcf142a",
+ "sha256:db4ab2e0c2644ba71b1e5212b14ff65dbf0af465796d314a75e0cf6128f605f7",
+ "sha256:e878036c2527dfd05818727846338be86b2be6955741b85fab2625f63d001021",
+ "sha256:eb19b46816fc106a4c2d4180022687eab40f9773cf61390b845afb093d1f4ecd"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==5.0.1"
},
"toml": {
"hashes": [
- "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
+ "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"index": "pypi",
- "version": "==0.10.1"
+ "version": "==0.10.2"
},
"typed-ast": {
"hashes": [
@@ -187,6 +272,7 @@
"sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
"sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
+ "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d",
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
"sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c",
@@ -198,8 +284,10 @@
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
"sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072",
+ "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298",
"sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91",
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
+ "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f",
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
],
"index": "pypi",
@@ -216,61 +304,91 @@
},
"yarl": {
"hashes": [
- "sha256:03b7a44384ad60be1b7be93c2a24dc74895f8d767ea0bce15b2f6fc7695a3843",
- "sha256:076157404db9db4bb3fa9db22db319bbb36d075eeab19ba018ce20ae0cacf037",
- "sha256:1c05ae3d5ea4287470046a2c2754f0a4c171b84ea72c8a691f776eb1753dfb91",
- "sha256:2467baf8233f7c64048df37e11879c553943ffe7f373e689711ec2807ea13805",
- "sha256:2bb2e21cf062dfbe985c3cd4618bae9f25271efcad9e7be1277861247eee9839",
- "sha256:311effab3b3828ab34f0e661bb57ff422f67d5c33056298bda4c12195251f8dd",
- "sha256:3526cb5905907f0e42bee7ef57ae4a5f02bc27dcac27859269e2bba0caa4c2b6",
- "sha256:39b1e586f34b1d2512c9b39aa3cf24c870c972d525e36edc9ee19065db4737bb",
- "sha256:4bed5cd7c8e69551eb19df15295ba90e62b9a6a1149c76eb4a9bab194402a156",
- "sha256:51c6d3cf7a1f1fbe134bb92f33b7affd94d6de24cd64b466eb12de52120fb8c6",
- "sha256:59f78b5da34ddcffb663b772f7619e296518712e022e57fc5d9f921818e2ab7c",
- "sha256:6f29115b0c330da25a04f48612d75333bca04521181a666ca0b8761005a99150",
- "sha256:73d4e1e1ef5e52d526c92f07d16329e1678612c6a81dd8101fdcae11a72de15c",
- "sha256:9b48d31f8d881713fd461abfe7acbb4dcfeb47cec3056aa83f2fbcd2244577f7",
- "sha256:a1fd575dd058e10ad4c35065e7c3007cc74d142f622b14e168d8a273a2fa8713",
- "sha256:b3dd1052afd436ba737e61f5d3bed1f43a7f9a33fc58fbe4226eb919a7006019",
- "sha256:b99c25ed5c355b35d1e6dae87ac7297a4844a57dc5766b173b88b6163a36eb0d",
- "sha256:c056e86bff5a0b566e0d9fab4f67e83b12ae9cbcd250d334cbe2005bbe8c96f2",
- "sha256:c45b49b59a5724869899798e1bbd447ac486215269511d3b76b4c235a1b766b6",
- "sha256:cd623170c729a865037828e3f99f8ebdb22a467177a539680dfc5670b74c84e2",
- "sha256:d25d3311794e6c71b608d7c47651c8f65eea5ab15358a27f29330b3475e8f8e5",
- "sha256:d695439c201ed340745250f9eb4dfe8d32bf1e680c16477107b8f3ce4bff4fdb",
- "sha256:d77f6c9133d2aabb290a7846aaa74ec14d7b5ab35b01591fac5a70c4a8c959a2",
- "sha256:d894a2442d2cd20a3b0b0dce5a353d316c57d25a2b445e03f7eac90eee27b8af",
- "sha256:db643ce2b58a4bd11a82348225c53c76ecdd82bb37cf4c085e6df1b676f4038c",
- "sha256:e3a0c43a26dfed955b2a06fdc4d51d2c51bc2200aff8ce8faf14e676ea8c8862",
- "sha256:e77bf79ad1ccae672eab22453838382fe9029fc27c8029e84913855512a587d8",
- "sha256:f2f0174cb15435957d3b751093f89aede77df59a499ab7516bbb633b77ead13a",
- "sha256:f3031c78edf10315abe232254e6a36b65afe65fded41ee54ed7976d0b2cdf0da",
- "sha256:f4c007156732866aa4507d619fe6f8f2748caabed4f66b276ccd97c82572620c",
- "sha256:f4f27ff3dd80bc7c402def211a47291ea123d59a23f59fe18fc0e81e3e71f385",
- "sha256:f57744fc61e118b5d114ae8077d8eb9df4d2d2c11e2af194e21f0c11ed9dcf6c",
- "sha256:f835015a825980b65356e9520979a1564c56efea7da7d4b68a14d4a07a3a7336"
- ],
- "version": "==1.6.2"
+ "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e",
+ "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434",
+ "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366",
+ "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3",
+ "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec",
+ "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959",
+ "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e",
+ "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c",
+ "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6",
+ "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a",
+ "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6",
+ "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424",
+ "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e",
+ "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f",
+ "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50",
+ "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2",
+ "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc",
+ "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4",
+ "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970",
+ "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10",
+ "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0",
+ "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406",
+ "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896",
+ "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643",
+ "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721",
+ "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478",
+ "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724",
+ "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e",
+ "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8",
+ "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96",
+ "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25",
+ "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76",
+ "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2",
+ "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2",
+ "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c",
+ "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a",
+ "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.6.3"
}
},
"develop": {
"aiohttp": {
"hashes": [
- "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e",
- "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326",
- "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a",
- "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654",
- "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a",
- "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4",
- "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17",
- "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec",
- "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd",
- "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48",
- "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59",
- "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965"
+ "sha256:0b795072bb1bf87b8620120a6373a3c61bfcb8da7e5c2377f4bb23ff4f0b62c9",
+ "sha256:0d438c8ca703b1b714e82ed5b7a4412c82577040dadff479c08405e2a715564f",
+ "sha256:16a3cb5df5c56f696234ea9e65e227d1ebe9c18aa774d36ff42f532139066a5f",
+ "sha256:1edfd82a98c5161497bbb111b2b70c0813102ad7e0aa81cbeb34e64c93863005",
+ "sha256:2406dc1dda01c7f6060ab586e4601f18affb7a6b965c50a8c90ff07569cf782a",
+ "sha256:2858b2504c8697beb9357be01dc47ef86438cc1cb36ecb6991796d19475faa3e",
+ "sha256:2a7b7640167ab536c3cb90cfc3977c7094f1c5890d7eeede8b273c175c3910fd",
+ "sha256:3228b7a51e3ed533f5472f54f70fd0b0a64c48dc1649a0f0e809bec312934d7a",
+ "sha256:328b552513d4f95b0a2eea4c8573e112866107227661834652a8984766aa7656",
+ "sha256:39f4b0a6ae22a1c567cb0630c30dd082481f95c13ca528dc501a7766b9c718c0",
+ "sha256:3b0036c978cbcc4a4512278e98e3e6d9e6b834dc973206162eddf98b586ef1c6",
+ "sha256:3ea8c252d8df5e9166bcf3d9edced2af132f4ead8ac422eac723c5781063709a",
+ "sha256:41608c0acbe0899c852281978492f9ce2c6fbfaf60aff0cefc54a7c4516b822c",
+ "sha256:59d11674964b74a81b149d4ceaff2b674b3b0e4d0f10f0be1533e49c4a28408b",
+ "sha256:5e479df4b2d0f8f02133b7e4430098699450e1b2a826438af6bec9a400530957",
+ "sha256:684850fb1e3e55c9220aad007f8386d8e3e477c4ec9211ae54d968ecdca8c6f9",
+ "sha256:6ccc43d68b81c424e46192a778f97da94ee0630337c9bbe5b2ecc9b0c1c59001",
+ "sha256:6d42debaf55450643146fabe4b6817bb2a55b23698b0434107e892a43117285e",
+ "sha256:710376bf67d8ff4500a31d0c207b8941ff4fba5de6890a701d71680474fe2a60",
+ "sha256:756ae7efddd68d4ea7d89c636b703e14a0c686688d42f588b90778a3c2fc0564",
+ "sha256:77149002d9386fae303a4a162e6bce75cc2161347ad2ba06c2f0182561875d45",
+ "sha256:78e2f18a82b88cbc37d22365cf8d2b879a492faedb3f2975adb4ed8dfe994d3a",
+ "sha256:7d9b42127a6c0bdcc25c3dcf252bb3ddc70454fac593b1b6933ae091396deb13",
+ "sha256:8389d6044ee4e2037dca83e3f6994738550f6ee8cfb746762283fad9b932868f",
+ "sha256:9c1a81af067e72261c9cbe33ea792893e83bc6aa987bfbd6fdc1e5e7b22777c4",
+ "sha256:c1e0920909d916d3375c7a1fdb0b1c78e46170e8bb42792312b6eb6676b2f87f",
+ "sha256:c68fdf21c6f3573ae19c7ee65f9ff185649a060c9a06535e9c3a0ee0bbac9235",
+ "sha256:c733ef3bdcfe52a1a75564389bad4064352274036e7e234730526d155f04d914",
+ "sha256:c9c58b0b84055d8bc27b7df5a9d141df4ee6ff59821f922dd73155861282f6a3",
+ "sha256:d03abec50df423b026a5aa09656bd9d37f1e6a49271f123f31f9b8aed5dc3ea3",
+ "sha256:d2cfac21e31e841d60dc28c0ec7d4ec47a35c608cb8906435d47ef83ffb22150",
+ "sha256:dcc119db14757b0c7bce64042158307b9b1c76471e655751a61b57f5a0e4d78e",
+ "sha256:df3a7b258cc230a65245167a202dd07320a5af05f3d41da1488ba0fa05bc9347",
+ "sha256:df48a623c58180874d7407b4d9ec06a19b84ed47f60a3884345b1a5099c1818b",
+ "sha256:e1b95972a0ae3f248a899cdbac92ba2e01d731225f566569311043ce2226f5e7",
+ "sha256:f326b3c1bbfda5b9308252ee0dcb30b612ee92b0e105d4abec70335fab5b1245",
+ "sha256:f411cb22115cb15452d099fec0ee636b06cf81bfb40ed9c02d30c8dc2bc2e3d1"
],
"index": "pypi",
- "version": "==3.6.2"
+ "version": "==3.7.3"
},
"aiohttp-cors": {
"hashes": [
@@ -300,21 +418,24 @@
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
+ "markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1"
},
"attrs": {
"hashes": [
- "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
- "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
+ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
+ "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
],
- "version": "==20.2.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.3.0"
},
"babel": {
"hashes": [
- "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
- "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
+ "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5",
+ "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"
],
- "version": "==2.8.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.9.0"
},
"black": {
"editable": true,
@@ -328,61 +449,22 @@
"sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080",
"sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==3.2.1"
},
"certifi": {
"hashes": [
- "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
- "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
- ],
- "version": "==2020.6.20"
- },
- "cffi": {
- "hashes": [
- "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d",
- "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b",
- "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4",
- "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f",
- "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3",
- "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579",
- "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537",
- "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e",
- "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05",
- "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171",
- "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca",
- "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522",
- "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c",
- "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc",
- "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d",
- "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808",
- "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828",
- "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869",
- "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d",
- "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9",
- "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0",
- "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc",
- "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15",
- "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c",
- "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a",
- "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3",
- "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1",
- "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768",
- "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d",
- "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b",
- "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e",
- "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d",
- "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730",
- "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394",
- "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1",
- "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"
- ],
- "version": "==1.14.3"
+ "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
+ "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
+ ],
+ "version": "==2020.12.5"
},
"cfgv": {
"hashes": [
"sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d",
"sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"
],
+ "markers": "python_full_version >= '3.6.1'",
"version": "==3.2.0"
},
"chardet": {
@@ -405,6 +487,7 @@
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.4.4"
},
"commonmark": {
@@ -454,34 +537,6 @@
"index": "pypi",
"version": "==5.3"
},
- "cryptography": {
- "hashes": [
- "sha256:22f8251f68953553af4f9c11ec5f191198bc96cff9f0ac5dd5ff94daede0ee6d",
- "sha256:284e275e3c099a80831f9898fb5c9559120d27675c3521278faba54e584a7832",
- "sha256:3e17d02941c0f169c5b877597ca8be895fca0e5e3eb882526a74aa4804380a98",
- "sha256:52a47e60953679eea0b4d490ca3c241fb1b166a7b161847ef4667dfd49e7699d",
- "sha256:57b8c1ed13b8aa386cabbfde3be175d7b155682470b0e259fecfe53850967f8a",
- "sha256:6a8f64ed096d13f92d1f601a92d9fd1f1025dc73a2ca1ced46dcf5e0d4930943",
- "sha256:6e8a3c7c45101a7eeee93102500e1b08f2307c717ff553fcb3c1127efc9b6917",
- "sha256:7ef41304bf978f33cfb6f43ca13bb0faac0c99cda33693aa20ad4f5e34e8cb8f",
- "sha256:87c2fffd61e934bc0e2c927c3764c20b22d7f5f7f812ee1a477de4c89b044ca6",
- "sha256:88069392cd9a1e68d2cfd5c3a2b0d72a44ef3b24b8977a4f7956e9e3c4c9477a",
- "sha256:8a0866891326d3badb17c5fd3e02c926b635e8923fa271b4813cd4d972a57ff3",
- "sha256:8f0fd8b0751d75c4483c534b209e39e918f0d14232c0d8a2a76e687f64ced831",
- "sha256:9a07e6d255053674506091d63ab4270a119e9fc83462c7ab1dbcb495b76307af",
- "sha256:9a8580c9afcdcddabbd064c0a74f337af74ff4529cdf3a12fa2e9782d677a2e5",
- "sha256:bd80bc156d3729b38cb227a5a76532aef693b7ac9e395eea8063ee50ceed46a5",
- "sha256:d1cbc3426e6150583b22b517ef3720036d7e3152d428c864ff0f3fcad2b97591",
- "sha256:e15ac84dcdb89f92424cbaca4b0b34e211e7ce3ee7b0ec0e4f3c55cee65fae5a",
- "sha256:e4789b84f8dedf190148441f7c5bfe7244782d9cbb194a36e17b91e7d3e1cca9",
- "sha256:f01c9116bfb3ad2831e125a73dcd957d173d6ddca7701528eff1e7d97972872c",
- "sha256:f0e3986f6cce007216b23c490f093f35ce2068f3c244051e559f647f6731b7ae",
- "sha256:f2aa3f8ba9e2e3fd49bd3de743b976ab192fbf0eb0348cebde5d2a9de0090a9f",
- "sha256:fb70a4cedd69dc52396ee114416a3656e011fb0311fca55eb55c7be6ed9c8aef"
- ],
- "index": "pypi",
- "version": "==3.2"
- },
"distlib": {
"hashes": [
"sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb",
@@ -492,6 +547,7 @@
"docutils": {
"hashes": [
"sha256:54a349c622ff31c91cbec43b0b512f113b5b24daf00e2ea530bb1bd9aac14849",
+ "sha256:ba4584f9107571ced0d2c7f56a5499c696215ba90797849c92d395979da68521",
"sha256:d2ddba74835cb090a1b627d3de4e7835c628d07ee461f7b4480f51af2fe4d448"
],
"index": "pypi",
@@ -506,40 +562,34 @@
},
"flake8": {
"hashes": [
- "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c",
- "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"
+ "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839",
+ "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"
],
"index": "pypi",
- "version": "==3.8.3"
+ "version": "==3.8.4"
},
"flake8-bugbear": {
"hashes": [
- "sha256:a3ddc03ec28ba2296fc6f89444d1c946a6b76460f859795b35b77d4920a51b63",
- "sha256:bd02e4b009fb153fe6072c31c52aeab5b133d508095befb2ffcf3b41c4823162"
- ],
- "index": "pypi",
- "version": "==20.1.4"
- },
- "flake8-mypy": {
- "hashes": [
- "sha256:47120db63aff631ee1f84bac6fe8e64731dc66da3efc1c51f85e15ade4a3ba18",
- "sha256:cff009f4250e8391bf48990093cff85802778c345c8449d6498b62efefeebcbc"
+ "sha256:528020129fea2dea33a466b9d64ab650aa3e5f9ffc788b70ea4bc6cf18283538",
+ "sha256:f35b8135ece7a014bc0aee5b5d485334ac30a6da48494998cc1fabf7ec70d703"
],
"index": "pypi",
- "version": "==17.8.0"
+ "version": "==20.11.1"
},
"identify": {
"hashes": [
- "sha256:3139bf72d81dfd785b0a464e2776bd59bdc725b4cc10e6cf46b56a0db931c82e",
- "sha256:969d844b7a85d32a5f9ac4e163df6e846d73c87c8b75847494ee8f4bd2186421"
+ "sha256:943cd299ac7f5715fcb3f684e2fc1594c1e0f22a90d15398e5888143bd4144b5",
+ "sha256:cc86e6a9a390879dcc2976cef169dd9cc48843ed70b7380f321d1b118163c60e"
],
- "version": "==1.5.6"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.5.10"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"imagesize": {
@@ -547,29 +597,24 @@
"sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
"sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.0"
},
- "jeepney": {
- "hashes": [
- "sha256:3479b861cc2b6407de5188695fa1a8d57e5072d7059322469b62628869b8e36e",
- "sha256:d6c6b49683446d2407d2fe3acb7a368a77ff063f9182fe427da15d622adc24cf"
- ],
- "markers": "sys_platform == 'linux'",
- "version": "==0.4.3"
- },
"jinja2": {
"hashes": [
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.11.2"
},
"keyring": {
"hashes": [
- "sha256:4e34ea2fdec90c1c43d6610b5a5fafa1b9097db1802948e90caf5763974b8f8d",
- "sha256:9aeadd006a852b78f4b4ef7c7556c2774d2432bbef8ee538a3e9089ac8b11466"
+ "sha256:12de23258a95f3b13e5b167f7a641a878e91eab8ef16fafc077720a95e6115bb",
+ "sha256:207bd66f2a9881c835dad653da04e196c678bf104f8252141d2d3c4f31051579"
],
- "version": "==21.4.0"
+ "markers": "python_version >= '3.6'",
+ "version": "==21.5.0"
},
"markupsafe": {
"hashes": [
@@ -607,6 +652,7 @@
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1"
},
"mccabe": {
@@ -618,45 +664,66 @@
},
"multidict": {
"hashes": [
- "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a",
- "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000",
- "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2",
- "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507",
- "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5",
- "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7",
- "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d",
- "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463",
- "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19",
- "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3",
- "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b",
- "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c",
- "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87",
- "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7",
- "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430",
- "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255",
- "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"
- ],
- "version": "==4.7.6"
+ "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a",
+ "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93",
+ "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632",
+ "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656",
+ "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79",
+ "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7",
+ "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d",
+ "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5",
+ "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224",
+ "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26",
+ "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea",
+ "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348",
+ "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6",
+ "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76",
+ "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1",
+ "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f",
+ "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952",
+ "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a",
+ "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37",
+ "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9",
+ "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359",
+ "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8",
+ "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da",
+ "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3",
+ "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d",
+ "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf",
+ "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841",
+ "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d",
+ "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93",
+ "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f",
+ "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647",
+ "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635",
+ "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456",
+ "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda",
+ "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5",
+ "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281",
+ "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==5.1.0"
},
"mypy": {
"hashes": [
- "sha256:2c6cde8aa3426c1682d35190b59b71f661237d74b053822ea3d748e2c9578a7c",
- "sha256:3fdda71c067d3ddfb21da4b80e2686b71e9e5c72cca65fa216d207a358827f86",
- "sha256:5dd13ff1f2a97f94540fd37a49e5d255950ebcdf446fb597463a40d0df3fac8b",
- "sha256:6731603dfe0ce4352c555c6284c6db0dc935b685e9ce2e4cf220abe1e14386fd",
- "sha256:6bb93479caa6619d21d6e7160c552c1193f6952f0668cdda2f851156e85186fc",
- "sha256:81c7908b94239c4010e16642c9102bfc958ab14e36048fa77d0be3289dda76ea",
- "sha256:9c7a9a7ceb2871ba4bac1cf7217a7dd9ccd44c27c2950edbc6dc08530f32ad4e",
- "sha256:a4a2cbcfc4cbf45cd126f531dedda8485671545b43107ded25ce952aac6fb308",
- "sha256:b7fbfabdbcc78c4f6fc4712544b9b0d6bf171069c6e0e3cb82440dd10ced3406",
- "sha256:c05b9e4fb1d8a41d41dec8786c94f3b95d3c5f528298d769eb8e73d293abc48d",
- "sha256:d7df6eddb6054d21ca4d3c6249cae5578cb4602951fd2b6ee2f5510ffb098707",
- "sha256:e0b61738ab504e656d1fe4ff0c0601387a5489ca122d55390ade31f9ca0e252d",
- "sha256:eff7d4a85e9eea55afa34888dfeaccde99e7520b51f867ac28a48492c0b1130c",
- "sha256:f05644db6779387ccdb468cc47a44b4356fc2ffa9287135d05b70a98dc83b89a"
+ "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324",
+ "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc",
+ "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802",
+ "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122",
+ "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975",
+ "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7",
+ "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666",
+ "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669",
+ "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178",
+ "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01",
+ "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea",
+ "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de",
+ "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1",
+ "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"
],
"index": "pypi",
- "version": "==0.782"
+ "version": "==0.790"
},
"mypy-extensions": {
"hashes": [
@@ -675,18 +742,19 @@
},
"packaging": {
"hashes": [
- "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
- "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
+ "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858",
+ "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"
],
- "version": "==20.4"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.8"
},
"pathspec": {
"hashes": [
- "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
- "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
+ "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd",
+ "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"
],
"index": "pypi",
- "version": "==0.8.0"
+ "version": "==0.8.1"
},
"pkginfo": {
"hashes": [
@@ -697,64 +765,63 @@
},
"pre-commit": {
"hashes": [
- "sha256:810aef2a2ba4f31eed1941fc270e72696a1ad5590b9751839c90807d0fff6b9a",
- "sha256:c54fd3e574565fe128ecc5e7d2f91279772ddb03f8729645fa812fe809084a70"
+ "sha256:6c86d977d00ddc8a60d68eec19f51ef212d9462937acf3ea37c7adec32284ac0",
+ "sha256:ee784c11953e6d8badb97d19bc46b997a3a9eded849881ec587accd8608d74a4"
],
"index": "pypi",
- "version": "==2.7.1"
+ "version": "==2.9.3"
},
"pycodestyle": {
"hashes": [
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.6.0"
},
- "pycparser": {
- "hashes": [
- "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
- "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
- ],
- "version": "==2.20"
- },
"pyflakes": {
"hashes": [
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
"sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.2.0"
},
"pygments": {
"hashes": [
- "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0",
- "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"
+ "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716",
+ "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"
],
- "version": "==2.7.2"
+ "markers": "python_version >= '3.5'",
+ "version": "==2.7.3"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytz": {
"hashes": [
- "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
- "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"
+ "sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268",
+ "sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd"
],
- "version": "==2020.1"
+ "version": "==2020.4"
},
"pyyaml": {
"hashes": [
"sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97",
"sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76",
"sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2",
+ "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e",
"sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648",
"sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf",
"sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f",
"sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2",
"sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee",
+ "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a",
"sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d",
"sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c",
"sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"
@@ -763,11 +830,11 @@
},
"readme-renderer": {
"hashes": [
- "sha256:cbe9db71defedd2428a1589cdc545f9bd98e59297449f69d721ef8f1cfced68d",
- "sha256:cc4957a803106e820d05d14f71033092537a22daa4f406dfbdd61177e0936376"
+ "sha256:267854ac3b1530633c2394ead828afcd060fc273217c42ac36b6be9c42cd9a9d",
+ "sha256:6b7e5aa59210a40de72eb79931491eaf46fefca2952b9181268bd7c7c65c260a"
],
"index": "pypi",
- "version": "==26.0"
+ "version": "==28.0"
},
"recommonmark": {
"hashes": [
@@ -779,37 +846,58 @@
},
"regex": {
"hashes": [
- "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204",
- "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162",
- "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f",
- "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb",
- "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6",
- "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7",
- "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88",
- "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99",
- "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644",
- "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a",
- "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840",
- "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067",
- "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd",
- "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4",
- "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e",
- "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89",
- "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e",
- "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc",
- "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf",
- "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341",
- "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"
+ "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538",
+ "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4",
+ "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc",
+ "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa",
+ "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444",
+ "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1",
+ "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af",
+ "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8",
+ "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9",
+ "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88",
+ "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba",
+ "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364",
+ "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e",
+ "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7",
+ "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0",
+ "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31",
+ "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683",
+ "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee",
+ "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b",
+ "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884",
+ "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c",
+ "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e",
+ "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562",
+ "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85",
+ "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c",
+ "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6",
+ "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d",
+ "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b",
+ "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70",
+ "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b",
+ "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b",
+ "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f",
+ "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0",
+ "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5",
+ "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5",
+ "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f",
+ "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e",
+ "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512",
+ "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d",
+ "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917",
+ "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"
],
"index": "pypi",
- "version": "==2020.7.14"
+ "version": "==2020.11.13"
},
"requests": {
"hashes": [
- "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
- "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
+ "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8",
+ "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"
],
- "version": "==2.24.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==2.25.0"
},
"requests-toolbelt": {
"hashes": [
@@ -825,27 +913,26 @@
],
"version": "==1.4.0"
},
- "secretstorage": {
- "hashes": [
- "sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6",
- "sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b"
- ],
- "markers": "sys_platform == 'linux'",
- "version": "==3.1.2"
- },
"setuptools-scm": {
"hashes": [
- "sha256:69258e2eeba5f7ce1ed7a5f109519580fa3578250f8e4d6684859f86d1b15826",
- "sha256:a8994582e716ec690f33fec70cca0f85bd23ec974e3f783233e4879090a7faa8"
+ "sha256:1fc4e25df445351d172bb4788f4d07f9e9ce0e8b7dee6b19584e46110172ca13",
+ "sha256:48b31488d089270500f120efea723968c01abd85fd4876043a3b7c7ef7d0b761",
+ "sha256:62fa535edb31ece9fa65dc9dcb3056145b8020c8c26c0ef1018aef33db95c40d",
+ "sha256:b928021c4381f96d577847d540d6e03065f8f8851c768a0c9bc552d463bae0d4",
+ "sha256:c85b6b46d0edd40d2301038cdea96bb6adc14d62ef943e75afb08b3e7bcf142a",
+ "sha256:db4ab2e0c2644ba71b1e5212b14ff65dbf0af465796d314a75e0cf6128f605f7",
+ "sha256:e878036c2527dfd05818727846338be86b2be6955741b85fab2625f63d001021",
+ "sha256:eb19b46816fc106a4c2d4180022687eab40f9773cf61390b845afb093d1f4ecd"
],
- "index": "pypi",
- "version": "==4.1.2"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==5.0.1"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"snowballstemmer": {
@@ -857,17 +944,18 @@
},
"sphinx": {
"hashes": [
- "sha256:321d6d9b16fa381a5306e5a0b76cd48ffbc588e6340059a729c6fdd66087e0e8",
- "sha256:ce6fd7ff5b215af39e2fcd44d4a321f6694b4530b6f2b2109b64d120773faea0"
+ "sha256:1e8d592225447104d1172be415bc2972bd1357e3e12fdc76edf2261105db4300",
+ "sha256:d4e59ad4ea55efbb3c05cde3bfc83bfc14f0c95aa95c3d75346fcce186a47960"
],
"index": "pypi",
- "version": "==3.2.1"
+ "version": "==3.3.1"
},
"sphinxcontrib-applehelp": {
"hashes": [
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
"sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.0.2"
},
"sphinxcontrib-devhelp": {
@@ -875,6 +963,7 @@
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.0.2"
},
"sphinxcontrib-htmlhelp": {
@@ -882,6 +971,7 @@
"sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
"sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.0.3"
},
"sphinxcontrib-jsmath": {
@@ -889,6 +979,7 @@
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.0.1"
},
"sphinxcontrib-qthelp": {
@@ -896,6 +987,7 @@
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.0.3"
},
"sphinxcontrib-serializinghtml": {
@@ -903,22 +995,24 @@
"sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
"sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.1.4"
},
"toml": {
"hashes": [
- "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
- "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
+ "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
"index": "pypi",
- "version": "==0.10.1"
+ "version": "==0.10.2"
},
"tqdm": {
"hashes": [
- "sha256:9ad44aaf0fc3697c06f6e05c7cf025dd66bc7bcb7613c66d85f4464c47ac8fad",
- "sha256:ef54779f1c09f346b2b5a8e5c61f96fbcb639929e640e59f8cf810794f406432"
+ "sha256:38b658a3e4ecf9b4f6f8ff75ca16221ae3378b2e175d846b6b33ea3a20852cf5",
+ "sha256:d4f413aecb61c9779888c64ddf0c62910ad56dcbe857d8922bb505d4dbff0df1"
],
- "version": "==4.51.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==4.54.1"
},
"twine": {
"hashes": [
@@ -943,6 +1037,7 @@
"sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
"sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
+ "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d",
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
"sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c",
@@ -954,8 +1049,10 @@
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
"sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072",
+ "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298",
"sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91",
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
+ "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f",
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
],
"index": "pypi",
@@ -972,17 +1069,19 @@
},
"urllib3": {
"hashes": [
- "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2",
- "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"
+ "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08",
+ "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"
],
- "version": "==1.25.11"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==1.26.2"
},
"virtualenv": {
"hashes": [
- "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2",
- "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"
+ "sha256:54b05fc737ea9c9ee9f8340f579e5da5b09fb64fd010ab5757eb90268616907c",
+ "sha256:b7a8ec323ee02fb2312f098b6b4c9de99559b462775bc8fe3627a73706603c1b"
],
- "version": "==20.1.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.2.2"
},
"webencodings": {
"hashes": [
@@ -993,49 +1092,54 @@
},
"wheel": {
"hashes": [
- "sha256:497add53525d16c173c2c1c733b8f655510e909ea78cc0e29d374243544b77a2",
- "sha256:99a22d87add3f634ff917310a3d87e499f19e663413a52eb9232c447aa646c9f"
+ "sha256:78b5b185f0e5763c26ca1e324373aadd49182ca90e825f7853f4b2509215dc0e",
+ "sha256:e11eefd162658ea59a60a0f6c7d493a7190ea4b9a85e335b33489d9f17e0245e"
],
"index": "pypi",
- "version": "==0.35.1"
+ "version": "==0.36.2"
},
"yarl": {
"hashes": [
- "sha256:03b7a44384ad60be1b7be93c2a24dc74895f8d767ea0bce15b2f6fc7695a3843",
- "sha256:076157404db9db4bb3fa9db22db319bbb36d075eeab19ba018ce20ae0cacf037",
- "sha256:1c05ae3d5ea4287470046a2c2754f0a4c171b84ea72c8a691f776eb1753dfb91",
- "sha256:2467baf8233f7c64048df37e11879c553943ffe7f373e689711ec2807ea13805",
- "sha256:2bb2e21cf062dfbe985c3cd4618bae9f25271efcad9e7be1277861247eee9839",
- "sha256:311effab3b3828ab34f0e661bb57ff422f67d5c33056298bda4c12195251f8dd",
- "sha256:3526cb5905907f0e42bee7ef57ae4a5f02bc27dcac27859269e2bba0caa4c2b6",
- "sha256:39b1e586f34b1d2512c9b39aa3cf24c870c972d525e36edc9ee19065db4737bb",
- "sha256:4bed5cd7c8e69551eb19df15295ba90e62b9a6a1149c76eb4a9bab194402a156",
- "sha256:51c6d3cf7a1f1fbe134bb92f33b7affd94d6de24cd64b466eb12de52120fb8c6",
- "sha256:59f78b5da34ddcffb663b772f7619e296518712e022e57fc5d9f921818e2ab7c",
- "sha256:6f29115b0c330da25a04f48612d75333bca04521181a666ca0b8761005a99150",
- "sha256:73d4e1e1ef5e52d526c92f07d16329e1678612c6a81dd8101fdcae11a72de15c",
- "sha256:9b48d31f8d881713fd461abfe7acbb4dcfeb47cec3056aa83f2fbcd2244577f7",
- "sha256:a1fd575dd058e10ad4c35065e7c3007cc74d142f622b14e168d8a273a2fa8713",
- "sha256:b3dd1052afd436ba737e61f5d3bed1f43a7f9a33fc58fbe4226eb919a7006019",
- "sha256:b99c25ed5c355b35d1e6dae87ac7297a4844a57dc5766b173b88b6163a36eb0d",
- "sha256:c056e86bff5a0b566e0d9fab4f67e83b12ae9cbcd250d334cbe2005bbe8c96f2",
- "sha256:c45b49b59a5724869899798e1bbd447ac486215269511d3b76b4c235a1b766b6",
- "sha256:cd623170c729a865037828e3f99f8ebdb22a467177a539680dfc5670b74c84e2",
- "sha256:d25d3311794e6c71b608d7c47651c8f65eea5ab15358a27f29330b3475e8f8e5",
- "sha256:d695439c201ed340745250f9eb4dfe8d32bf1e680c16477107b8f3ce4bff4fdb",
- "sha256:d77f6c9133d2aabb290a7846aaa74ec14d7b5ab35b01591fac5a70c4a8c959a2",
- "sha256:d894a2442d2cd20a3b0b0dce5a353d316c57d25a2b445e03f7eac90eee27b8af",
- "sha256:db643ce2b58a4bd11a82348225c53c76ecdd82bb37cf4c085e6df1b676f4038c",
- "sha256:e3a0c43a26dfed955b2a06fdc4d51d2c51bc2200aff8ce8faf14e676ea8c8862",
- "sha256:e77bf79ad1ccae672eab22453838382fe9029fc27c8029e84913855512a587d8",
- "sha256:f2f0174cb15435957d3b751093f89aede77df59a499ab7516bbb633b77ead13a",
- "sha256:f3031c78edf10315abe232254e6a36b65afe65fded41ee54ed7976d0b2cdf0da",
- "sha256:f4c007156732866aa4507d619fe6f8f2748caabed4f66b276ccd97c82572620c",
- "sha256:f4f27ff3dd80bc7c402def211a47291ea123d59a23f59fe18fc0e81e3e71f385",
- "sha256:f57744fc61e118b5d114ae8077d8eb9df4d2d2c11e2af194e21f0c11ed9dcf6c",
- "sha256:f835015a825980b65356e9520979a1564c56efea7da7d4b68a14d4a07a3a7336"
- ],
- "version": "==1.6.2"
+ "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e",
+ "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434",
+ "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366",
+ "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3",
+ "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec",
+ "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959",
+ "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e",
+ "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c",
+ "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6",
+ "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a",
+ "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6",
+ "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424",
+ "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e",
+ "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f",
+ "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50",
+ "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2",
+ "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc",
+ "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4",
+ "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970",
+ "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10",
+ "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0",
+ "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406",
+ "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896",
+ "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643",
+ "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721",
+ "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478",
+ "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724",
+ "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e",
+ "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8",
+ "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96",
+ "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25",
+ "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76",
+ "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2",
+ "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2",
+ "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c",
+ "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a",
+ "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.6.3"
}
}
}
diff --git a/mypy.ini b/mypy.ini
index 295bab4e302..589fbf65139 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -5,7 +5,6 @@
python_version=3.6
platform=linux
-# flake8-mypy expects the two following for sensible formatting
show_column_numbers=True
# show error messages from unrelated files
From c3df2c1a69c687d34f41478235c869d0526a46bd Mon Sep 17 00:00:00 2001
From: nikkie
Date: Mon, 28 Dec 2020 00:18:35 +0900
Subject: [PATCH 099/680] fix format_str() docstring to prevent users from
running into NameError (#1885)
---
src/black/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 48690573810..5f0f89719f9 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -986,7 +986,7 @@ def format_str(src_contents: str, *, mode: Mode) -> FileContent:
allowed. Example:
>>> import black
- >>> print(black.format_str("def f(arg:str='')->None:...", mode=Mode()))
+ >>> print(black.format_str("def f(arg:str='')->None:...", mode=black.Mode()))
def f(arg: str = "") -> None:
...
From e7ddcb8686859cf3033b2a5d1a2934426abdae9d Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Sun, 27 Dec 2020 10:19:08 -0500
Subject: [PATCH 100/680] Fuzz on Python 3.9 too (#1882)
Fuzzing on Python 3.9 used to cause errors but now they have disappeared
on more modern Python 3.9 and Hypothesmith.
---
.github/workflows/fuzz.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index 343eed10df8..0153767509a 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: [3.6, 3.7, 3.8] # Python3.9 should be added after fixing [https://github.com/Zac-HD/hypothesmith/issues/11].
+ python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
From a497570fcb364b40e0d952d3133e8d4f2d329fea Mon Sep 17 00:00:00 2001
From: Richard Si <63936253+ichard26@users.noreply.github.com>
Date: Sun, 27 Dec 2020 21:38:11 -0500
Subject: [PATCH 101/680] Bump mypy to 0.780 in pre-commit config (#1887)
To avoid hitting a mypy bug causes pre-commit to always fail on CPython
3.9. Even though it's still an outdated version, the bug effectively
blocks development on CPython 3.9 so that's why this commit exists
instead of waiting for cooperlees to finish his bump to 0.790 PR.
Also this fixes primer to ensure it always raises CalledProcessError
with an int error code. I stole the patch from cooperlees's mypy bump
PR.
It's funny how mypy 0.790 is already asked for in our
Pipfile.lock file, but oh well mypy is probably more commonly run
through pre-commit than standalone I guess.
Oh and if you're curious why the bug doesn't up on CPython 3.8 or lower:
there was some subscription AST changes in CPython 3.9.
---
.pre-commit-config.yaml | 2 +-
src/black_primer/lib.py | 8 +++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 667b22d6328..4e12e46f8d8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -18,7 +18,7 @@ repos:
additional_dependencies: [flake8-bugbear]
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.770
+ rev: v0.780
hooks:
- id: mypy
exclude: ^docs/conf.py
diff --git a/src/black_primer/lib.py b/src/black_primer/lib.py
index afeb0721cc4..5c5576e1ff3 100644
--- a/src/black_primer/lib.py
+++ b/src/black_primer/lib.py
@@ -59,10 +59,12 @@ async def _gen_check_output(
raise
if process.returncode != 0:
+ returncode = process.returncode
+ if returncode is None:
+ returncode = 69
+
cmd_str = " ".join(cmd)
- raise CalledProcessError(
- process.returncode, cmd_str, output=stdout, stderr=stderr
- )
+ raise CalledProcessError(returncode, cmd_str, output=stdout, stderr=stderr)
return (stdout, stderr)
From e912c7ff54c392e92a765c5eff0bd2ca3bb05b47 Mon Sep 17 00:00:00 2001
From: Bryan Bugyi
Date: Mon, 28 Dec 2020 15:30:23 -0500
Subject: [PATCH 102/680] Fix INTERNAL ERROR caused by removing parens from
pointless string (#1888)
Fixes #1846.
---
src/black/__init__.py | 11 ++++++-
tests/data/long_strings__regression.py | 42 ++++++++++++++++++++++++++
2 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/src/black/__init__.py b/src/black/__init__.py
index 5f0f89719f9..c7c5d724a9f 100644
--- a/src/black/__init__.py
+++ b/src/black/__init__.py
@@ -3271,7 +3271,8 @@ class StringParenStripper(StringTransformer):
Requirements:
The line contains a string which is surrounded by parentheses and:
- - The target string is NOT the only argument to a function call).
+ - The target string is NOT the only argument to a function call.
+ - The target string is NOT a "pointless" string.
- If the target string contains a PERCENT, the brackets are not
preceeded or followed by an operator with higher precedence than
PERCENT.
@@ -3295,6 +3296,14 @@ def do_match(self, line: Line) -> TMatchResult:
if leaf.type != token.STRING:
continue
+ # If this is a "pointless" string...
+ if (
+ leaf.parent
+ and leaf.parent.parent
+ and leaf.parent.parent.type == syms.simple_stmt
+ ):
+ continue
+
# Should be preceded by a non-empty LPAR...
if (
not is_valid_index(idx - 1)
diff --git a/tests/data/long_strings__regression.py b/tests/data/long_strings__regression.py
index 7065b2fcef8..2e7f2483b63 100644
--- a/tests/data/long_strings__regression.py
+++ b/tests/data/long_strings__regression.py
@@ -375,6 +375,27 @@ def xxxxxxx_xxxxxx(xxxx):
print(f"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam. {[f'{i}' for i in range(10)]}")
x = f"This is a long string which contains an f-expr that should not split {{{[i for i in range(5)]}}}."
+# The parens should NOT be removed in this case.
+(
+ "my very long string that should get formatted if I'm careful to make sure it goes"
+ " over 88 characters which it has now"
+)
+
+# The parens should NOT be removed in this case.
+(
+ "my very long string that should get formatted if I'm careful to make sure it goes over 88 characters which"
+ " it has now"
+)
+
+# The parens should NOT be removed in this case.
+(
+ "my very long string"
+ " that should get formatted"
+ " if I'm careful to make sure"
+ " it goes over 88 characters which"
+ " it has now"
+)
+
# output
@@ -844,3 +865,24 @@ def xxxxxxx_xxxxxx(xxxx):
"This is a long string which contains an f-expr that should not split"
f" {{{[i for i in range(5)]}}}."
)
+
+# The parens should NOT be removed in this case.
+(
+ "my very long string that should get formatted if I'm careful to make sure it goes"
+ " over 88 characters which it has now"
+)
+
+# The parens should NOT be removed in this case.
+(
+ "my very long string that should get formatted if I'm careful to make sure it goes"
+ " over 88 characters which it has now"
+)
+
+# The parens should NOT be removed in this case.
+(
+ "my very long string"
+ " that should get formatted"
+ " if I'm careful to make sure"
+ " it goes over 88 characters which"
+ " it has now"
+)
From d34eb7fea38384cd1cd029179298116fabdbccd4 Mon Sep 17 00:00:00 2001
From: Hugo van Kemenade
Date: Thu, 31 Dec 2020 19:03:39 +0200
Subject: [PATCH 103/680] As long as it's black (#1893)
Make background transparent for dark mode
---
docs/_static/logo2-readme.png | Bin 80754 -> 99591 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/docs/_static/logo2-readme.png b/docs/_static/logo2-readme.png
index ec5dafb64958d222ecbe9a29e9e13963acc6d73d..7704ec01ed98a803f59465020422ff6cd24c0958 100644
GIT binary patch
literal 99591
zcmeFYj0ef?3thkKk(7k)Wwj(
z!`8;mncqW*@?U501E2pn45Fm?*C{U6LX?_viWFk@PNo!GtemWDl)`8f6cmC^CT9G~
zU%vjIp99~7C@oxE9QZ*XcXxMIcMev2Cvy-xA0Hox?IY;pM;71=7H3a87efygJ7=nY
zU*td6`C{s9>}2WSVrg$j@y~S)jqF`rgeWQhxzT@L{~o7{rP=@PWas>UrUgt8^v{1l
z?5u2{|6UvTso+0H`4yZjO@TZAbA4fU!GE3k|2p=6?js2L=jQ*{VE#SRe;oy;DvTxw
z`tPy{qseyDX+3)$q$~ABRMlhQaM?E0)ZF*k-*k)L>Wg_sWtGa(RFVx3lLWYxwr?NI
z8Z@=(3lk|-wJjzlm3Ko7{>t?FFa%TLPArey9dx)Kk1Wlnk(%$Au9_tr9WH124vx{b
z3AANk<0Pb}&UoAk?kt@-A+n;vqPW63$?y3PJHk7eJD5u%Pdg*y_Za`$)Bpd^|A$sU
zB#&B3pJ4sctpEQ@?{BcO
z1>BE~4Ifp^L75MAY!BtwkT)k+h7TfH0!+#01m9<&@eKmERm>|9z|d7Q$AVBGl%
z{*vfvUF~rA(|u>Z)O=!{JBsi+9i&^uhijRSr`&^?N-X^h;YJ$OFEgcDn8vic#lvs-LIW!628+t96I@Xzh((?UgzzR
zs^jBh*h8EInWuAejC*8cq@>?-VD$HFSq0Mv#>!jky%Fxhe}5t)I-VZyL~x7zYg<}w
zV#>?QpK3fDY8tAm9WH(bAX}amF*zpE1%_MJN#brT?rd-Cvu*F}3|qKTjx=o#-{?q`
zr`k904_B(MI#y=;Mi^i5`j;PMZuz*k-K7kF*Pv;cO|m$UZygPG?(-&E?^|DA-ya$p
z(ug4!c?<@I>qGkswa2;nU~@15F}J+@Fdu}91=FuZ2S(~K8aAWMepA!X;A$RvmBsJ6
zkA+1pd=F=0VghUH>u1|FXk+nYa14&fhJK~8{FQ21FdUU;Up^K4p4EA;wlTNNc#>s>j#CR~`1sBtx+hO3uXDvhj>E=J{pB;S
z=K$*wuI}h~fF8iBo^*Y-y@Y}B{0;wIn%SDaY}d*4s|tAeySTbu*e9o?AhIA3FeD?R
zjgZOLjEF<3Lmx{?X1@VD%8Q0CNeyK*cu!ZKfh&W?b!aGe$f0%HX569Oj2#111!GJZ
zW~^|5wZiOI&D<~4>z?lGgznt1o-Z8;niAg%3kaR`ARZn
zVMXN1RtksRgVx2^@1{pvjwa#p5gFU7mAjGqP&mh#Sag=>+oQBJ%lr3;tIGUGN@(ST
zluehWlBYg<-EqM&ePS=5r`9@$D_dnI+5CjhKu8C2vtYWKc=gH7gn~o
z;n0~trvUQ@mY-wwew(Lr$*MuVck{EXtem_&t;z@5V%jpA1i-*)E#2&VI!%ESxQF4$~xP(uih=mO)II6iZvwmf)u!Qs2=s
z4R75J3Ewz5j*a=LB<-1)t`3%-a`2B^=1tCqt?Ze&>FM=rWZMc}O}kv`7BgMdr+X|N
z=(`DZj@2}*+^oPRu11R5#`UCJ@O&$IxdmYEPkX@Sd`pFkJ&qjpI`mhwA&{r5nVA`d
zY+>J9=ryTg%|Gz6>e)%~9J~tbFVVLA_!lt3PG)9il4!h7?1UWr{K=6~QFwzO!`*4U5WLUyh!^@($GFs-oBJ1WxJxrC9h!3+VQ;X;xIccQKZY(NBg2wpyx4I00V`9M%;#^{W^M?XG*rJMUAN6m
z==Iel9e!I|+h9*FOO2e4j*i~ZaTGA_^Ujakpw@BlNOnh1S@LB-P0bweW*N5o6>s6)(Q0xVi%pY
zYDmT+5+<7*EU@cG)wKLG8aO!1akSx#Ny4VV>MhU;i*))i6}t>(#x`17+Nspc
z-IZiD#-zcxlasKIaqkv{1O+oO-n=Pf|M)RpjX{ku7?XrImevxJTsZTq>=(UehYu}Y
z7t&5!r(2stNyC
zI4Hl)D&y1V$HmQ`oXvDzT8@s64%P>QpMKq(?fBF9HQfgTx%yiQJ#5O{-Tmt51ZDQ0
ztY^cWk+L3hI`NlE@DY2wJoc1Yk*}SZSesioj;6M+|IE^KR~PJJFEbv8)s(S|Q7N-C
zoa?rCBM=6?cRnr4j+>~-%cAP(v0SIJcP27~q|$3?YD!YkDi{S%%-5MG9335Pu8+x|
zUgIbfNR4dogCMwBP8ae+v&w7xdG9SVI-@m>)}69rm!dUujRH?Euc8eFl+E0H=UN
zGKCYaZ5EE)oX&SAOaOf1YgaSGB$r(@&MAZa#y#6TTaolg@Ty+h&ZuHn1}Do;g>*7)
z)cw}JVRie#p?#8|+_&EPDLZwS%q)$re#i<|0qOH4j_PYDEG(?Dv9t4ApDdI?M9xG#
zz5`a~Q(A?WUfZ(Y+2!S>J(-Z#xpSquHP_=Oav~!6m#C;y)Qa%xmLbLJ$6a#TS-g!b
z-@dym@&i7EhofxTtAVCoYH^#zpFa&g@^Y$Dvl?xwJ}RGWZEe*nGpb+zdZ9*=?*6dQ
zu&EuInmU9-FS7R~ntDz~guBnZ^KN;wFX~|Ltleo#+G+bd?Qca)(YT=cFc1w1!2@LF
zEk>WMEiBj$rYsAW4L*_-v3IE*Rk6RGSq5M2_iQxnUTx_4y8E<0-LbF0`}^x2`ufAE
zbr0cUL1KHCKt{0wQUydIi~j;JHG$6EEN#G-?m1~H?9fH7SMmLJpSrrcyE~?)8u}%=
zvF^K4F^Jv|#H-gh(Nw*Y^4E+>#*@wZqv-i-^QWXPLAliFQGOCyYWuAvwMb)ahcXS0
z2pwk4kchhnHSw(bsxR^&%=fP697Z|8#>10R0fo{W92^YpO&xItyxj<+%clQP^T4Sg
z6^AQEWK5&s1JNXbHFx-O&%g4ez0BZc=2Qsp6YjM4pY8v_|9S$7_%u-^;M+b~HRUR_
z8dFgf<8pf|>5gK+5An4>eQ1q7QhnWL+auVhA1s&E1Jm?i
z0j3#)nCEjloHN(1ob|X4xc5=&;AES-2ofUxp@!K@L`cZG9m3RevVQC0u_F1@=;6ap
zTAJ<5OFYY?jd?*tLL%*-+8-Jg^d30~*)n5Y_=IN7u|(5HV{&h*c*4i-(1*gI=o(!;x`uV`DsEYc3u}vUY_J96lPyGIti-BEL&xhdRkE
zFTXfAa6``6m9!p=hwilzg4kL#z<_bb+Z&HjsIZ<)T<
z`u>=@*^fDHf5X1+Q^6UAm^bZOVHC9Ztjq7YPyyqa=h9Th&5gD6af23HtvLE`_ZSnpJ
zlLXUOw#0sa5c3Y6(OuF4MxUALPrCm>P>V;G_TOdVej@?nPx^D*hJYRY!xYH)oHkWP
z&5ZdUJ)(PZVJA`cs`2VUmcPDCuC0ACUnuP!+CfF-$D>QKEMB0hnjMN6XHq$}^o!O^
zd&g9|jcm6HxUxEaotovFdKBc5!+|8K-9LA2KrnRh^x82;r$I4grT0hl{7O
z$Ng-~Vucm{Rlg_y@CY?WLAwOCv+Xp28__)^cc@qhrqXY_UmtHZVwP12zDLVrW`((Z
z+-IA)NkgN5^5ec`PWY6`bYnh^*M%f*b39`qrc9`)?nPU2d0?{=4+
zLIxNi2Jx!bW>QkG331G(7>nbSO^?zWa3T94Lf#cw_m3xSZZ~4{*oyaeh)%jKc}C=u
z0MZLZiHaY@A5B8{S326f_DhSW6BLB^EccF0Z*|MO_2D-}(LlUZ1i`K;8$c+-RTb3m
zvE?>*xVHWh>dBe6oqKZ~<;rXTGO7jq*Z#iM#f%16>6vFFO_ifjbpI(ekGAuD5?zeQ
zUDMW>=CN0QYYN+Ol=bD3qW0GpXBAp?>J@rzm#a~3q8ODgKW3o)cM$?|#=aShX7Jd<
z%;sxNk&DMuVZ`W@9!k1+DY!Uszsl(PM^?9zPcEG9G>=cBO}EAz0~-|0YRl-!G{nWl
z3)lkIk)K!ZjAaz7S85l!9WP0JM+r2CgxT5Cl{G)VX*3wd`{?%5c<2Y_CcH_NlS@ofE*nlj%GSV1UXo{sU&UPCwsP
zH(H|8*=j?vA*R!%W=G?hGBqaGo;N%NEC(!2OP?>EpIYz!&b2z6tNC*1F|(1*Wjzfr
zCWYpwOicLKTm5u}zeY_eWR={vk1rl%gAVOdA_Y=x-Lr
z+I4<0O4`TE%D#xsNzq|Q)pUevcqVO&gIVemCzN
zTPG){>D!BgL0hbX^^<0vecw%&wv50WWF=G{?O(In)dtUd1A68*PF@DSFHoQNu+E%p
zZs@a46VCQ*PoW(_795kgVAof-8hvf*w#4d_UtJw5$*G(1(RAoaCYjkYzo@84h}%C_
zOY>j8_-~h#`X&%lQ_@jUR<_jhtx{N%eH5_js__4r?_4l0aN
zupd`4GSap@v>!Kk{87=hrotsF*^T=!8f0{L#lK7{L@koVzQkTxx#zL02
za;^gw7po0>2ILR`;H33FpOBa_P0=f2ZP|QBnUljAI20N+n2gTa{Owh;*=YKNebXPW
zsUkV4q!&G&wm)`qth#v|UJN1|mAD?vO3k7ruj?OZAM?!)$q##u^DB6DCOx!g3l9x?
z{@}8l{KcwJXZC|jT!>SKktut_OG+bzqGb#~vy{WJuoZj@;&!Ypq6PF{lucAi#HbkW
z4X);G7aQlH5sD&Ndv@jZRO|MgN}ZLZH(<(!YGgXxXwJtsOce2@zm?^aQccvgO8eit
z_jyT$xvg@U$I2L&-e8QQaAIkq4L2KxLb%MwGP_xH8kek1>e5Dj|Ic#&_5+;T4k)Cx
zmX(!NgolTNwF0`()i7UWO|JJgh!_)wLMN3udAzg92_#v@US6e&1))f?%8fd*(*|bu|?{+Bp>s4Dvi{3`g<8uZi)3=h0
zdXC?{DZrl=e&(bbSk
zuju3W$6F{~5eFo$x7zGZGqf=Vj`W-zH?B1z<@Oce6SwgMz6#RDw8et=%4hMHHm!Kc
z_+I}BQ6x|T2QbmEtBUrbe76b{mIsXtagWZWM>ThfQQ8OTb+YC_1S~M3Y#WnpD9?R{pOg7eIAG(rI%$BIPh2
z8wojm{v9ur+F4Xvb`vkH5cVlE)8j2M?zo`?9@*?QjjD-Atjbp8Slo;r6*z5
z+cS~*-~FVwWa%}Wv?j=dx+qFlU*4O3Qhrb(CAxrD>hMrzE;QQ89Bt2FF3HO}I1;bx
z+8cKqIsW20$0`Vb1@4GCb#c{O!=anmuf+H|A8)P0HC%x>UjK0BQiAROctb#$+ytL4
zDU?Z$UBGzZ#+hDxdU{#{#?#tZ&ZNrZdwv$oSA}aiu6Y%{eo4}ECKtyA|Ev4G(d`w2
z*G+e+!{_Fd!2Nj1vnE#S|K5?nngMHNJ-vsYk!c*oy-_F>1JtyYByb@RlcIQVx;Hs~
z+O7TY7M27vZ<^|-1%{@ScA`W#t9q}k^~sJpm$@ac`u
zrM!m**q5s%GQh(i@U=v9{h##X_`YK+SzWfUE;NRA^waJmefNu*BefJ(f8~eU3z~*8
zV*7T>IbvMogQKQZb*=n5RX5&@{baq$C1?hT7zX3}jBoW)K_*kL`xh2O9v(jDi{Q@Q
zVx);@Dv_6N=^S&S>)y~ah8HtuEGmMdhtosa$_vpVH@}n|t$HJbE@<~xt&r;nhe1!`
zLic}r3Mv9Ka##FzQ7v}7(gliE9$U+)%IiN_eIPw1Cs8E#J(`pO(U_8ia$(ym3I`kp
z%TO{WP@K_atbM+Hg&o7|qL=_glt@X-SecE}zKOYh670I|l;+v-lGT<)82wHAz)b(O
zkg_SErLmnTB%R-M=no!~db{nB6}?IFzY*6zQU1RCxinJ`|0s~8iSTzPmUOi>Y0K4&
z$QViOFXTrq1VOwtno(c#8R6G2arNd*{|?WC8DSw}q2JDrf<5O~%l+x_j4NEv
zoH#J(YHv#74H0Lks)wD4*Lc*QE8fF<6z6X&wAR-Zh?rF
zXcPv&Vg)MS2lkfrqXqn8ufPRKdy&j1*Ez7-D$I8#2urvG+Ij4i-yxu!(PY26vddG6
zV-z)0{5bi0MeS_?etGj+`YQtQbI`E{MHla`AfhDHK41>Xu-;`)+3Bo2YMLJ}oF%sF
zJ(!|?dmN%r!1ao5MBvr8aTD&|>9RrvlLhGR>P^An2#vDK?fG7aMwR}UVd>f`_>R8<>!=?=7%+JIk_kxJy7QTS$1Dl!xH@atun`O1~(L0
zedwF)nx+Iv6Pk&-P{g<2K@}TW<{GMFUC0zaw;Z#~n_!dK-*B(Fu*$xudqg6WBp;dWS{(P7VR_uE3pYCEoGQRO
z8=|`!)(l@I+v|-#2)%V>7Lns4F%C9zB0_O%+v{U>+=#3(Y4jA1ii#1wRqO!v7Pl!v
z)mO;7%v-=+os{22)tvZ22J0Po+)2q~673;oBkvE-mCA7wMXLiAv|^6$ZNO%YGo>5u
zQmrX^Z#BQ3V}nPNSTpL=c#)jdaeE7zKw6;1(AXGl?PA4vs`+9Ohx21N&0}<0nL;yt
z5OYt%U;-1SI*a~27zmn{RR6bmXFh+=ZuBJx$Q@u6Ev>XSTS)j0Ey)XL5tjO@kkCDJ
zuL8o?5~|eia#s^*jBOHr#h@?IqE*|WysQl-K+jgM@U>IOm#ERG(pwgy0wBos=cL5;hQU~e^
zG$WMgHkwiy*@i@UVI^V*?}FNP*+m-A*(Grb
z)6Fg3>KVFI&?L3uxHt)|?;YPaK}i;D>Ea$&g4Gwf!Polb8dd63Me;PCIp~bW
z=t^)Brk%El5tpOCPe*QsRQEPgKnM6Y0U0#={O
zii$&oqW2d{LxG&y#XQdyxC$YN+S!Sc5f^RZGtQ>0scE5hlO0QM)Mt!L6;cCIyKfF@
z+U*|fEUKRHPV1_UWK{;uIkC~eUg3dqbWnRNu9{*kTU;TNz5{0Wvl?hM6FG+H0xHI}
zg7)L{uD_6>!DUf2P
zwxKwsv^=_ag}Urz_=wN+%1*wz-&2d%&|fVU5wq3bx1r+UY1An#2XqPMevla4pgHC>
zfW@heU+fp!wHhR6
z4mHEe8vw9aZ#CGeXz5w_{4@IT4~iK8v5MCWDDrd}geF=GqR!~|Ped-f{Gv`Tkg;72
z)Dc1KutgNs*;9=#|AXA}?_dv?i)LzSYW~@5hTZDgiNT)>4AxMb?4YGE{tpq`%0!Y_
zT@`Vc1yVQ+*ZjEhidtD#7cbp}02qy2s)1dF22~CP{R*h7F+jBpc3#25ME5Mw*l*K)
za`=@;R`b18cqwU`!QrL{(?RhU+x$n3!&=w6;b!fTU*z0zX6qo7u6HQFnM&$g3)LU=DtpOXR6y0z&Ar)e;(Z@f2CCv*4P
zFY>`841)DTbC#agysjoB$V=h+zH&l$haCGrrMURoR#De+gn5N62WBXj&e`BD`HT0@
z&r{j$b+RRc8e&S-2uc5&)2)K|Q_3v)qTvscoU!Y}l?H!Hup*^42kiuoQd=wY!i*Ca
z`#)lzcEw7?2XJO^n9IA#$;o*;vj4A|5Xk=wUjwM+lNKpJ^ndhfvb`?|CT&|OXw=2a
z&A-rvI)jW_2#soDdPycRULd<*!hf0$C*xS6^iGw&dN#UWhJf8gCw`z`bRHKf20E%=
zM5nb}1ABP`_3G{tJEn9tXdBD%Ua?^7DjERrND1H{au4C&UoVv%$WFY(
z=D*@uDN6|_BDg&c74-XLW+*sQXJjv06Usi7FM$P6PkJtZi!F*@t3DqZ6Y{?NhcT5}
zPBE5dra}w~UN-DNNABdPbM@q!#7A;JM2y_tpN(HtX*ZOub_Jo1Cw21v53M<5eriuf
zLB~Ij=-)tn@Ph!g3pWhL{_Y_c^2k()gQZ7Xh{U0L%{981>DCn!WL&{280ZGX-y*%3
z1llt4VYf)D0Rj|m%sqB?6lvAO6o_+F@z(?N*qRgtOLpI6H4Wod8f|ry%|zuUpMoFv
zAFfu5Ngs2lH&8(3Jk55p5p%9TJk(!b6v98iy?H;oru=^Sr&=9u7;_ocyzt@Biulow
z`b6jLLq^!%+>P8mmbzy<)Orns6BTeAa${)SLDF!OW33Rgkk*vH0680b8f#r
z_qCz@_VZY;&5efO7ZbF(o3?yki@jm)O`MvxgV3+InegW5W!$&l$-c~=?@i5I9xYlX
zd{0<+y7nL3>zZ)@5NE*%<>VMWc^}uS5)x}c0aw!5K6ZFq+i}lp*XUbhz8{=%kATAC
z$-6(Al!q8%PvIZ@pCF7BK!H+ip$*WOQ6O=p2cx9hS4ioa5~H)dUMt2JWDGZ5AnKBrhqE_f$|^>vv5ba3Ge^fQ?GQepj;NF9-|<(1*RAibPjLH(B7T=y
zPZA#whhUMbXK>r*y&MqVNb)u4B@Cat1gfE!Cd>%rrtAlJ;*RSvVX%LTyHTU
zlQ;A~T!!=})RxQt@qWeA(-XDkTX$Zho>_GX+T;YMQ*lkq7{`YrLRxS&wwTu(JJt@+
zl~ylTucRc%N|4hIs9tDuulk4|u-O=rDx~P)FhWKz=n&n@-d__~(;9B9+eNw$Z97&T
zzw}BTV2lMD*C6NA>~a$96io)cVywjcT+(Skf0M2r<0E2`c2==byIJtZ3+_c*L)y;r
z{=C^t4?ZRBrO}Bm%bM{RogI>stpA%sq3f4$K3rVcGjlZ&%mNmzatSKkAHT3;YZ;mU
zfbfzY^EToiyx4~J`#??FT})hmpS$NMZyCA
zE^Ev~bOP+|Cg;HWd8MFRi}I$pf4?lq;HtskCqwbFM8oabkn<w9e=4QLoGHQL!
z@{lu&h3rK>J!Cjer|p-z>o}d3&md2--H2>;Of4B^_g~#iC+#ZU{o4)mPLHgZ&FJ0Z2r}Z0u)%%n#_JVZ;yi>ZMb*io@W%jpOwKV7X
z1vfuaYBPnaxTh!2Ew|(-8rHgP3ixd19A=vAMJ{##N=hl+9^oYe03abAsbQj
z^8l?h(gM@(AzkmWF>2Y17U!1rwDe}OmXnsXpf(zdNW{q8>zuR!SB!}_kb3IIQY%oiND{H
ztvsc=)`vGIjf-|TK-+-b7Bed8y@T1?`uMT(B@R@~N*5O;EHJ+SqMc#AOhk_cEf{G6
zW^uAkPb0&B&sf#tQjy?ef-RFIyXwAE{;~g1;;Sn`S`Lk5asALAd6By-T}T()
z*K8|PCg%IzWqPaQwwgP8(^)gWhHUs(PF1!AlPPaz4o`B%XGo%sujU^;Tb|77(bT7E
zIY}Mmm&&kHSeFXgsxo=cP->cQc|8`Ks}<+s#|3{J^q}X>;&h0y?+l(+?$Uk6&X38U;#nA
z_8Cwyr`Fae_(YsK@e7KWF|JCavq6#L4t5rr)g5hIXWuW%G96$}))6WaD>O6|nlUSllJ?dFt!u>@3W+Ji!zDXi6B<)_j!3oN
z57AnI6&fouc4}nLY>S~UI92-8cPp6M;VLerog1!tPvKcbZS5L!DyJDB2E9P<$`V0PFuDuQBhRSvH@raNHqGX-C$r6q9-W>6#Sc;>
z>-q+`eB!P2h`xQLIhyVoRier$8g82Nz!7|v`n3=`dlfs`wAqEjrBJvk^PBvtY8b9!@^)&A#${g|
zky(l}H71*N;AReMs{J^(53rIwu-GAg>A@O_Y0$rWN5P>UAjG=;ha^qfwzmjj>)lw0(<4{!n
z_HOOxxAzV&>95b`jAOz`0U=g$m5<>}cR#tmY&mZA^Sgf(PJ1G+q|?CYzl2&&_%p|Y
zFoiWUU1zn7wnx(P+S)Qmw9?3Evh)WbL&e3yXI627f;YM?wsa5g&X3vjJxf%_9Fhnhr9ck5P=9z*iZ$SsR{Uoe^U|QXH
zond51&8bWKYkMoeaLZwA
z8h?nud|z7sApEWniFWirfjgL~UXiVsVi|iJ`@IC($9;Xbc~jhx?xE7Yxy)B5FvHWH#=^)to+UTclZLL*nKYw)JMW}
zeaQQmFyJik9dAOe3gNLzVRi;ebiz_T%FZ>jP@n@u?yGDVFrO<*E)3c>(Luk!Nus?eHJ#M8
zmOU#mnQQfhyX^?5PI*dZ6Z22ecR8hqyqIg(D#oqxFp&j2qnAH#O+@;b*Gd!JRNUxZ5V@xap)
z8LpwN(31132%zrpaNdm$8aWA>Pw{6U{7=B|gK#-q+#XaxybdaxU;=Bro7FGI)E!Kac5f(EF8jTHdWO6
z!c6f_AZEl?B0q#$;$VOasH>$BJF%UET~$oLTsN`{S(g>*K6EDDDyy__pw|1~%i_7n
zOV(ZI2;qKJJmLg#Uy02*wIBjDyVfORuMuP=1Fl%5zwylB}_HvS-g-R7_n#q)SR!ZBXoakb*@-`|<}pKW`&@xH#fR>@l(P^DC+CEtGSnI-V%VI=%
zLPBDT2EG=MEZ2MOIk~A+p~j!a?7lv>J}16RPqb{s7&KIvYbl=@W`S1ldsd7M_bBvpuJP$xbgCKVO6u}mEY6DRia-g
z`?&C(jT1)AI7aj<`N66HDf73_D}ylkLoYjT9}pm1y6o^)OK9y_2klsbOaq&x(iGlHc9Ymtx%{ap!9TbVN9qEyM`c&$vh<
zlYGB9K3aGG;qnhy{l5v3y(pkO1?=b9U28i0xY+1D_`Iw0B#~@gfLi+m!I!iK6QdCf
z2_l~D4-a~j?(|p-K;4bdF^;3952#NEIwC5}`t6#TfJmx7Pt-1vc?x+(Ztm^R?wup0
ztGtM!I4Vg@8kG5$RJW$j$sc~1{{+(%@-A>RbR&
zTlqjNx(4#7%iVSnJ=*Amo)kMV`M@#1sFixJ*YkNIbmDH&+T(*BMf>z}Kjg)3)IVAd
zL&>AIc57WZweKgZ&UXq1dIodh6Q;+Uv1$2He+s6k`fW^x8MxOhxbY#yC+n)G=a#2%
z;CZKv>4mF)xP11p)8;ZnK4D{XK0wSlS~jYlIRc<7qYn4iVtEF4&1{ugMS{b)7KfuD
zKM91XPxi~J!tFaB+(rVmG~Y%f)hGyE;1*)yR|M`N5LhnwL$BacX|y{ExcAQk)tVyk
zS#JOAH7Gmy`#>|O0^T%yIdiS2ICgjIw3R*IXjuBiP1kZyXf!8Z6#UAakU$4vyj=D7N7FWB?aBP{Ga19k&=h}63AJ1v(R#pon_$4KQX3cD;daFT0%9TG
zzXTl@+H=b}_~?Q4_7~&L(E|+Tcigh`IEwI7c$_i)hxz==#aaFUmEfm-pw+cYR&bLv
zR5%cj+D!}Apr
zNyFi|`t9lTS#kJf%TYbrr}a3LmDY5LfjjRS`SS1l3J07oa@EgBMvvl?Xg@DLkNG~v+Q
z?`z;zqy#V%j>I!e@=E;m>;C@Q=y<7R9S}wpfiRyMDF
z=mfuI<5QD^@r3$CmKcz2eu}g`^;5R`jpT1b^WnxOLcKA1=hb|l4OiVT**92+*g|F1#rlT6H;g4Oe%j7ji
zXip_QO5ZsG)HHdEtx3GS*AJYuoarYT^Xn4050?RL4KiM&FkSzUo^}$s`O>!$T8bSZQ48;EZ|n3u
z$(BJpIi;C4i@0yLzKD1V@+>xCD0gL77%92jTq?anhYs_#zS@l68FC$jEaF+x}Zv*%HsL<
z-sgSfk7!$}+^#B^dw&&)`Hu||EK}}3&=bcAkhM$
z&!F>=uju@ri0?g-mPG)YYVrZx_U;74`R_~)csqs7eC^gRVtmlb#QcP4z)L4VC27+q
ze%|6c)%&yKYI#kbSGro~2m$U-Au1+*Z>%|=j_>3nn=covdjV!If7eDKj$QtGEp?Dn
zl-Jr}ta@P|=w=bv+S-2MP%n|ZrP9Y;Q1g>94Tje`k=qUzmP92+c_q3^ON*Fp=vqV6
zanq%9D>im$Oa)ytmBYu7w71Tjekly*Ftlr+O@7&`oA#s9qUTn^+%g(Ki>9+dl6RHp
z_vv)`0tOKi5#=RPk<^rSs?}Tcew9ivWe!s2(=)4;R~D|xHWiB6Q6BsabksM&b$3qL
zD;O(co?l%o>PuRGn`W{e#P^ADaB`Ma?P<(pQQp;lc9;4?w@miN)9i0v2Y5?a+TXNS
zSVpb`Wa*QvN~UZ{Z#q?QY7Ofz$SEjg`p>}qFJ;imVvV|KOPoNY%$
zu(nE)xZgXje?z{ELDF(EWej|hWdE{89`FbV{;6CQd@kqB+cLFFW+kkyB0w<7a{E23
zH%||!w&{nP0-34Ec#Ef=`@4rfe_V=Gf;%5SuI&Do(p?kx%~tC8d!CK*h(xccW3RFrtrE-8qgBrI9wxnYXyj4`KRu2F1ZZ(pohn8bv5OZ;S=?*Zs(d*NVz
zx#xq3&Zq3?=9SxRb#?%VYm92IgX3HoV)~52r?l^T-D97)#zTgaG@SHS@3s
zD-tLDnM(5Hux|X=X>I6UwGEN;`;m3dhI9;1UF}aOu4o0>djh|&WaY2qdH=is
zsxjiY%wa+H{71=2y&6jX_@`zQ4TXu{u3TC)YkVfd)kZArCp;#uzmi+Ay^?n=#mx>8
zjd77^OMUyDQT&LCFljnRt*L0|*IPmE>Nn?MN%(NENQ}A;vtF&L%|gAD4m+|*iDMyx54IAzRqT#c5L~2iaW81~?n++b&C9S)%_{HJ;_3`|?wnqu
zx!@c`Kv{yx@)0gV+7rC&56gSCHR1D@b~IHRCzYzgqmGp$!j*r0-rCei6F#Hp~GTIiI
z7H>4Ui2g`AmkglCHT*UWF=6%LkoB&_B
z1!!CJC6_1gth_s)nxdX#u>T!U+wgRJxPYrzp{t`&I0UPTR#e9m;~@T!WAXm|gr+;1
z${s{Q<&e>=Ly_s?~(t
zVXif4O^MwGc|MSi*CQ4M$r*lAiz!Wx{Nd|5WO0Bn1$*d8-Tl(j6NHch2A|rqMnMSG4N!ctm
z%3)w&eA$SXrMl5s@DyDrosRM%8xol;%=6dMC^yDCzs-HM($_xaIK6
z4ikmZUSiewT)6)~OubcDm0`CwD&38AgM@T=8(#i=N#_v1l<-@iiaEujO84;E3t)y+M8^qjz8$V(N81oc=Fj&2_?i(wl2
z+|2xWOX-6@!w`Kg|HWaBZ2yr-3%dPB+*8ect~w?Pab1SjX~X7k+tREAif2hy7?C)4
zSd*Xm*b}RSByOMp0fQf2PDnfynuk9XH(wj4UCh%;B|c+TtC`b1rLD0W4x_-RSCyr_
z_#GP{mE%hPmYHxOWDmhS`qUPR1g`Z*2%&cH88!
zsxvy^XDth@*PFeJ?VWcJ2{Mmp54JGM5CT=ipl2YsqEb2nuc9XczY5@QD*qM
zJ;37N1+=E~0OX?ZqKeAtSAn?lJr=bio$jgL>3P}P?D}IORTsW*mmIuMo!!DGGS?Xv
ziwY&Alk3Bo2tb~bq5Q*@gl>H3GXl(lwDaff{v8nd_NaMTzhsi7j%|URmC9V;0Tx+h
z@sMXaCPpdUPGzyI?vi<6E0R!7<0l^^%lKFLn?dBV1gFd!Jr=tCS2WMmNrK7Upi0`%
zOotyBQ+ez(_l7)|I@PSnQ`S?5iD+ATk$%ArL}xwv9Zw8&qeub1^7uoSBx=!D=8fWp
zCY9Ts*;@r$s?m?@m#D64TNyM@&H-sVUS!|Udrxf38bWRkh|`Sdi7VMKy264R2=308
z1N@2T+GmOBB4C;uSAF;FyK7ArcJ0L0&^q*WlqWGL=Qu{Nekm9izpb44*4^a9hzRt&
zb^%|mBLI->Flm>czrB0D>4f^%XZ90vT0E1Y;KAAy^k9Nl6Arq?V(`8|wCSlFzkdEI
zK=^Svsr~!Kvdh~j@KjHnvR<4o2B=y9>ZWb$;FIrsN|>wf*fiac>uO0nt+ZwVnGui8
zv=v7Jtl>cp9CIK4In^b-GV+d>G1=Ql{9e!5PEj{b3sVjABhrwkx$*KAM136xy5D7t
zVI55pV{^V;+x5-ouA(5r_cyBQxj@%n1HMV$r`r>|JHxk6dhA$&@F2x=#XwcW=Av&E
z^jG9aCs`|PFK263lt|Q@5MeL+?^j98dh@$VsS6oA&p%pj$Q@$+k1<$Z{I_3$>c0|b
z9K%LZI75Z#9plJR?ZceZ6`D!9c_e-MZ`K3REkd6+F|me@r9?4=f_yzCYV}*quvMPqPF^_y$?=Joxn9N+!}
zR;WsrqwV!hWBh>_Zt%pQb+9b>3eT
z8@Bmw-wcY980niRhhU*R)ZZRVg+{LZDqFPef4psSuqF$-+et`b*qF~K9nX)Z)DShU
zXT%FHULPxaeH(c3jc+S|yZ^T*{ac$4dY<(~YY)`&U7>TqCFNI2-FX0VTv8+|3wQ
z6>RGdY*dbtsHpD&?F}|cqAnhjvkrkJ@XdPx3z@PEqxZtuY)R^Q?Pf=C6WMIy&B;6Jhl3rXd$A2HAJH^;ly$H
z&5>pB9;sbNfb2W~0S?IB^xzx*+`w3JT)b5K#3AH{TV25X^;zPbI{~G!RZ*>ILI%K~
zft}l{0`a=rjynauiHsZ4e9I4Z2BhGU!Z%b4p0UMW9z1_XdXVarPIb2w?91!7hKqm;
zmk;U+4vFW8H4q9IUOuOPzN31%%60lPb0tI#5+H-t3dScog*1?+qpQq#WMpr**$wfLQ=CXc%@)MR-kz
zsf?c?E~ETr{fiAQmQ8z<9&W+_-M}^1^%Rdf50Q28cZx&?OJ5tmwg9S}wxwq9)w`!L
zhyOd{Mjf4^SnWqPfFBO0oE{04@pZ_r-VHMX+73pt-98r^8@U#A}a7`OCs@R9DwGAK38aC(s?
zME+ELVk%p|Ah%X0(p7UDH`@L|M@m+nkotpl+J1HTMc#&b99ZK^UyOdMc6jeEs#KFp
zKAaa@)7n2#V`}oJvi3Rj7vkqBM+qXyhz8+Da#Aj;om!nmxwSd{l12CzOv(ww&rX|Bt(r#Oi{aR@#yeJ
z(*=`jYHMq~PQN42*D9+(jz15JJmdeZOt5C|y&v6rV%D!eweR8LjVqvwc%5K~a>)IWctCewo;|Lp5aGg7waL5L%9HLucH&RGZbSB}
zjGUynFDTMghd5Q$Z-PK$Na|YFREG#AO@>(|5(Xwp{7F?W|CRqMhp#lD=nr3%S(Xj^6uu)9KFkXu|W-X<^kREyW$>Cckjd4VYAVE`6V
zu<9Qyx{{DiM=T9v{TC9I!2?KvN`0vR@2&(cO=D454}%ij8XJ;;4HE@o(Vl3~sT11N
z7v>JRKsabruJezbs(XNl!*-4e)+zB`Q~cB|FgUuI6pp@Y>mMLN2LtaT&x&h$Uz3h(
z4DHjw%@^fh<4BaD_SHsPc@IkYJoOV9@;bgpta8B*UTrzy*ZZyG^lz(KB`or*RwozQ
zi^NN?f{i1dP)le&Ei?x02Nte_I(1)*9oky$HoJeAP=4H2W!tyzpmF)DpOu-!Kr_co
zbD-*f6q8HG<&Heymg{WMlHf=$XO0rWV^aT~iliGU{{1<9H<<4HV?Wo2>8OiovCx3z
z5U;GyDQ(Vg!4gZC5PNzW5rj#X
z?Q$th*2K5Js&ks)EUr)5h$(>bsB(zK6zE^6z-AW>Bv*W!1XLZFZUE9d+fwrxQno%2
z4kN;bV99|F)*le!oEWoy*E+u#)i?(DXw`v6>4EYV?LYS$a2{X*$JToy
z&)x$FfAeI0j$g{@TOqX+`(TJ8yf4XHbCBp*a(t%rb}$#?-9zSyH)wjpdqc~km^<%x
zzsEmw?$i%Iy;^T^|IxdfSz*X})H{d?%5L47uL4q=F>WVkgI!5)p^yD?0BD1z8!+U=
z830wkzgBP?;>^y^IoE_CrGBmWbVh_>eDh0PJzC*@g~yzugy_%&x@2+Uhd(OIjAK_=
zqo2sZS}51#4xAZUOS`5EBUkKL6HMoWO*Z_5-rKc&ZCCKCJ8;q7#RGg-
zBCyJf_wPNN3F|pZ)lw_muRa;fxuH47E=G=EU}t=2X!t6U&-+M%6A!ni0!sC|Bq}qI
zw*aqNf_fxN84uh*ap|`0BQw|PW8cjAw`bTl2L)({q+eqYugtr~%Qw1o#vsBtb$Fdi
zNe;{j!j*?9sjYw^^>!Twqf-8_{40sD^oBhb{=TbBb9-o5s^zGZU9s=2(tj_TA6xT9
zFEPy5`i+nbBUBqFJu81KeFFhu%(j)Jb>7p0^>6j`;%P+;4J2L%dC4|B)BO>s1nT3x
zJIRL1PO88mdd+LasYQOg3nlmEYr4;l_-jbOTHa6L4L8cX=%6(aSb^pdu@=%;s!8`r
zRfPAPKb~$1y3dKWDuvUpOS4$JqxS2{pfAv?x%WjEno&qZGI`LiOSJ;9Zl}U`;frpZ
z<5@k24-VBxIJD(}TD=mBTIT0m7htMg2*wBXvo!8g2{c@UGnN|78GIvcdVM_lCvwm>
zHKitm!D_O&Jzf9`7r0{pJ*Nzu0JtCd2(dH{L>O&mI`~tx`IaZ*?DyVdJC)U#+yhH9
z+g}s@ipvIYNf#d32yDO)M9sN;Aa&NGG2px&b8<#I+20Agm{o(KTaC6I-!k)Y|Iqxm
z*|xV!sdK@e{d$9Fzwmn?abHW%_ohjRX6Wfk66}HtSiZWkRFzhqp;WY#>lx!0&u;^=L&nXdD9{S`y?KZ>{R~hZM
z9TpbNJN1$F0Th5y1v7z5YjX`I0;iEk=dya)1<4yL;Cz9IwkcWu{Sv;Dzn%LQEr;+>
zKWPr9uezGCc6OV3WVc52pTbgmzHhxPj2y7nP5K3Qz#y@n@`aO-Y0D#lWVENbo>;Hh
zJSiYkmKi>pSDg|pp%iCU_ytI0&i#}X>Tv!am8(1dYWx!{z@re1e%x4`_+f>C&vkH3
zql=grK4sN3^v`mo)m&D44_`*@q2dD|EM{_^fIs4(-{Tt1^vm1p%YYsfnaw&WUVuDO
zgv6&ypI@Jo4P(n4yWdC16X2+bzPKVsW8~oDIRnHhHQat^@&dpR42e9SOLFAb^f_vw
zw1?exhT}g6n@20TL$3sX3s!a4S6LiC!Bh49q_K0?G(G8?rk7v+rH@*D%|0qa*ZME=
z+O2SmBj@l32@G8U+`UEf?7f<4DZQ7!c_VwwZdcT@jUf#rOEbnsvXM+awprdbl)Gdk
z?h|AfYi-Xvwzojv99Ka`Ef?k}J{pB3x3*78&FAs$yyYJo#a052E5p6qUJ(b#@0nglhVft$M@=m+@b^Ku0pf&04xh2j^#K%^=tNt58I+s
z8O%Af|Bx|F=Vp|b&IaM$`_leCmvgibmTD&;B&CvbOeh}pdmw`0U{^jU5VifsQn6!upTTjPL7w@4)gNQ_`J=a$^8B6
zK6$Obb4<9Wao;WJHWs`o{lUO??G1ma=wcO<$}3eZKig;en<`bG_|u-pEL+EZ@gDYq
zEs7N!vPzaCSM&HA5kl5gizFpYh&khWyhf>xf8g+tU25~*i={b4?s?dG)wWX2YPeR(
zG;J37c|fL_dyv}}V)*euY_ck>)y}+?c&YFUOB@C-K@Y@osaH_`cEQfYjzzhfF_nAM
z+!G$uZi@m@o&OZgA35f*{4ARv;AXB7k9~M9qtPJoFCnt%+zeYy|sANW!d?c^HF7K%C1S
zFp_X4+?21_sN`%Rjw)ndnR;MonS}U^gE0~
zb7m^WJMbEG1AnCsQY1|S>g{>^x)qs=0;B%GZ%}vc0Ud=vRcpo;7n6R5+Z*N&g@nTA
zA)Iyk9SVkibmosLHADXU^CWY858OX`d5${}1oeBLed~67n>60&F+UGTmi@K=yF)g2
zN**-j@A?hS=1M{fHMaO*c1MRM!PELc*Br7KWY8X9`nG6GztQ8VFs33j!F@R0`^&G`
zyBDo)PHf?$x#)J-H?dG4WL=6rx10O?B6&M1GMOITnfE|GQuTTFIS@(6r$YKstL=~$
zRr?nLyFfon(bqMtp1DGjwzfi`|oQ(e2DJ}d;
zkA$3#Ma$sSTEnp0MYfB2tNZAqcY>2671(^*J!(sfbx+QF>Y6TH&d}UvH0V(;aX4r>
zEvPmv+@5dpnRzOL9Tj8V$+S-uRpfz2cQ}Ua@v0;^Y^=^^rm%4@2Jjh{12*Bf3vwBT
z-;$&TmNo3eqY*enwB-Qt=vU>m)w^|^8uI^e$^XHiPJ#fvb>?0qA!n<@IsR?+ANhip
z1mpO98@bm_IKyP)-1Z?MWrX6Mqfa2I^;{v?CzncaLE?nbzb66P2aJ7dh6=O8QW)k4uN)U{N0y3Nby#S)d
zQ?_irySsagSgi>MZw@
zkEKl)yE;rXsU$)M`R4V2r~cos$G_!wb#~Tv!32Ojkl)Kq)d_Lve=Un!=VFa*XoqcnmN6AGt&w+F+^yhy{R=g82ZrwVi`CZZ2C~Up
z34Cwn5TOvG`hbZLR-yV1_>c(B$BYiz$P6*VI({++xW1O;b?e|Bgx1EcBBYC7NvwvT
zDRlf?{j|#QgV6&9qXPD~4exu@l&gkI!Nb3icM)q0lVC3A+l`*4H*0>}5PR0~CUG&p{W%SIzET
zO&0{VoPM0~wXkr_3g04IgP!F-TvX+#H8wgQd_2rktEtn&SJoar%W<-uoEUU2$}gD~
z=@qu0E@{bA|Len&>Ka;y*YnX;g!CULPZAi!(MHEvVim=Q4`D29mpH!7F*
zJE?oF9(81f_cT*iHtOMP2-Q>_T}Xzb$;^5(;yy(H1wT%hT?Y-5S)|&$
zv+;cY86oU|puOX8#*la?)mzj}=ZOoI8hNeBDw@_jT*xm-fdmZUJ(uk)Q@Ei<5xYxQ
z2pWw8!NvCaRoE4`Nq1O#YKKaF=&Bp6-CAq9Qo2B5^C51_C+lmvb5`^9z`?l$9ZPkk
ztgkcKG4IuKkm}_8aF!G`;_OY|^Ksd@5huDMB8YN-YW_5E@BDh6fIKL>#eZ0!+Dj*n
z&QbC=fhBqHO5zJINOLCYOYvD){+Fe#&$e@aV4~zbEq-dnvE(iGQsy0ix%ub@C}%95
zlnU)?oZ>hJ3RH7i28#C{wb4WbatCm(i>pSnnS(B6v3!5QVK)l{(X)nk2)_X1^TP`)
zsNt<|wWd#7wfF(*>#4=uc4LtY7Ax}c>jN0@AG
zmC8utG|$O$^;7u~lJ|>-;ge%Z=X7d({xYsZxDY0u6L(M5%G+q=`J}ZnV*vccaBeA$
zKgO~8D2o;Xym3)kQR{1KSyFw7H|P~CaNy>#Tndo-i5w;jpEJe$q2Z8m=|BG*_PA-{
zF4A%c_D3`fVu_6dp&7slw`CI)HsA5{1}xjR&5SWLbF`%agb$F96Ln)K*Ivo8b%
zS~ikEyt#5eNK{Uici(J0hg6nVRGta`)Mao3-^z*+j~4MFup)=>yVe9ipxGKYcEIJf
zunnuSGCt?qlXIBSvu{TcaSz(0PkllS+J||(fw0(!GomdWpPph>XBGUtMZmh@zZ^e0
z4~|89%mAHz1B%B
z+OEG9E$g@soknlyV7J1*ep5I_x@_akZeE3T3pqVdnjTwqQCz!4K0SzzOY=^
zOx{~1SbRRJe8tK*J4u{lwpHL|9Pe`prR-U~9}@8K+iz6vL_P??yp}|AWm^@UA2i@q&nPOj?ANK&_@|iah
z{g-~%Q#ccZ{?_&CttoxLL!4Dx%h09f=Jn}}H}MbYKm%3)0u1_%h>dY;WY>32-)BA(
zu1h@Ez%b$M`JW(%`ML6U;++QP@EbN>tMl=TO{n}yMhOkQpy~DqWEn#5o6tJK)L&%w
zy?)!T625dy)#LcxR&z%LSWPLd{sowkTK}3t*7gjU!Y5MD%}-JS##@Qw*qaz_AfZE2
z9ijzD3$Hri|1zaHb|U3sq`5y%0tl_&{0^*e)U`%FSG%$bEueV5VG;~qo<+Goq@t5h~(uv&C1FShLLlAWe
zD(w!`=}D~q*#ViWa4MpgC%jv+N1IM7SK8HeAgTzJ7(yM}#=`i>^brm@leR-KmnS*p
z01z@&gBr8sWRg7KRC6HyX6sw(b
z=g)lwQQV$+P`h;NPDNO)!9>(>sKoz7^X$9h=V;qLHpX$sRCMxQUV2SDePu@?ZepQ?Ke
z;O#0xB6g057wt#K#h4CoglHd{GC;upCw|fC37}fV7h8St&)of0Jy%tfd0DCsA>TIk
zj1nSG5NOc7
zV%O@_`D@?@;|ki?yx+N{?=sZS7jq~nG5A0;(9ZnruTes4KdL6Sum5
z1LPhj1l*GUbq3mlfpEB(Lxaj~1jX_Js(BeEcbn@v;f7MThd1_y5OD+u8z8JNuFKIU
zy1(Cw8IU3!nD=MfE+>;aZ)`N0Wx~b_k(MdZj5kVuGb}?nyTZF)Zm9`#w#@}q*?@#MZm-rdVRbXw4X`{|we<{w?x}TBsUR
zAan)g?K#7b7W{<-GLs_W3f4%z3TJV)MLI00@~+D`aJCk&M|I9OmMyi2)bc=w7b4=3
zdvFDM+Bi6hW`ZEDne<2F&(f_W9p{|^eI$5wfL^ehnjOiI-W|#OGey&lktpmFQ~x&^
z_amfUNqzp#b3
zUX#B61MLI5A`B$j#;p~mENjoVZvrT~i->ahS#{&Kk9kJuoKwS>N7sK6q(Y02|2a3l
zSL1VYyf>9VfS571nZGq_y@Zv3tYUr>v0LQ_e%f`!PzAvs2u7jeGy}nN?<2L2O`zZ-
zR12c0nud(AS#v`rGc1C<+-4fFg>d}yr$$3k+mPT>0Q+j+>8GB{_4AFcSxF3GGaytc
zhRN5cT*8B9WDB+%>^(y+cxzYGGJZ;!Lqeojz|4;Lx_t%SBy`;F$5P|HRN6U#!+xA<
z7(*4d0lho$G;BLLWIi;X{~nl$NX|0xnf@)ye%U5L03da;4s=d
zpF4Bl@yZ{8i~>b>drzK9@bYN)9b{*TKp;b6ccc_5I;P)K>8lX5t^5H;julU8ennEk
z+m8veUgau*Ud0(LyNuMp*=Shjy%}5D(;`FEk?qE?9Q8q4^+NTmzVa;i=8Aom;A=@3
zAqm+SGNn}{E-rzS&{P1r9~|7RtZgkrSu_q=b@&OR_vX_8G}T)(k<$PBU}uVTIN$*R
zsv}qO*(WBkib)$UT{#CNN_$}g-0rRaAmS3hv7g5S?+xHUvw6uN8`5vfPG;=wA-3!H
z2v5sk&M|6`3Y#Vnu`S`+(M7LV%@c5
z)V4JKyiyqO#I+7tl+@gn)S!@i5CHmGU%AVlHKXYY&HVR`<~}=v@97HiuYF~|jyM`>
z{1N)~!z>^%Lv8^n$$^JAi7$nFWdDf~CWr9umqlfT{imbE12nKsaVdLXGe;4_wUS1*
zp^`Cwz8u+b*e$jIwbNCQa{2CT3Q3Zv~6m%>w4?UMs6u$*OrkslZyK`pvodG
zwP5+L^WieSwkAq$m>Q8sl|u7PQy^Vhp(jO3OCJWqz@t6vJJQ{WUrRbc_N3=LMK@YQ
zu@wO+u|#MO)TL2JYMA0Kqej^&Jq^Ef|JnJA{LnhY313&czF4oz5M%Q8Mke{??1ESUmP;nYT;Z5
z(Gi(kN`0K0^
zd))Sf)!~67%F}k@-D4T`JQEL=*fKpf)G-Ea_rMNuHd+23+~<KO&NeKF114LNq2yF+hKLxjdL_YOGC#rf4kNxHz5}2JmnGS=?r93~U??s5Q
z5IGil8J_A6n^^@>kYjC{;d*M#MP>YBME$fl3IC^O_@7#-&2zr#n}eW)5VEH~6YbAE
z`LI(dUDo|>>H~nel505+l|}DFGoChXvHf%RzOxH&(Y#9zYAZGYFT96}AO4IPAv#tY
zeJ;?Q$^ko40yr>A)$hb1MSyrbtlIySUTz728dU;rqefD=ltj`fWg4rvZTJQ6JZqR-
zoVB^^w0;XcM$>b-!W7}+nMl|M*IK=SX`{$Mvtc-4WU^fd216LDMO(3!*{({n45TD9
z{yR94QOi^QNoVVQ^Tqh6TAauS?cM#c9p@m?Kq$FZA@6%fTfkdQqGXOV79f;(E3POg
zrLCxqo>ih9H-z1h{SyRE+2o-mY}!EtzVmTzYQ&Y*elO~40hSDroCj;*oaSAc80Y`0?&@3qsu9?+x4!pjp
ziC7B%c@N6)A0HogDZdnqub#4ChMUE%lMq(Z^-s%H@>R_*HrQGoO1}Z9sd6h5_1t^g
zw0E8A2iH?7bcX5dl(Lh{m}sa;Qe2Dk##29flFuP(@@h$MsF*i7>6sffb07sE_nl4n
zmg?+Ha9Ay|kg
zR1TB`#W|9r1=xLC=%=FG?Z&*NKpVqVvqYYE1F$%?%0d@XRMf8Gh%xZTz1r?A!vS20
zQr=kO$s-oU9~6>x|Jm^~w>tuwsfH61H3ULF>(yl_Z8p0}%H87o%}VP3B-fUxEs`kVjeF1?7gR?|isbP=^Mi5*
zTpyT$GG^1`!&GpV*C*AxrphnKX^e>kzCCl)@l%d^(gNiP`zzyE8p(5`YD^9RFPiRX
zd7PFITY$`JbEFJrGcvyV@mIaze=7PC)1NTq1xthY1WxSw}&k
zN)tA0T=3SXj#e_z_kw4QQ;!dO8IoMLH#hu(Wav+%7q8iZ4810*z9SURhZH^`A^{i3
z@?i&~k=EacVcYL{Mv`D`nzJL?|HUJY+c{o#^aqQ5m2wU;XdTa25qU6chi4+_C})#?
z>zrSROe2E%X_(Vxr90YnY5pu>su{8*
z9@DKf`*8y#RE;MFx%yuKYEEZx^Say2rmuhKhWTdhkM4aflO+qblky2RSI8OVqv@hm
zvZ@uUHe5;i68I+^4U24R5~XB_Zb^yD^6TtMqYhW6^Y=9@uI%F3hLTx8VPZ-OycO1g
zFA?_If&6oXIw0OZ&)S6gUp5t0DcYs
zV(@?XM{Sc=fz1%_yVFr7Sz{jpAereLTmhG+K|`GLkJ4NON^{JleNYV+XPi>alOJA~
z6QQV|^~55?dkr$bQhy4ed9fsb65dLdHRqlu!*W2aTQL?uRTh;_q};A5G7J*n%zQF&
zvlk1cO%kW3Af9}RvUmtxPB4e&lJKEbu9J5|u!`?%_^D`dm@1;-8`n>OWGB&_U#
z<5f+!dk${T^}@l!5^#;WfQRc6r}3kSrx#Ow2gQ=
zM#3LnoKBEVFFY;=Zpq_Eg58&ooWW6tmg(g2^f7ZjSR6S|)xqXNysHDfKIKdXngye`
zhT4WlN>PB)!1qASfWp&n-^o)8KUsc_db*y=coQ~zO!wDMiG3c5Rqpd}rJO)uZi&=k
z3V2oVvUzUKvpl^Ol}Ug8b_Gz@zABA){HJsxsd2nN-w(-&h9}eA~r!I7MT9c=akKh6sm=)=Rg^dpZ^&JGSz?T#23?O
zJl=5LxuBV+`+<^JwYLZc0Ua}kI8xjSzp05LQU_35)Ly!$Xy^7$z!4!2JM8_6#>j*t
zvwL}YnQXlWN+&r%aYhO51CwtQLf<0AuS$UWbm6h_#|pfT2cYO`Zq{<`K%h2a$+(J)
z`&*7Fw8zR~eEhUz>PMU)bt*THp(`6hC}~EoZRk?TKVB;{pm%kHdiV=>V)*dguIFc7
zE0|)z<>-K45X4r2kQ!GXt$W$4`570JM+7ws%HQtxjzcebj^#TI?ft0SG#!$h^<6BT
zdlwh*Al>;A9wk1?NbUKDIsh^!818W{bUK^y7I_038m_V1?B@I*%$m9WC-C+4b4EV;WaLnjX@{Beo<%fw~A3GM2DcD6K)nZa-q+dAYJ{^NVo|l6WwB-Fdl`Nr>@w|DpK3kc*)7*k2mtz4c@T~f
z%9R@_Wt`lk@{+W*EGdz!!>I7%9v8mwD_v&lv@tav=ll%24Hhg6-Xo5ya=^wo=-Mg>
zJ&C1_^$%*;!yZB6=QAKxan4o3_0t&;$@zmFr&0oEjok0~qT3XeYw9+9r@cS}<)%eu
zTgub}xe+VmYCRAGJsb)JWV-K1vtQ7g#MTB|tpGl%OT2a12amL|#C^-a|9!4|JwJY*
zlS{I}H|?P_E9K0zg+cn%pdY5G6dwaqNH@e
zgT+C!Wn%j@%T0hQ^bQzccCKOrpZj0y2<3Gu=(;nQ$lBf?fW*^@P+>IO5Ye0VB$(`m
zifdnbVFQTP{-R<7gIuz?zb^Bc{K;;J{1b~1g5=R|WO6c1{AURixw|aQppfC`5Z#d6
zl7x{!E%63nddT>|FIkS+ei{x0PA2)H^_6-8(KL(?p6F7)DD3f3S#f9;DR$Iacb&ly
zo+Ql}5K`3kLVYdYOHeu4xJs95HmbJvaVCZsBRi^-+wL#VE3!5h@z*6;p$M~{p=3j2
z)_in~{&zvoRzu0GbTZ0D5vi>3@X{ZtDThPNyZFhIeo&CB(hkaS>~y^I$nf9Y=nBQ!7juH4`}lrmM1a
zw+^Ph@9`tL3z1O^s2!0{#7%!U82OqDfb8C{{~~q&;$8ovI+Xc$a4q`;d9(P@(g}oZ
z5@u@4qo!j#Nr=(}4*d44-YnS^%CKC|M(VE-AO&}mw@!1-JeKlaSexu_gI}ujGp;V-37)=PYXTGM`lULKf+dde8G{r;X#;0URyTr
zOkbj-Ja%6>N@C#au_ZKAN2f3buKc~@p?ssmBWIfwiw582ZL&mUg%PoUx~7%#4#-b`
zU{^Nh>56iK;JYrHcX`Iz`^_eUHQg&Aqe%TYvVPj|`wp?7!OE9@kg03Ais5VlYi662
zCsQicffy*MN?ZqJ0CsIoVr6XdII557?dygsPnm-E%1GqUcgQIGy_|LC*|K~*oy)+A
z6R`h+vez8D&_HAzG+Fca>j8Jz7Z$Z{Ub}A#O_K!t;Y=q`oE#Pwn$RwCFJc~T@MVl`WFxtTHi!Y(#^DzOte473YV5-L}(
zoDp%yAU1p#B8}MK2rCEmW>2h!&dSIIH3WCCa5ysTn`QS9er&gl##{NX2X&z-;4;{J
zV#jl&ra}O26i0vwg+XYu_5h@VDhyhK&g*M-m8MmRB=K(q_f(Q7DOxM1;2PsD_*w_7
z0IX*vPD24c^TCAj39HK639b0+v9}25wBODF0cGTuPkPOz^=;7N#;?b4j?@(Ody#b-&eetX)Yabx_`eK$@>!C0(FdG2tAlrf@kko0!
zPhN>x-%3FdCJefVonDU_{~=Y1`)!ah0>s`b{kZqwdk-WV#mK)y5f@)aOUDRkOsnR}
zJN@Wd9Y&((-ZFDjCvPXV2wl*Yw-|pRc#v^dFyGXxRhYqClYZ|lku1$S{F(|sIy-5j
z>u60RR5dxh?HjO&wxJq-9`%UD5{hSEWImrlDJZE~D{x7)m$>a0F`qPemb80EGny-U
z+NEn@n7_95n)Bgxs`j8l`k`n-;X-2(??ly`COv%Z!+qYOYy7@Tk@=%!Ui6{u`&VrU
zGqlPE9U^3!MGw#SV_#A)>kJv%YK@}62074i7YLDW#BYe6JDUM_KhqMgZ7rua{j<4)
z11rjComWVvgIPwYPStMPa#%mGxc_gJ0i-r0G?Z>@NV^}iLktfFlweU6`y2ybk4Pj1
zqwlSOMqF!Yz|(IG&}MdoiDb=0qP~7SM#(899q!Y9)D0}B*1dZs`l-hvA(mAs
z(+Y>!ok-S8^=0TfZKr1
zM_xZQDkNM>a&ypXr+Ke>;_K%+xF##CA$qs~a
z1;)QTXh*p|lb1c4PDAlc!?g-Un3QXBURN+Y(zzE*)L=P2xy*ITGy#}sy_P6D^xjqm_CWZ!?O^0)5AH@4uikHLre-8D(
zbw5uH1g4FV*8}q7vX3*!#LXtG(OFrZd|%3{)^4`LI#`kjjQnWGng<0M3<02cC~X3u
zBIK^Gugg20KTnICMcfF|_zk(Ri@2Q#b$yc)jHx}m7Cr0@e%ZCJxHU6G3z7AfU@R4b3nkuwr*tAAckhy*UK<`*!6N(
zSmI2}5qF|u^vW_(dHS(yJu0ptv7Z$3-U67aCWi-I+YZUVig4F37=+*o4!H~`A`HR;h>|wT^ERQ_z)7=^tW<1
z%pTFcRy-dSLgtW|tpz@p6qRmMWLxcoov!DYRIZm3<`JZMmT&
ze#KSsTDoa8U^Pp1o7qKu-EU^QD4koHlCp0V#`w~#NTl?5Bo#vwa~RI#_ROcU|KJ%u
zrqomM^-9{UNo00pPrlJkNd)Pw;9NDQ=Nl5ZGPi~CYs@T~bN
zThA4?+CFx1Jms{AkF1K>eQ3c?nbck-T=Dp%!=|v0nuxN&8gSwO7za>!CH{L^p^n`u
z0HvAMd2L-R?9WZ??mi04y#QTI;7c<9YhjzvL6ldYR^~}MiY}P0K3zw&W@AqW=C0j;
zYj;9Cv_nd2r)!`s!_n_~_&(Fst`8wrJ42do^x%xjTm*Q87
z6DpkXSC|1WC*}_Ra
zsV{cu!>utt0e?|thW%~;^?aMm8BDJ=~a0ek=40?n#N8f(e!RvUi{
zEo^*G&|yeV-!!z~*?Zy-H-WjBZ#jo#b4^4WNLe2~UubO0T4Q^bJaHIk(R}rvNIFo#
zeVN)(S<3a9R`k=aTFm<7q7?5|pf=_GCkrD4WHT2)G~q*qppaHj|61KLWWXu+q{Z~qncIk`q!vqR!id<2d;2^B_LE*krvac_;A5d`t?-Dj<9?U|#S7!b%90=D)!y+|e`7B(zT8L;GGIY3IP1
z;idsrAyf<|b~wKa!iU>OwJ5iMExno}#Tf~d>x?(}5l)NI?#UE(9ryGjT5oD#QYj?27_~}cy-lfq_x}0tchj!%$ltnNQUDzK6#0S^3>0t?Q`8f
zp9CqmZJ%hUFP6Ah_Ko@$|IUtBS=XPrHk$S|`?*i)!!s>Vt$G9!v2GkE?5miJFTC-z
zE8u?6L)ovieI{v8f%TPrv@87GEHy5eaTmE0)ZZVzZ3Ml2oUwSqd>;VqRY%F
zCqf{R9yIAn+hEkM|0{dZNuF&mqm279z`cY`Y@nm6l#c38Gu`5CtTi+pd^#Wh-c+pNW2|oQ(o*HAF)-mpm`u47
z>jPovCK-%m%>w%v2*Hpub`TQD$I3VNdac6uSI?O(%F@&3J@x&O=UDZ|pob#KM0m!tp
zKr;30<3V6vx}ZN7Pzxa`jfjeqNLx;%4dn@#J>2q$(%!3iEHWZxq=~>05Eys11iPNr
zSuMI_hK(mF&$0#zi%9Kc%sSQ7&%lXDS`nTd{9Yni)q7LfgBIZ`IioTM7w4S#6X(9m
z&XzI@*$B61K?Gf7+W}X-{}SQ#=>p7iavRp868CnpjCtbiJ%PFPoSyCxXYeglLaMG$
zZg@%)z5X{Xk7)%?4;-gniOY1rM`V3fvS>|XFUogV&YKrY(iTF~!l=Z?VR
z`RPaT7~Dym9=`bGG-TC*o79K(rv(9n4>uU8Se;nvVz?EIiQwiY2
z#r~DX%aHjbC?-W&1WlbcIq%cNcl^vt=y6iF@~4o8EcX=X_t`rT9ucx|I^P|N?*S6p
zx3`~6<4vGhh)C0{iVi&fEGRw}2ht8=brsB&Xt}rHz4yEg
zu#bq&jHYnt=mq|$9=0zh>f45R`mm#Y>J`Ei2;A^vw*2!VH`(s?OyftL?w=D{Da1;O
zi}9X$0)4gX-|%EH@n&%7qRBR(T0wSM*$5zNz
z&4lns68hHfqF-Jr=YZV;>Futeqw3D3L+q(Ef7z5P{?&afg8j6BxM8ZSJb~tXIH_49
zh&l0||Ai`>D({rjm;E7`Ydz77OqV&Pbwj;F<80IH)|tV#)mmU{IPbpYraDLao)Np9Rrm>I)CFuRyAdOx>cf{5k9D41
ztmd5r|zS_hcEOuUJ(7jsMRBM8{4-xbPL1cED-W1a7WP6Ca;o6^evdoxAzRRR7o
zysso)CxMkf4O{89VwEA-*bRspZUH)CI_O(>zT)3*su;nzPet)W^)Y5$2sU4%AUzrK
zl)p1vZOEoozqGTLGoI$Za3j;s0|^84il#eVGZsvx1wio_zBBHo4w!M>?1f(9Q}E@T
zpnj|M9sc&B=fG&W71d_Qyf8tlxZ!(ktJQ^TqaDrtivnJUqNX^_E)(oa;SD^S0qVt`
zhe*ZESf3CG+>K5WA~dsX%jxt$Bh~3M20hoz?mK|VL;D{CaOP5lA{8
z6P59;I_kDi5K4#vnW*2;YC`bkrqJT%cEKpLAl>sLvenALf%Y)=zLO>4uxFaf<4E&b
zV$G5--QSOvoi+N*t`iZQK1|2^!?etK+`_DLZwY0d6UgbXMRzLmD-AblXVY4F7n!NG
zI-~)k!v-TOwOt_R{SaW{tEDBsXX{UMK$q$Ob!}jA={-0YM|+0
za~$k(wk;(&ufXK}4QC`cmQB;{TEAR+;h*$GFc24A-;@6GUk`{atiKfCIjw_QDzOm)
zh+qU*L0whqkumfhR1uMw?J=rJn>b196rV#NV8&X-MPUepZ2T1-zsL`Dim}i*2^pSg
zu^&Zv$=^NJy9rHL>d*kdeucctVa5t<0xjM^VWyDYzgOG-;X%{M(egzJayd>y#XPLP
zx(nhS$>a?ydv}327p{@=37n`v^}h>RvKtE4P{ib}9t55PIBwbvpknv8h2iB1Bhv=7
z{lh=-0I@R)a#HTG_r81M!1bA*4-75Wyi^}tmQO;@&=H}to)0<-{6&uA*l!AJ=kgi;|=YzkouZ!1xFl@BpWx~
z;m>|1_WQOGAi`0(-;n!{o9#LeSiTLG{+*BX8AWLew<#BzJ`N988ht`!D
z_Wq1Z1c>y3$R#>*qZ!a&9=^!+k+|+lHv7yd@>q-;Tf^V4I-{?c{alhQ!s?Lw*A#H~
zh`te-`~@q1A2YoMa9EKc8w0gs4DYSf|Mu?y{cE&3W+$`uA(GU%^;77}3n(d%Ur_Mb
zk|*xJZW0llfFk*7_A$v^D*g+MkTwI#;D$<Px7{95#utwDq)0Xvj>CZRZm>jgI_vXIzW}T`S!WtVvq_*
z(|oB!*EBN9$c(xo;WVqR0U8HJ?H_8ofl{)8z>MdSAZ^cu4D*D(*3W@l-vs5S!ngem
z&Z#SQEF>}?0FO6OeC8_9vDbe9A(RR0_4%^B^l^x;Y*va3avXo!%S6H!gqeVTt!X+F(8DnxacQm!-m@ch}fB1Fpb<3eoZh;j2v?VRANI(R${jH41s809ulQ?CrlFHtcpZRJ@e1$M#*6(F3)y
zr1Cj7u#klx0rbI8ra0e#Fd}XU>ZqfcSUOV1j8BzH%%q{{~1p}PzDZw1`r;q
zCnz(zSaM=@6aJHFvLsP1g6DfRRFFd30CYr}*#B8lE!r({Y$cC~P%cEyW@+cMd+ykb
zcgAKh`DQaM4I=kY0L$+&ffEj}<_Mmo
zM}=pUnJC^HT7H`w8GisW9CS5Ni4TCo|KF&^GnQNC*6x$4@^hf?7#=_7NOtD5=H`Cx
zneh-lZPgp-cCNpA(GC()#k~LWW$M?VBao1;$s}7j(cS8(Sc08%6mKy4VyjW3w^j}t
z(@3`I8n$uaqfstwTR_gUdw1K8G)RRRw>rRrR?D7xd8F2j+t;&Qu@&Gu#y+lH8fy
zcsH3(jHPl#pe|S0pc$21XNxgFah~|O^lsu1$i@iYS^c9yP-!tmMU;nCM+-LlQ={j8E5C?Zu5A>8;~O^x#J%bQpWEy?F9IR(qxFs3Hqvf{C4kEg{`mHevWN?XuAnV{U|)Hb}P16IZ32(R_uKs^Z}lMW2B~!!y@=q
z``=tDpAE=kUSUVcAN}`*g61o6Dz^{6*Ze`dzR$mh`M30|=lEJR{;j{4TWIMdk%7h>
zuPlbGyw5?tH!NZChINzdqKu9spV{rcYRT
zkhoivGp38>X$-y_#7*BP=?l0TNZ?zZ`R|rH%?;2Ew@x|Q^>^uP_f@R?PTbY63|-dE
zz?-D5SFw09ZsXg|jW
z)L&yLs>7ei?4IcWdEKEDQmg@>f~s$Evk0gOarfL$D5
zybmQ!Q*I7%40ZBw2jqFm?)^Dv$X-Dt0R=caWthmclH3a3aE=J`>Lf{sHDG*^{0e`-
zxBE+C_BCAa>c{$ip7#fVu*1*0(k`jPx{JtL@`kAs5x&>z>*)on1;_U5*X9&&06UlT
zy4@ueTVUe5Z-2Jgs{NF(x|ildas!x2Hu(mbf6WkS{Gqk4A&%RnE!$@=992c2W+@(X
zvwCo@R!lse5^or~NG6pu=0d$dv6k_<@KL`LQ-1s63Q8v#xN8z&iPXEF8{$HUS0=;9
z7nUKkse*f4W%gCl!Q4Yntr{{rfiF-G)jl_4LE~Ebj#6LAATk12PEA3S>`X#I4WuKZ
z;t*ayx1xJXY~p`D3T&ZP`pp|aql24CXY?jAT)tYY|q8};B%095J0gBgjHt+ktN$~M*GCE
z{<2?iYZu8-a<q#M7~YI7R3r_8Fbp0*u~l*|K;^pVK|I!%ZK^tt3U6lpi76fK?5M
zq-KPT*-Ki3^AeNrp21W-6d4z63nJ_U?(~@@1(w1in#%xxN;(Kf^t|6@19q8zSuK2^IHlnUd==PbNe1CFIXh
zJnMyvNYSI3S0HHjkRST+>lT-O4~+nrmwHcX8fYmyWmCh^dHw5HJv9Dt90}?QG0gkL
z_1?#2M!EW9p_zA7Nhxi4v^`c%P_#5~2o7WWS$Y|O4rLt%mjjo|B^^(~)p=0aet|G<`zL`n7VY~@}v%^B#f+4q6u62i|ul`gvYRMCM=7gOFn0QtziQeOEV?g$Dt04Kh0VNfeTvV>11_54=1
z+|(Yj^rr(BrCUnJb&?T!O?#%rBN
zVIJm3p-(++wgE(`TCC$W2fgB?pe#}Jw6IWbLl5XRJ35a$U0sv{H?3yvZ+XXJ!U+f&P<0xSQPqlX7?
z!*j03vb9tQpOoSidHqoEvxL(vP;i7o?f3+o;%F%|ezm4$CyI`&{z{5wUuD0q$t=m+
z7EHF{oDJ~}>WZv(-XCu#i8x-U!1D-H&Pc{2^!V#B8)|p{PgLa{%2!D$~IHPYj-%6-{Pao{ol{Z##%c6VPC*OZEas^
z0aw%~(kYEv)_&6jB+8$apFiQqcbmiyUS`@eN@kxFtKFbBP9?c(e2kYyDHF^2DatSZF;Q7(S?H1~o8j;J
zV4)RKC?YYP8C!25*$^oBHHf%V6!LsZ8kTNcZr~9vb1bakl6WP0~j_GSng*azB}SfE+|Z6h@0_=!qi!3rcZQe`G=s9BAo}JmBvo+
zDt+E>>vEKjvRHKCgN<$iZyriGGc{vh-ItwLXVegnBfv}Q{ohwImElyDo1U82H0*0z
z4#bDd7~B;@&c{f>=}QelBcd%F<9vICBr}PF#x2LIqP3K9L(XfR*Uy{pWc%OONJASF
zg4YUJ5{DM>h6u*(gKOt&HwJo%S*4y?-#0UJ*dn0PU{*IPH-O4Q=0CsiP@Jh<qo2rUIztUw?U8;h-Wj5+ziV#;_krfxhN^09$Ycx099ViS-ZdMTF1G^;h3f?Fb39X
z&4UwRZrlXk3Z*sRBGZkZwR;*S2x*JBj@omNj|aPe(rhM}_@;Kx4U0ZAA@lMhtkSps
z7Er-I{RWWvto6N@$4_T;k<436R?@beqFtK9mw`ZV01-NZvhzoQqM0|XVUf4p(F@B%
z|5@w=&5Kxuif~2B_UxzK(jy*KL^}3vOa!Xfdh{&g6`HAO&OsYF-#@f`CO4lS|1vDe
zM5i8Rbv2m}eW-G+H0mMNqu5%cekd4}tFx!nR|Q|NWlmWTC&!5>?g$l4(C!!TRoOS?4~n=aDrxEFhw%RVdCU9b&N{W4Hd%ar(WvWK$?;
z!;Nyvjpeg4_zQaaNyUiU%zN5f)gy1@cb4BDHsuGu#s-|7V74Zcp8f#73W&+BNt3Wn
z!k_4TqA+UgcXd@G;Ih%7WslG1O(~;^v~Bb11E|ZeX-_BdlF|}9Vgu>!>!1`qvfHvf
zpkSoh0es0evClo4_dDqIB2@nEYtDb|kvFJe?Yb8%$gf1Q#jZVAbckI#Yz-t*wyU58
zgC^YDMZ--{x@lpKaRgMhqOoA+x^xa{om#;J*bB^nbb;W9MT+|1XW*?m |