Repository: alunduil/zfs-replicate (language: python)

Issue:
Remove any

Compression option parsing appears broken
When I run with `--compression=off` I get this error:

```
Traceback (most recent call last):
  File "/nix/store/hkfqsx8dpy1v1d85114xr427g1a0nfhp-python3.8-zfs-replicate-1.3.0/bin/.zfs-replicate-wrapped", line 9, in <module>
    sys.exit(main())
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 781, in main
    with self.make_context(prog_name, args, **extra) as ctx:
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 700, in make_context
    self.parse_args(ctx, args)
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 1048, in parse_args
    value, args = param.handle_parse_result(ctx, opts, args)
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 1623, in handle_parse_result
    value = self.full_process_value(ctx, value)
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 1965, in full_process_value
    return Parameter.full_process_value(self, ctx, value)
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 1589, in full_process_value
    value = self.process_value(ctx, value)
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 1579, in process_value
    return self.type_cast_value(ctx, value)
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 1568, in type_cast_value
    return _convert(value, (self.nargs != 1) + bool(self.multiple))
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/core.py", line 1565, in _convert
    return self.type(value, self, ctx)
  File "/nix/store/5fdi3wy303acacv48m9c9xlxw64184w7-python3.8-click-7.1.2/lib/python3.8/site-packages/click/types.py", line 46, in __call__
    return self.convert(value, param, ctx)
  File "/nix/store/hkfqsx8dpy1v1d85114xr427g1a0nfhp-python3.8-zfs-replicate-1.3.0/lib/python3.8/site-packages/zfs/replicate/cli/click.py", line 24, in convert
    value = super().convert(value.name.lower(), param, ctx)
AttributeError: 'str' object has no attribute 'name'
```




Discussion / hints:

Fix patch:
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..c4ac9fd
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,27 @@
+---
+name: pypi publish
+on:  # yamllint disable-line
+    release:
+        types: [created]
+
+jobs:
+    deploy:
+        runs-on: ubuntu-latest
+        steps:
+            - uses: actions/checkout@v2
+            - name: setup python
+              uses: actions/setup-python@v2
+              with:
+                  python-version: 3.x
+            - name: install dependencies
+              run: |
+                  python -m pip install --upgrade pip
+                  pip install poetry
+            - name: build and publish
+              env:
+                  PYPI_USER: ${{ secrets.PYPI_USERNAME }}
+                  PYPI_PASS: ${{ secrets.PYPI_PASSWORD }}
+              run: |
+                  poetry config http-basic.pypi $PYPI_USER $PYPI_PASS
+                  poetry build
+                  poetry publish
diff --git a/.github/workflows/python-checks.yml b/.github/workflows/python-checks.yml
new file mode 100644
index 0000000..aa035a7
--- /dev/null
+++ b/.github/workflows/python-checks.yml
@@ -0,0 +1,60 @@
+---
+name: python checks
+on: [push, pull_request] # yamllint disable-line
+jobs:
+    build:
+        runs-on: ubuntu-latest
+        strategy:
+            matrix:
+                python-version:
+                    - '3.7'  # EOL 2023-06-27
+                    - '3.8'  # EOL 2024-10
+                    - '3.9'  # EOL 2025-10
+                    - 3.x
+
+        steps:
+            - uses: actions/checkout@v2
+            - name: setup python-${{ matrix.python-version }}
+              uses: actions/setup-python@v2
+              with:
+                  python-version: ${{ matrix.python-version }}
+
+            - name: cache pre-commit
+              uses: actions/cache@v2
+              with:
+                  path: ~/.cache/pre-commit
+                  key: ${{ runner.os }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}  # yamllint disable-line
+                  restore-keys: |
+                      ${{ runner.os }}-pre-commit-
+            - name: cache poetry
+              uses: actions/cache@v2
+              with:
+                  path: ~/.cache/poetry
+                  key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }}  # yamllint disable-line
+                  restore-keys: |
+                      ${{ runner.os }}-poetry-
+
+            - name: install dependencies
+              run: |
+                  python -m pip install --upgrade pip
+                  pip install pre-commit
+                  pre-commit install-hooks
+                  pip install poetry
+                  poetry install -v
+
+            - name: pre-commit
+              run: pre-commit run --all-files
+            - name: vulture
+              run: poetry run vulture --min-confidence=61 --exclude=zfs/replicate/cli/click.py --ignore-names=main zfs  # yamllint disable-line
+            - name: zfs-replicate --help
+              run: poetry run zfs-replicate --help
+            - name: pytest
+              env:
+                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+              run: |
+                  poetry run pytest
+                  poetry run coveralls
+            - name: poetry build
+              run: |
+                  poetry build
+                  tar -zvtf dist/zfs-replicate-*.tar.gz | grep zfs/replicate/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 2c59efa..44567e3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,7 +3,7 @@
 # See https://pre-commit.com/hooks.html for more hooks
 repos:
     - repo: https://github.com/pre-commit/pre-commit-hooks
-      rev: v3.2.0
+      rev: v3.4.0
       hooks:
           - id: check-added-large-files
           - id: check-builtin-literals
@@ -19,7 +19,6 @@ repos:
           - id: debug-statements
           - id: detect-private-key
           - id: end-of-file-fixer
-          - id: fix-encoding-pragma
           - id: mixed-line-ending
           - id: name-tests-test
           - id: pretty-format-json
@@ -27,17 +26,17 @@ repos:
           - id: sort-simple-yaml
           - id: trailing-whitespace
     - repo: https://github.com/pre-commit/mirrors-autopep8
-      rev: v1.5.4
+      rev: v1.5.6
       hooks:
           - id: autopep8
     - repo: https://github.com/pre-commit/mirrors-mypy
-      rev: v0.790
+      rev: v0.812
       hooks:
           - id: mypy
             additional_dependencies:
                 - hypothesis
     - repo: https://github.com/pre-commit/pygrep-hooks
-      rev: v1.6.0
+      rev: v1.8.0
       hooks:
           - id: python-check-blanket-noqa
           - id: python-check-mock-methods
@@ -46,17 +45,17 @@ repos:
           # - id: python-use-type-annotations
           - id: rst-backticks
           - id: rst-inline-touching-normal
-          - id: text-unicode-replacement-char
+          # - id: text-unicode-replacement-char
     - repo: git://github.com/FalconSocial/pre-commit-mirrors-pep257
       rev: v0.3.3
       hooks:
           - id: pep257
     - repo: https://github.com/asottile/add-trailing-comma
-      rev: v2.0.1
+      rev: v2.1.0
       hooks:
           - id: add-trailing-comma
     - repo: https://github.com/asottile/pyupgrade
-      rev: v2.7.2
+      rev: v2.10.1
       hooks:
           - id: pyupgrade
     - repo: https://github.com/asottile/yesqa
@@ -64,12 +63,11 @@ repos:
       hooks:
           - id: yesqa
     - repo: https://github.com/asottile/blacken-docs
-      rev: v1.8.0
+      rev: v1.10.0
       hooks:
           - id: blacken-docs
-            additional_dependencies: [black==19.10b0]
     - repo: https://github.com/asottile/setup-cfg-fmt
-      rev: v1.15.0
+      rev: v1.17.0
       hooks:
           - id: setup-cfg-fmt
     - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt
@@ -77,11 +75,12 @@ repos:
       hooks:
           - id: yamlfmt
     - repo: https://github.com/Lucas-C/pre-commit-hooks-markup
-      rev: v1.0.0
+      rev: v1.0.1
       hooks:
           - id: rst-linter
+            additional_dependencies: [webencodings]
     - repo: https://github.com/Lucas-C/pre-commit-hooks-safety
-      rev: v1.1.3
+      rev: v1.2.1
       hooks:
           - id: python-safety-dependencies-check
     - repo: git://github.com/detailyang/pre-commit-shell
@@ -90,16 +89,17 @@ repos:
           - id: shell-lint
             args: [--format=json]
     - repo: https://github.com/adrienverge/yamllint
-      rev: v1.25.0
+      rev: v1.26.0
       hooks:
           - id: yamllint
             args: [--format, parsable, --strict]
     - repo: https://github.com/psf/black
-      rev: 19.10b0
+      rev: 20.8b1
       hooks:
           - id: black
+            args: [-l 120]
     - repo: https://gitlab.com/pycqa/flake8
-      rev: 3.8.4
+      rev: 3.9.0
       hooks:
           - id: flake8
     - repo: https://github.com/pycqa/pydocstyle
@@ -110,17 +110,18 @@ repos:
       rev: 0.9.0a1
       hooks:
           - id: doc8
+            additional_dependencies: [stevedore]
     - repo: https://github.com/PyCQA/isort
-      rev: 5.6.4
+      rev: 5.7.0
       hooks:
           - id: isort
             args: [-m3, --tc, -l 120]
     - repo: https://github.com/codespell-project/codespell
-      rev: v1.17.1
+      rev: v2.0.0
       hooks:
           - id: codespell
     - repo: https://github.com/myint/docformatter
-      rev: v1.3.1
+      rev: v1.4
       hooks:
           - id: docformatter
             args: [--in-place]
@@ -129,7 +130,7 @@ repos:
       hooks:
           - id: autoflake
     - repo: https://github.com/twu/skjold
-      rev: v0.1.6
+      rev: v0.2.1
       hooks:
           - id: skjold
     - repo: https://github.com/pappasam/toml-sort
@@ -138,7 +139,7 @@ repos:
           - id: toml-sort
             args: [--in-place]
     - repo: https://github.com/executablebooks/mdformat
-      rev: 0.3.4  # Use the ref you want to point at
+      rev: 0.5.3
       hooks:
           - id: mdformat
             additional_dependencies:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 76bc702..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,54 +0,0 @@
----
-language: python
-python:
-    - '3.6'  # EOL 2021-12-23
-    - '3.7'  # EOL 2023-06-27
-    - '3.8'  # EOL 2024-10
-    - 3.9-dev   # EOL 2025-10
-    - nightly
-matrix:
-    allow_failures:
-        - python: 3.9-dev
-        - python: nightly
-    fast_finish: true
-
-cache:
-    pip: true
-    directories:
-        - $HOME/.cache/pre-commit
-
-install:
-    - pip install --upgrade pip
-    - pip install pre-commit
-    - pre-commit install-hooks
-    - pip install poetry
-    - poetry install -v
-
-script:
-    - pre-commit run --all-files
-    - vulture --min-confidence=61 --exclude=zfs/replicate/cli/click.py --ignore-names=main zfs  # yamllint disable-line
-    - zfs-replicate --help
-    - pytest
-    - poetry build
-    - tar -zvtf dist/zfs-replicate-*.tar.gz | grep zfs/replicate/
-
-after_success:
-    - coveralls
-
-before_deploy:
-    # User and password environment variables are set as hidden variables
-    # through the web interface in the project settings.
-    - pip install --upgrade pip
-    - pip install poetry
-    - poetry config http-basic.pypi $PYPI_USER $PYPI_PASS
-    - poetry build
-
-deploy:
-    provider: script
-    script: poetry publish
-    skip_cleanup: true
-    on:  # yamllint disable-line
-        all_branches: true  # Travis recognizes tag names as "branches"
-        condition: $TRAVIS_BUILD_STAGE_NAME = Deploy
-        repo: alunduil/zfs-replicate
-        tags: true
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 77ee179..3acdd35 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
 
 Examples of behavior that contributes to creating a positive environment include:
 
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
 
 Examples of unacceptable behavior by participants include:
 
-* The use of sexualized language or imagery and unwelcome sexual attention or advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a professional setting
+- The use of sexualized language or imagery and unwelcome sexual attention or advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
 
 ## Our Responsibilities
 
diff --git a/mypy.ini b/mypy.ini
index 57a5dd1..da2acf5 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -3,3 +3,15 @@ strict = True
 
 [mypy-stringcase]
 ignore_missing_imports = True
+
+[mypy-zfs_test.replicate_test.list_test]
+allow_untyped_decorators = True
+
+[mypy-zfs_test.replicate_test.task_test.generate_test]
+allow_untyped_decorators = True
+
+[mypy-zfs_test.replicate_test.optional_test]
+allow_untyped_decorators = True
+
+[mypy-zfs_test.replicate_test.snapshot_test.list_test]
+allow_untyped_decorators = True
diff --git a/nix/default.nix b/nix/default.nix
index 1575908..1486d02 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -3,4 +3,13 @@ let
 in
   pkgs.poetry2nix.mkPoetryApplication {
     projectDir = ../.;
+    overrides = pkgs.poetry2nix.overrides.withDefaults (self: super: {
+      pytest = super.pytest.overrideAttrs (old: {
+        postPatch = old.postPatch or "" + ''
+          sed -i '/\[metadata\]/aversion = ${old.version}' setup.cfg
+          echo "VERSION"
+          grep version setup.cfg
+        '';
+      });
+    });
   }
diff --git a/nix/shell.nix b/nix/shell.nix
index 33f5eaf..84e12b4 100644
--- a/nix/shell.nix
+++ b/nix/shell.nix
@@ -3,9 +3,12 @@ let
   zfs-replicate = import ./default.nix;
 in
   pkgs.mkShell {
+    inputsFrom = [zfs-replicate];
     buildInputs = [
       zfs-replicate
-      (pkgs.poetry2nix.mkPoetryEnv { projectDir = ../.; })
+      pkgs.poetry
+      pkgs.pre-commit
+      pkgs.python38Packages.virtualenv
     ];
     shellHook = ''
       unset SOURCE_DATE_EPOCH
diff --git a/poetry.lock b/poetry.lock
index 2a03e76..4bb1a9f 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -21,11 +21,11 @@ description = "Classes Without Boilerplate"
 name = "attrs"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-version = "20.2.0"
+version = "20.3.0"
 
 [package.extras]
-dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"]
-docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
+dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"]
+docs = ["furo", "sphinx", "zope.interface"]
 tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
 tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
 
@@ -35,7 +35,7 @@ description = "Python package for providing Mozilla's CA Bundle."
 name = "certifi"
 optional = false
 python-versions = "*"
-version = "2020.6.20"
+version = "2020.12.5"
 
 [[package]]
 category = "dev"
@@ -50,8 +50,8 @@ category = "dev"
 description = "Universal encoding detector for Python 2 and 3"
 name = "chardet"
 optional = false
-python-versions = "*"
-version = "3.0.4"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "4.0.0"
 
 [[package]]
 category = "main"
@@ -76,7 +76,7 @@ description = "Code coverage measurement for Python"
 name = "coverage"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
-version = "5.3"
+version = "5.5"
 
 [package.extras]
 toml = ["toml"]
@@ -87,7 +87,7 @@ description = "Show coverage stats online via coveralls.io"
 name = "coveralls"
 optional = false
 python-versions = ">= 3.5"
-version = "2.1.2"
+version = "2.2.0"
 
 [package.dependencies]
 coverage = ">=4.1,<6.0"
@@ -127,33 +127,35 @@ description = "A library for property-based testing"
 name = "hypothesis"
 optional = false
 python-versions = ">=3.6"
-version = "5.38.1"
+version = "5.49.0"
 
 [package.dependencies]
 attrs = ">=19.2.0"
 sortedcontainers = ">=2.1.0,<3.0.0"
 
 [package.extras]
-all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "numpy (>=1.9.0)", "pandas (>=0.19)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)"]
+all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"]
 cli = ["click (>=7.0)", "black (>=19.10b0)"]
+codemods = ["libcst (>=0.3.16)"]
 dateutil = ["python-dateutil (>=1.4)"]
 django = ["pytz (>=2014.1)", "django (>=2.2)"]
 dpcontracts = ["dpcontracts (>=0.4)"]
 ghostwriter = ["black (>=19.10b0)"]
 lark = ["lark-parser (>=0.6.5)"]
 numpy = ["numpy (>=1.9.0)"]
-pandas = ["pandas (>=0.19)"]
+pandas = ["pandas (>=0.25)"]
 pytest = ["pytest (>=4.3)"]
 pytz = ["pytz (>=2014.1)"]
 redis = ["redis (>=3.0.0)"]
+zoneinfo = ["importlib-resources (>=3.3.0)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"]
 
 [[package]]
 category = "dev"
 description = "File identification library for Python"
 name = "identify"
 optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
-version = "1.5.6"
+python-versions = ">=3.6.1"
+version = "2.1.3"
 
 [package.extras]
 license = ["editdistance"]
@@ -172,15 +174,19 @@ description = "Read metadata from Python packages"
 marker = "python_version < \"3.8\""
 name = "importlib-metadata"
 optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-version = "2.0.0"
+python-versions = ">=3.6"
+version = "3.7.3"
 
 [package.dependencies]
 zipp = ">=0.5"
 
+[package.dependencies.typing-extensions]
+python = "<3.8"
+version = ">=3.6.4"
+
 [package.extras]
-docs = ["sphinx", "rst.linker"]
-testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
 
 [[package]]
 category = "dev"
@@ -188,8 +194,8 @@ description = "Read resources from Python packages"
 marker = "python_version < \"3.7\""
 name = "importlib-resources"
 optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-version = "3.2.0"
+python-versions = ">=3.6"
+version = "5.1.2"
 
 [package.dependencies]
 [package.dependencies.zipp]
@@ -197,7 +203,8 @@ python = "<3.8"
 version = ">=0.4"
 
 [package.extras]
-docs = ["sphinx", "rst.linker", "jaraco.packaging"]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"]
 
 [[package]]
 category = "dev"
@@ -221,11 +228,10 @@ description = "Core utilities for Python packages"
 name = "packaging"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-version = "20.4"
+version = "20.9"
 
 [package.dependencies]
 pyparsing = ">=2.0.2"
-six = "*"
 
 [[package]]
 category = "dev"
@@ -249,7 +255,7 @@ description = "A framework for managing and maintaining multi-language pre-commi
 name = "pre-commit"
 optional = false
 python-versions = ">=3.6.1"
-version = "2.7.1"
+version = "2.11.1"
 
 [package.dependencies]
 cfgv = ">=2.0.0"
@@ -273,7 +279,7 @@ description = "library with cross-python path, ini-parsing, io, code, log facili
 name = "py"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-version = "1.9.0"
+version = "1.10.0"
 
 [[package]]
 category = "dev"
@@ -288,16 +294,16 @@ category = "dev"
 description = "pytest: simple powerful testing with Python"
 name = "pytest"
 optional = false
-python-versions = ">=3.5"
-version = "6.1.1"
+python-versions = ">=3.6"
+version = "6.2.2"
 
 [package.dependencies]
 atomicwrites = ">=1.0"
-attrs = ">=17.4.0"
+attrs = ">=19.2.0"
 colorama = "*"
 iniconfig = "*"
 packaging = "*"
-pluggy = ">=0.12,<1.0"
+pluggy = ">=0.12,<1.0.0a1"
 py = ">=1.8.2"
 toml = "*"
 
@@ -306,7 +312,6 @@ python = "<3.8"
 version = ">=0.12"
 
 [package.extras]
-checkqa_mypy = ["mypy (0.780)"]
 testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
 
 [[package]]
@@ -315,10 +320,10 @@ description = "Pytest plugin for measuring coverage."
 name = "pytest-cov"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-version = "2.10.1"
+version = "2.11.1"
 
 [package.dependencies]
-coverage = ">=4.4"
+coverage = ">=5.2.1"
 pytest = ">=4.6"
 
 [package.extras]
@@ -329,8 +334,8 @@ category = "dev"
 description = "YAML parser and emitter for Python"
 name = "pyyaml"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-version = "5.3.1"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+version = "5.4.1"
 
 [[package]]
 category = "dev"
@@ -338,13 +343,13 @@ description = "Python HTTP for Humans."
 name = "requests"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-version = "2.24.0"
+version = "2.25.1"
 
 [package.dependencies]
 certifi = ">=2017.4.17"
-chardet = ">=3.0.2,<4"
+chardet = ">=3.0.2,<5"
 idna = ">=2.5,<3"
-urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26"
+urllib3 = ">=1.21.1,<1.27"
 
 [package.extras]
 security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
@@ -364,7 +369,7 @@ description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
 name = "sortedcontainers"
 optional = false
 python-versions = "*"
-version = "2.2.2"
+version = "2.3.0"
 
 [[package]]
 category = "main"
@@ -379,8 +384,17 @@ category = "dev"
 description = "Python Library for Tom's Obvious, Minimal Language"
 name = "toml"
 optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+version = "0.10.2"
+
+[[package]]
+category = "dev"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+marker = "python_version < \"3.8\""
+name = "typing-extensions"
+optional = false
 python-versions = "*"
-version = "0.10.1"
+version = "3.7.4.3"
 
 [[package]]
 category = "dev"
@@ -388,7 +402,7 @@ description = "HTTP library with thread-safe connection pooling, file post, and
 name = "urllib3"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
-version = "1.25.11"
+version = "1.26.4"
 
 [package.extras]
 brotli = ["brotlipy (>=0.6.0)"]
@@ -401,7 +415,7 @@ description = "Virtual Python Environment builder"
 name = "virtualenv"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
-version = "20.1.0"
+version = "20.4.2"
 
 [package.dependencies]
 appdirs = ">=1.4.3,<2"
@@ -411,7 +425,7 @@ six = ">=1.9.0,<2"
 
 [package.dependencies.importlib-metadata]
 python = "<3.8"
-version = ">=0.12,<3"
+version = ">=0.12"
 
 [package.dependencies.importlib-resources]
 python = "<3.7"
@@ -419,7 +433,7 @@ version = ">=1.0"
 
 [package.extras]
 docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
-testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
+testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
 
 [[package]]
 category = "dev"
@@ -427,7 +441,7 @@ description = "Find dead code"
 name = "vulture"
 optional = false
 python-versions = ">=3.6"
-version = "2.1"
+version = "2.3"
 
 [package.dependencies]
 toml = "*"
@@ -439,14 +453,15 @@ marker = "python_version < \"3.8\""
 name = "zipp"
 optional = false
 python-versions = ">=3.6"
-version = "3.4.0"
+version = "3.4.1"
 
 [package.extras]
-docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
-testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
 
 [metadata]
-content-hash = "d007d61c2023614c842d51030afd3915dc972aecf67eb9306a3524c0ae61c8fb"
+content-hash = "983f45abd724f7a0996510d36856043d46565a0b7e9ab979a1064940620da227"
+lock-version = "1.0"
 python-versions = "^3.6.1"
 
 [metadata.files]
@@ -459,20 +474,20 @@ atomicwrites = [
     {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
 ]
 attrs = [
-    {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"},
-    {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"},
+    {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"},
+    {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"},
 ]
 certifi = [
-    {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
-    {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
+    {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
+    {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
 ]
 cfgv = [
     {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"},
     {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"},
 ]
 chardet = [
-    {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
-    {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
+    {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
+    {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
 ]
 click = [
     {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
@@ -480,46 +495,65 @@ click = [
 ]
 colorama = [
     {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
+    {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
 ]
 coverage = [
-    {file = "coverage-5.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270"},
-    {file = "coverage-5.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4"},
-    {file = "coverage-5.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9"},
-    {file = "coverage-5.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729"},
-    {file = "coverage-5.3-cp27-cp27m-win32.whl", hash = "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d"},
-    {file = "coverage-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418"},
-    {file = "coverage-5.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9"},
-    {file = "coverage-5.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5"},
-    {file = "coverage-5.3-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822"},
-    {file = "coverage-5.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097"},
-    {file = "coverage-5.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9"},
-    {file = "coverage-5.3-cp35-cp35m-win32.whl", hash = "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636"},
-    {file = "coverage-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f"},
-    {file = "coverage-5.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237"},
-    {file = "coverage-5.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54"},
-    {file = "coverage-5.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7"},
-    {file = "coverage-5.3-cp36-cp36m-win32.whl", hash = "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a"},
-    {file = "coverage-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d"},
-    {file = "coverage-5.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"},
-    {file = "coverage-5.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f"},
-    {file = "coverage-5.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c"},
-    {file = "coverage-5.3-cp37-cp37m-win32.whl", hash = "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751"},
-    {file = "coverage-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709"},
-    {file = "coverage-5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516"},
-    {file = "coverage-5.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f"},
-    {file = "coverage-5.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259"},
-    {file = "coverage-5.3-cp38-cp38-win32.whl", hash = "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82"},
-    {file = "coverage-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221"},
-    {file = "coverage-5.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978"},
-    {file = "coverage-5.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21"},
-    {file = "coverage-5.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24"},
-    {file = "coverage-5.3-cp39-cp39-win32.whl", hash = "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7"},
-    {file = "coverage-5.3-cp39-cp39-win_amd64.whl", hash = "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7"},
-    {file = "coverage-5.3.tar.gz", hash = "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0"},
+    {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
+    {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"},
+    {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"},
+    {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"},
+    {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"},
+    {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"},
+    {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"},
+    {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"},
+    {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"},
+    {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"},
+    {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"},
+    {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"},
+    {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"},
+    {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"},
+    {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"},
+    {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"},
+    {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"},
+    {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"},
+    {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"},
+    {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"},
+    {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"},
+    {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"},
+    {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"},
+    {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"},
+    {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"},
+    {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"},
+    {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"},
+    {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"},
+    {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"},
+    {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"},
+    {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"},
+    {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"},
+    {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"},
+    {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"},
+    {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"},
+    {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"},
+    {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"},
+    {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"},
+    {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"},
+    {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"},
+    {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"},
+    {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"},
+    {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"},
+    {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"},
+    {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"},
+    {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"},
+    {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"},
+    {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"},
+    {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"},
+    {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"},
+    {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"},
+    {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
 ]
 coveralls = [
-    {file = "coveralls-2.1.2-py2.py3-none-any.whl", hash = "sha256:b3b60c17b03a0dee61952a91aed6f131e0b2ac8bd5da909389c53137811409e1"},
-    {file = "coveralls-2.1.2.tar.gz", hash = "sha256:4430b862baabb3cf090d36d84d331966615e4288d8a8c5957e0fd456d0dd8bd6"},
+    {file = "coveralls-2.2.0-py2.py3-none-any.whl", hash = "sha256:2301a19500b06649d2ec4f2858f9c69638d7699a4c63027c5d53daba666147cc"},
+    {file = "coveralls-2.2.0.tar.gz", hash = "sha256:b990ba1f7bc4288e63340be0433698c1efe8217f78c689d254c2540af3d38617"},
 ]
 distlib = [
     {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
@@ -533,26 +567,27 @@ filelock = [
     {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
 ]
 hypothesis = [
-    {file = "hypothesis-5.38.1-py3-none-any.whl", hash = "sha256:84a83d3d1f05c82e80c04e50d0e4d9ae233676734baa60e488047102f9d5420a"},
-    {file = "hypothesis-5.38.1.tar.gz", hash = "sha256:49d87a00e904d9bd4468f89130932c947410cbf9bd5ec18937b40b045486bc27"},
+    {file = "hypothesis-5.49.0-py3-none-any.whl", hash = "sha256:e91111f2f01abf2566041c4c86366aa7f08bfd5b3d858cc77a545fcf67df335e"},
+    {file = "hypothesis-5.49.0.tar.gz", hash = "sha256:36a4d5587c34193125d654b61bf9284e24a227d1edd339c49143378658a10c7d"},
 ]
 identify = [
-    {file = "identify-1.5.6-py2.py3-none-any.whl", hash = "sha256:3139bf72d81dfd785b0a464e2776bd59bdc725b4cc10e6cf46b56a0db931c82e"},
-    {file = "identify-1.5.6.tar.gz", hash = "sha256:969d844b7a85d32a5f9ac4e163df6e846d73c87c8b75847494ee8f4bd2186421"},
+    {file = "identify-2.1.3-py2.py3-none-any.whl", hash = "sha256:46d1816c6a4fc2d1e8758f293a5dcc1ae6404ab344179d7c1e73637bf283beb1"},
+    {file = "identify-2.1.3.tar.gz", hash = "sha256:ed4a05fb80e3cbd12e83c959f9ff7f729ba6b66ab8d6178850fd5cb4c1cf6c5d"},
 ]
 idna = [
     {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
     {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
 ]
 importlib-metadata = [
-    {file = "importlib_metadata-2.0.0-py2.py3-none-any.whl", hash = "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3"},
-    {file = "importlib_metadata-2.0.0.tar.gz", hash = "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da"},
+    {file = "importlib_metadata-3.7.3-py3-none-any.whl", hash = "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4"},
+    {file = "importlib_metadata-3.7.3.tar.gz", hash = "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71"},
 ]
 importlib-resources = [
-    {file = "importlib_resources-3.2.0-py2.py3-none-any.whl", hash = "sha256:8edc04db69df82b68f6d63925b368ef4d45c3c101e2c3fc418945bab395016ea"},
-    {file = "importlib_resources-3.2.0.tar.gz", hash = "sha256:3a06ef514291bec1cc89c905380b17a59c0381bc536500f322b56404f7a86ef6"},
+    {file = "importlib_resources-5.1.2-py3-none-any.whl", hash = "sha256:ebab3efe74d83b04d6bf5cd9a17f0c5c93e60fb60f30c90f56265fce4682a469"},
+    {file = "importlib_resources-5.1.2.tar.gz", hash = "sha256:642586fc4740bd1cad7690f836b3321309402b20b332529f25617ff18e8e1370"},
 ]
 iniconfig = [
+    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
     {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
 ]
 nodeenv = [
@@ -560,78 +595,93 @@ nodeenv = [
     {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"},
 ]
 packaging = [
-    {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"},
-    {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
+    {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
+    {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
 ]
 pluggy = [
     {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
     {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
 ]
 pre-commit = [
-    {file = "pre_commit-2.7.1-py2.py3-none-any.whl", hash = "sha256:810aef2a2ba4f31eed1941fc270e72696a1ad5590b9751839c90807d0fff6b9a"},
-    {file = "pre_commit-2.7.1.tar.gz", hash = "sha256:c54fd3e574565fe128ecc5e7d2f91279772ddb03f8729645fa812fe809084a70"},
+    {file = "pre_commit-2.11.1-py2.py3-none-any.whl", hash = "sha256:94c82f1bf5899d56edb1d926732f4e75a7df29a0c8c092559c77420c9d62428b"},
+    {file = "pre_commit-2.11.1.tar.gz", hash = "sha256:de55c5c72ce80d79106e48beb1b54104d16495ce7f95b0c7b13d4784193a00af"},
 ]
 py = [
-    {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"},
-    {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"},
+    {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
+    {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
 ]
 pyparsing = [
     {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
     {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
 ]
 pytest = [
-    {file = "pytest-6.1.1-py3-none-any.whl", hash = "sha256:7a8190790c17d79a11f847fba0b004ee9a8122582ebff4729a082c109e81a4c9"},
-    {file = "pytest-6.1.1.tar.gz", hash = "sha256:8f593023c1a0f916110285b6efd7f99db07d59546e3d8c36fc60e2ab05d3be92"},
+    {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"},
+    {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"},
 ]
 pytest-cov = [
-    {file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"},
-    {file = "pytest_cov-2.10.1-py2.py3-none-any.whl", hash = "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191"},
+    {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
+    {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
 ]
 pyyaml = [
-    {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"},
-    {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"},
-    {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"},
-    {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"},
-    {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"},
-    {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"},
-    {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"},
-    {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"},
-    {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"},
-    {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"},
-    {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"},
+    {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
+    {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
+    {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
+    {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
+    {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
+    {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
+    {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
+    {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
+    {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
+    {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
+    {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
+    {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
+    {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
 ]
 requests = [
-    {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
-    {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
+    {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
+    {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
 ]
 six = [
     {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
     {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
 ]
 sortedcontainers = [
-    {file = "sortedcontainers-2.2.2-py2.py3-none-any.whl", hash = "sha256:c633ebde8580f241f274c1f8994a665c0e54a17724fecd0cae2f079e09c36d3f"},
-    {file = "sortedcontainers-2.2.2.tar.gz", hash = "sha256:4e73a757831fc3ca4de2859c422564239a31d8213d09a2a666e375807034d2ba"},
+    {file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"},
+    {file = "sortedcontainers-2.3.0.tar.gz", hash = "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"},
 ]
 stringcase = [
     {file = "stringcase-1.2.0.tar.gz", hash = "sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008"},
 ]
 toml = [
-    {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"},
-    {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
+    {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
+    {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
+]
+typing-extensions = [
+    {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
+    {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
+    {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
 ]
 urllib3 = [
-    {file = "urllib3-1.25.11-py2.py3-none-any.whl", hash = "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"},
-    {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"},
+    {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"},
+    {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"},
 ]
 virtualenv = [
-    {file = "virtualenv-20.1.0-py2.py3-none-any.whl", hash = "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2"},
-    {file = "virtualenv-20.1.0.tar.gz", hash = "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"},
+    {file = "virtualenv-20.4.2-py2.py3-none-any.whl", hash = "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"},
+    {file = "virtualenv-20.4.2.tar.gz", hash = "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d"},
 ]
 vulture = [
-    {file = "vulture-2.1-py2.py3-none-any.whl", hash = "sha256:f4b3a422a6cb0285ec9df0ed46241a8577fa53ec3c43f8c70bd2a4d708c95377"},
-    {file = "vulture-2.1.tar.gz", hash = "sha256:933bf7f3848e9e39ecab6a12faa59d5185471c887534abac13baea6fe8138cc2"},
+    {file = "vulture-2.3-py2.py3-none-any.whl", hash = "sha256:f39de5e6f1df1f70c3b50da54f1c8d494159e9ca3d01a9b89eac929600591703"},
+    {file = "vulture-2.3.tar.gz", hash = "sha256:03d5a62bcbe9ceb9a9b0575f42d71a2d414070229f2e6f95fa6e7c71aaaed967"},
 ]
 zipp = [
-    {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"},
-    {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"},
+    {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"},
+    {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"},
 ]
diff --git a/pyproject.toml b/pyproject.toml
index e7bdea9..f605b67 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,7 +7,7 @@ line-length = 120
 
 [tool.poetry]
 name = "zfs-replicate"
-version = "1.3.0"
+version = "1.4.0"
 description = "ZFS Snapshot Replicator"
 authors = [
   "Alex Brandt <alunduil@gmail.com>",
@@ -46,7 +46,7 @@ packages = [
 
 [tool.poetry.dependencies]
 click = "^7.1.2"
-python = "^3.6.1"
+python = "^3.7"
 stringcase = "^1.2.0"
 
 [tool.poetry.dev-dependencies]
diff --git a/zfs/replicate/__init__.py b/zfs/replicate/__init__.py
index bed1a68..1a94ff7 100644
--- a/zfs/replicate/__init__.py
+++ b/zfs/replicate/__init__.py
@@ -1,2 +1,1 @@
-# -*- coding: utf-8 -*-
 """ZFS Replication Functions."""
diff --git a/zfs/replicate/cli/__init__.py b/zfs/replicate/cli/__init__.py
index 2adc202..bd1d184 100644
--- a/zfs/replicate/cli/__init__.py
+++ b/zfs/replicate/cli/__init__.py
@@ -1,2 +1,1 @@
-# -*- coding: utf-8 -*-
 """zfs-replicate CLI interface."""
diff --git a/zfs/replicate/cli/click.py b/zfs/replicate/cli/click.py
index 4af81a3..3093242 100644
--- a/zfs/replicate/cli/click.py
+++ b/zfs/replicate/cli/click.py
@@ -1,5 +1,5 @@
-# -*- coding: utf-8 -*-
 """ZFS Replication Options."""
+from enum import Enum
 from typing import Any
 
 import click
@@ -21,7 +21,10 @@ def __init__(self, enum: Any) -> None:
 
     def convert(self, value: Any, param: Any, ctx: Any) -> Any:
         """Convert string value to Enum value."""
-        value = super().convert(value.name.lower(), param, ctx)
+        if isinstance(value, Enum):
+            value = super().convert(value.name.lower(), param, ctx)
+        else:
+            value = super().convert(value.lower(), param, ctx)
 
         return next(x for x in self.__enum if x.name.lower() == value.lower())
 
diff --git a/zfs/replicate/cli/main.py b/zfs/replicate/cli/main.py
index fde56a2..a94b675 100644
--- a/zfs/replicate/cli/main.py
+++ b/zfs/replicate/cli/main.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Main function zfs-replicate."""
 import itertools
 
@@ -15,17 +14,32 @@
 @click.command()
 @click.option("--verbose", "-v", is_flag=True, help="Print additional output.")
 @click.option(
-    "--dry-run", is_flag=True, help="Generate replication tasks but do not execute them.",
+    "--dry-run",
+    is_flag=True,
+    help="Generate replication tasks but do not execute them.",
 )
 @click.option(
-    "--follow-delete", is_flag=True, help="Delete snapshots on REMOTE_FS that have been deleted from LOCAL_FS.",
+    "--follow-delete",
+    is_flag=True,
+    help="Delete snapshots on REMOTE_FS that have been deleted from LOCAL_FS.",
 )
 @click.option("--recursive", is_flag=True, help="Recursively replicate snapshots.")
 @click.option(
-    "--port", "-p", type=click.IntRange(1, 65535), metavar="PORT", default=22, help="Connect to SSH on PORT.",
+    "--port",
+    "-p",
+    type=click.IntRange(1, 65535),
+    metavar="PORT",
+    default=22,
+    help="Connect to SSH on PORT.",
 )
 @click.option(
-    "--login", "-l", "--user", "-u", "user", metavar="USER", help="Connect to SSH as USER.",
+    "--login",
+    "-l",
+    "--user",
+    "-u",
+    "user",
+    metavar="USER",
+    help="Connect to SSH as USER.",
 )
 @click.option(
     "-i",
@@ -104,5 +118,9 @@ def main(
             (filesystem, list(tasks)) for filesystem, tasks in itertools.groupby(tasks, key=lambda x: x.filesystem)
         ]
         task.execute(
-            remote_fs, filesystem_tasks, follow_delete=follow_delete, compression=compression, ssh_command=ssh_command,
+            remote_fs,
+            filesystem_tasks,
+            follow_delete=follow_delete,
+            compression=compression,
+            ssh_command=ssh_command,
         )
diff --git a/zfs/replicate/compress/__init__.py b/zfs/replicate/compress/__init__.py
index e08ded8..e9be1e6 100644
--- a/zfs/replicate/compress/__init__.py
+++ b/zfs/replicate/compress/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS Replication Stream Compression."""
 from .command import command as command  # noqa: F401
 from .type import Compression as Compression  # noqa: F401
diff --git a/zfs/replicate/compress/command.py b/zfs/replicate/compress/command.py
index 0aab4cd..2e9ee16 100644
--- a/zfs/replicate/compress/command.py
+++ b/zfs/replicate/compress/command.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS Replication Compression Command Mapping."""
 from typing import Tuple
 
@@ -9,5 +8,7 @@ def command(compression: Compression) -> Tuple[str, str]:
     """Compress and decompress command strings for compression."""
     if compression == Compression.LZ4:
         return ("/usr/bin/env - lz4c | ", "/usr/bin/env - lz4c -d | ")
+    elif compression == Compression.OFF:
+        return ("", "")
 
     raise ValueError(f"invalid compression: '{compression}'", compression)
diff --git a/zfs/replicate/compress/type.py b/zfs/replicate/compress/type.py
index b8b34af..8d47f38 100644
--- a/zfs/replicate/compress/type.py
+++ b/zfs/replicate/compress/type.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS Replication Stream Compression."""
 from enum import Enum, auto
 
@@ -7,3 +6,4 @@ class Compression(Enum):
     """Stream Compression Choices."""
 
     LZ4 = auto()
+    OFF = auto()
diff --git a/zfs/replicate/error.py b/zfs/replicate/error.py
index dcf429c..e248f06 100644
--- a/zfs/replicate/error.py
+++ b/zfs/replicate/error.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS Replication Errors."""
 from typing import Any
 
diff --git a/zfs/replicate/filesystem/__init__.py b/zfs/replicate/filesystem/__init__.py
index 5d82e52..1f129d5 100644
--- a/zfs/replicate/filesystem/__init__.py
+++ b/zfs/replicate/filesystem/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS FileSystem Operations."""
 from .create import create as create  # noqa: F401
 from .destroy import destroy as destroy  # noqa: F401
diff --git a/zfs/replicate/filesystem/create.py b/zfs/replicate/filesystem/create.py
index 1f15bca..93239eb 100644
--- a/zfs/replicate/filesystem/create.py
+++ b/zfs/replicate/filesystem/create.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS FileSystem creation."""
 import os.path
 
@@ -37,7 +36,9 @@ def create(filesystem: FileSystem, ssh_command: str) -> None:
                 return  # Ignore this error.
 
             raise ZFSReplicateError(
-                f"unable to create remote dataset: '{filesystem.dataset}': {error!r}", filesystem, error,
+                f"unable to create remote dataset: '{filesystem.dataset}': {error!r}",
+                filesystem,
+                error,
             )
 
 
diff --git a/zfs/replicate/filesystem/destroy.py b/zfs/replicate/filesystem/destroy.py
index caac760..1d2b4ab 100644
--- a/zfs/replicate/filesystem/destroy.py
+++ b/zfs/replicate/filesystem/destroy.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS FileSystem destruction."""
 from .. import subprocess
 from ..error import ZFSReplicateError
@@ -14,7 +13,9 @@ def destroy(filesystem: FileSystem, ssh_command: str) -> None:
     _, error = proc.communicate()
     if proc.returncode:
         raise ZFSReplicateError(
-            f"unable to destroy dataset: '{filesystem.dataset}': {error!r}", filesystem, error,
+            f"unable to destroy dataset: '{filesystem.dataset}': {error!r}",
+            filesystem,
+            error,
         )
 
 
diff --git a/zfs/replicate/filesystem/list.py b/zfs/replicate/filesystem/list.py
index 16e18e1..d02c2bf 100644
--- a/zfs/replicate/filesystem/list.py
+++ b/zfs/replicate/filesystem/list.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFs FileSystem List."""
 import re
 from typing import List
@@ -25,7 +24,9 @@ def list(filesystem: FileSystem, ssh_command: str) -> List[FileSystem]:
 
     if proc.returncode:
         raise ZFSReplicateError(
-            f"error encountered while listing filesystems of '{filesystem.name}': {error!r}", filesystem, error,
+            f"error encountered while listing filesystems of '{filesystem.name}': {error!r}",
+            filesystem,
+            error,
         )
 
     return _filesystems(output)
diff --git a/zfs/replicate/filesystem/type.py b/zfs/replicate/filesystem/type.py
index e9c6c0f..d8423b2 100644
--- a/zfs/replicate/filesystem/type.py
+++ b/zfs/replicate/filesystem/type.py
@@ -1,9 +1,9 @@
-# -*- coding: utf-8 -*-
 """ZFS FileySystem Type."""
-from typing import NamedTuple
+from dataclasses import dataclass
 
 
-class FileSystem(NamedTuple):
+@dataclass(order=True, frozen=True)
+class FileSystem:
     """FileSystem Type."""
 
     dataset: str
diff --git a/zfs/replicate/list.py b/zfs/replicate/list.py
index 6484cc4..2811235 100644
--- a/zfs/replicate/list.py
+++ b/zfs/replicate/list.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Common List Functions."""
 from typing import List, Sequence, Tuple, TypeVar
 
@@ -19,7 +18,8 @@ def inits(elements: Sequence[ElementType]) -> Sequence[Sequence[ElementType]]:
 
 
 def venn(
-    lefts: List[ElementType], rights: List[ElementType],
+    lefts: List[ElementType],
+    rights: List[ElementType],
 ) -> Tuple[List[ElementType], List[ElementType], List[ElementType]]:
     """Calculate venn diagram of the two sequences.
 
diff --git a/zfs/replicate/optional.py b/zfs/replicate/optional.py
index 0a6b933..e894817 100644
--- a/zfs/replicate/optional.py
+++ b/zfs/replicate/optional.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Optional Functions."""
 from typing import Optional, TypeVar
 
diff --git a/zfs/replicate/snapshot/__init__.py b/zfs/replicate/snapshot/__init__.py
index 515c1c1..3e5f57f 100644
--- a/zfs/replicate/snapshot/__init__.py
+++ b/zfs/replicate/snapshot/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS Snapshot Operations."""
 from .destroy import destroy as destroy  # noqa: F401
 from .list import list as list  # noqa: F401
diff --git a/zfs/replicate/snapshot/destroy.py b/zfs/replicate/snapshot/destroy.py
index b5c8a07..89b7acf 100644
--- a/zfs/replicate/snapshot/destroy.py
+++ b/zfs/replicate/snapshot/destroy.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS Snapshot destruction."""
 from .. import subprocess
 from ..error import ZFSReplicateError
@@ -14,7 +13,9 @@ def destroy(snapshot: Snapshot, ssh_command: str) -> None:
     _, error = proc.communicate()
     if proc.returncode:
         raise ZFSReplicateError(
-            f"unable to destroy snapshot: '{snapshot.filesystem.name}@{snapshot.name}': {error!r}", snapshot, error,
+            f"unable to destroy snapshot: '{snapshot.filesystem.name}@{snapshot.name}': {error!r}",
+            snapshot,
+            error,
         )
 
 
diff --git a/zfs/replicate/snapshot/list.py b/zfs/replicate/snapshot/list.py
index 6a8a15d..a4f9f45 100644
--- a/zfs/replicate/snapshot/list.py
+++ b/zfs/replicate/snapshot/list.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS Snapshot listing."""
 from typing import List, Optional
 
@@ -22,7 +21,9 @@ def list(filesystem: FileSystem, recursive: bool, ssh_command: Optional[str] = N
 
     if proc.returncode:
         raise ZFSReplicateError(
-            f"error encountered while listing snapshots of '{filesystem.name}': {error!r}", filesystem, error,
+            f"error encountered while listing snapshots of '{filesystem.name}': {error!r}",
+            filesystem,
+            error,
         )
 
     return _snapshots(output)
@@ -66,5 +67,8 @@ def _add_previous(snapshot: Snapshot, previous: Optional[Snapshot] = None) -> Sn
         previous = None
 
     return Snapshot(
-        filesystem=snapshot.filesystem, name=snapshot.name, previous=previous, timestamp=snapshot.timestamp,
+        filesystem=snapshot.filesystem,
+        name=snapshot.name,
+        previous=previous,
+        timestamp=snapshot.timestamp,
     )
diff --git a/zfs/replicate/snapshot/send.py b/zfs/replicate/snapshot/send.py
index 23c3eb3..f48993c 100644
--- a/zfs/replicate/snapshot/send.py
+++ b/zfs/replicate/snapshot/send.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """ZFS Snapshot Send."""
 import subprocess
 from typing import Optional
@@ -36,7 +35,9 @@ def send(
             return  # Ignore this error.
 
         raise ZFSReplicateError(
-            f"failed to create snapshot: '{current.filesystem.name}@{current.name}': {error!r}", current, error,
+            f"failed to create snapshot: '{current.filesystem.name}@{current.name}': {error!r}",
+            current,
+            error,
         )
 
 
diff --git a/zfs/replicate/snapshot/type.py b/zfs/replicate/snapshot/type.py
index 166eef0..11a86e9 100644
--- a/zfs/replicate/snapshot/type.py
+++ b/zfs/replicate/snapshot/type.py
@@ -1,20 +1,24 @@
-# -*- coding: utf-8 -*-
 """ZFS Snapshot Type."""
-from typing import Any, NamedTuple, Optional
+from dataclasses import dataclass
+from typing import Optional
 
 from ..filesystem import FileSystem
 
 
-class Snapshot(NamedTuple):
+@dataclass(frozen=True)
+class Snapshot:
     """ZFS Snapshot Type."""
 
     filesystem: FileSystem
     name: str
-    previous: Optional[Any]  # mypy doesn't support nested types yet
+    previous: Optional["Snapshot"]
     timestamp: int
 
-    def __eq__(self, other: Any) -> bool:
+    def __eq__(self, other: object) -> bool:
         """Equality of Snapshots."""
+        if other is None:
+            return False
+
         if not isinstance(other, Snapshot):
             raise NotImplementedError
 
diff --git a/zfs/replicate/ssh/__init__.py b/zfs/replicate/ssh/__init__.py
index a3872a1..9c9d16a 100644
--- a/zfs/replicate/ssh/__init__.py
+++ b/zfs/replicate/ssh/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """SSH commands and types."""
 from .cipher import Cipher as Cipher  # noqa: F401
 from .command import command as command  # noqa: F401
diff --git a/zfs/replicate/ssh/cipher.py b/zfs/replicate/ssh/cipher.py
index 35de0df..4188d41 100644
--- a/zfs/replicate/ssh/cipher.py
+++ b/zfs/replicate/ssh/cipher.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """SSH Cipher Type."""
 from enum import Enum, auto
 
diff --git a/zfs/replicate/ssh/command.py b/zfs/replicate/ssh/command.py
index d9ec004..06c3847 100644
--- a/zfs/replicate/ssh/command.py
+++ b/zfs/replicate/ssh/command.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """SSH Command Generator."""
 from .cipher import Cipher
 
diff --git a/zfs/replicate/subprocess.py b/zfs/replicate/subprocess.py
index 7c7144d..f6a1763 100644
--- a/zfs/replicate/subprocess.py
+++ b/zfs/replicate/subprocess.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """subprocess wrapper."""
 import shlex
 import subprocess
diff --git a/zfs/replicate/task/__init__.py b/zfs/replicate/task/__init__.py
index 3f4730a..bd74072 100644
--- a/zfs/replicate/task/__init__.py
+++ b/zfs/replicate/task/__init__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Task Functions."""
 from .execute import execute as execute  # noqa: F401
 from .generate import generate as generate  # noqa: F401
diff --git a/zfs/replicate/task/execute.py b/zfs/replicate/task/execute.py
index 0e329d7..78d7fa7 100644
--- a/zfs/replicate/task/execute.py
+++ b/zfs/replicate/task/execute.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Task Execution."""
 import itertools
 from typing import List, Tuple
@@ -32,7 +31,11 @@ def execute(
                 _destroy(a_tasks, ssh_command=ssh_command)
             elif action == Action.SEND:
                 _send(
-                    remote, a_tasks, ssh_command=ssh_command, follow_delete=follow_delete, compression=compression,
+                    remote,
+                    a_tasks,
+                    ssh_command=ssh_command,
+                    follow_delete=follow_delete,
+                    compression=compression,
                 )
 
 
@@ -50,7 +53,11 @@ def _destroy(tasks: List[Task], ssh_command: str) -> None:
 
 
 def _send(
-    remote: FileSystem, tasks: List[Task], ssh_command: str, follow_delete: bool, compression: Compression,
+    remote: FileSystem,
+    tasks: List[Task],
+    ssh_command: str,
+    follow_delete: bool,
+    compression: Compression,
 ) -> None:
     for task in tasks:
         snapshot.send(
diff --git a/zfs/replicate/task/generate.py b/zfs/replicate/task/generate.py
index 23f8a9f..4833286 100644
--- a/zfs/replicate/task/generate.py
+++ b/zfs/replicate/task/generate.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Replication Tasks."""
 from typing import Dict, List
 
diff --git a/zfs/replicate/task/report.py b/zfs/replicate/task/report.py
index 720c84c..2eb8c8b 100644
--- a/zfs/replicate/task/report.py
+++ b/zfs/replicate/task/report.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Task Reporting Functions."""
 import itertools
 from typing import List, Tuple
diff --git a/zfs/replicate/task/type.py b/zfs/replicate/task/type.py
index 91048bb..ff04078 100644
--- a/zfs/replicate/task/type.py
+++ b/zfs/replicate/task/type.py
@@ -1,7 +1,7 @@
-# -*- coding: utf-8 -*-
 """Types for Tasks."""
+from dataclasses import dataclass
 from enum import Enum, auto
-from typing import NamedTuple, Optional
+from typing import Optional
 
 from ..filesystem import FileSystem
 from ..snapshot import Snapshot
@@ -15,7 +15,8 @@ class Action(Enum):
     SEND = auto()
 
 
-class Task(NamedTuple):
+@dataclass
+class Task:
     """Task Type."""
 
     action: Action


Test patch:
diff --git a/zfs_test/__init__.py b/zfs_test/__init__.py
index 1cd8b49..10b5cb6 100644
--- a/zfs_test/__init__.py
+++ b/zfs_test/__init__.py
@@ -1,2 +1,1 @@
-# -*- coding: utf-8 -*-
 """zfs tests."""
diff --git a/zfs_test/replicate_test/__init__.py b/zfs_test/replicate_test/__init__.py
index 2eab648..ea3228b 100644
--- a/zfs_test/replicate_test/__init__.py
+++ b/zfs_test/replicate_test/__init__.py
@@ -1,2 +1,1 @@
-# -*- coding: utf-8 -*-
 """zfs.replicate tests."""
diff --git a/zfs_test/replicate_test/cli_test/main_test.py b/zfs_test/replicate_test/cli_test/main_test.py
index 0d29c42..c41d091 100644
--- a/zfs_test/replicate_test/cli_test/main_test.py
+++ b/zfs_test/replicate_test/cli_test/main_test.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """zfs.replicate.cli.main tests."""
 from click.testing import CliRunner
 
diff --git a/zfs_test/replicate_test/compress_test/__init__.py b/zfs_test/replicate_test/compress_test/__init__.py
new file mode 100644
index 0000000..2f8b929
--- /dev/null
+++ b/zfs_test/replicate_test/compress_test/__init__.py
@@ -0,0 +1,1 @@
+"""Test compress functionality."""
diff --git a/zfs_test/replicate_test/compress_test/command_test.py b/zfs_test/replicate_test/compress_test/command_test.py
new file mode 100644
index 0000000..6f1e426
--- /dev/null
+++ b/zfs_test/replicate_test/compress_test/command_test.py
@@ -0,0 +1,14 @@
+"""Test compress command generation."""
+import pytest
+
+from zfs.replicate.compress.command import command
+from zfs.replicate.compress.type import Compression
+
+
+def test_command_total() -> None:
+    """Ensure command is a total function."""
+    for compression in Compression:
+        try:
+            command(compression)
+        except ValueError:
+            pytest.fail(f"unhandled case for {compression}")
diff --git a/zfs_test/replicate_test/list_test.py b/zfs_test/replicate_test/list_test.py
index 456ad13..c54f71d 100644
--- a/zfs_test/replicate_test/list_test.py
+++ b/zfs_test/replicate_test/list_test.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Tests for zfs.replicate.list."""
 from typing import List, Set
 
diff --git a/zfs_test/replicate_test/optional_test.py b/zfs_test/replicate_test/optional_test.py
index f951945..864abf0 100644
--- a/zfs_test/replicate_test/optional_test.py
+++ b/zfs_test/replicate_test/optional_test.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Tests for zfs.replicate.optional."""
 from hypothesis import given
 from hypothesis.strategies import integers
diff --git a/zfs_test/replicate_test/snapshot_test/__init__.py b/zfs_test/replicate_test/snapshot_test/__init__.py
index 9caf4cb..0356b4f 100644
--- a/zfs_test/replicate_test/snapshot_test/__init__.py
+++ b/zfs_test/replicate_test/snapshot_test/__init__.py
@@ -1,2 +1,1 @@
-# -*- coding: utf-8 -*-
 """zfs.replicate.snapshot tests."""
diff --git a/zfs_test/replicate_test/snapshot_test/list_test.py b/zfs_test/replicate_test/snapshot_test/list_test.py
index 37cdbc4..004db12 100644
--- a/zfs_test/replicate_test/snapshot_test/list_test.py
+++ b/zfs_test/replicate_test/snapshot_test/list_test.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """zfs.replicate.snapshot tests."""
 from typing import List
 
diff --git a/zfs_test/replicate_test/snapshot_test/strategies.py b/zfs_test/replicate_test/snapshot_test/strategies.py
index 4ddb7f2..30500e1 100644
--- a/zfs_test/replicate_test/snapshot_test/strategies.py
+++ b/zfs_test/replicate_test/snapshot_test/strategies.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """Snapshot Hypothesis Strategies."""
 import string
 from typing import Any, Dict
diff --git a/zfs_test/replicate_test/snapshot_test/type_test.py b/zfs_test/replicate_test/snapshot_test/type_test.py
index 8e20657..ad451b3 100644
--- a/zfs_test/replicate_test/snapshot_test/type_test.py
+++ b/zfs_test/replicate_test/snapshot_test/type_test.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """zfs.replicate.snapshot.type tests."""
 from zfs.replicate.filesystem.type import filesystem
 from zfs.replicate.snapshot.type import Snapshot
diff --git a/zfs_test/replicate_test/task_test/generate_test.py b/zfs_test/replicate_test/task_test/generate_test.py
index 0d7d861..bd0cf37 100644
--- a/zfs_test/replicate_test/task_test/generate_test.py
+++ b/zfs_test/replicate_test/task_test/generate_test.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 """zfs.replicate.task.generate tests."""
 import itertools
 import operator
@@ -25,7 +24,8 @@ def test_empty_remotes(snapshots: List[Snapshot]) -> None:
     snapshots_by_fs = {
         k: list(v)
         for (k, v) in itertools.groupby(
-            sorted(snapshots, key=operator.attrgetter("filesystem")), key=operator.attrgetter("filesystem"),
+            sorted(snapshots, key=operator.attrgetter("filesystem")),
+            key=operator.attrgetter("filesystem"),
         )
     }
 
@@ -43,7 +43,8 @@ def test_empty_locals(snapshots: List[Snapshot]) -> None:
     snapshots_by_fs = {
         k: list(v)
         for (k, v) in itertools.groupby(
-            sorted(snapshots, key=operator.attrgetter("filesystem")), key=operator.attrgetter("filesystem"),
+            sorted(snapshots, key=operator.attrgetter("filesystem")),
+            key=operator.attrgetter("filesystem"),
         )
     }
 
diff --git a/zfs_test/replicate_test/task_test/report_test.py b/zfs_test/replicate_test/task_test/report_test.py
new file mode 100644
index 0000000..116c1f5
--- /dev/null
+++ b/zfs_test/replicate_test/task_test/report_test.py
@@ -0,0 +1,21 @@
+"""zfs.replicate.task.report tests."""
+
+from typing import List
+
+from hypothesis import given
+from hypothesis.strategies import builds, lists
+
+from zfs.replicate.task import report
+from zfs.replicate.task.type import Task
+
+
+def test_empty_tasks() -> None:
+    """Ensure no actions is an empty report."""
+    assert "" == report([])
+
+
+@given(tasks=lists(builds(Task), min_size=1))
+def test_nonempty_tasks(tasks: List[Task]) -> None:
+    """Ensure nonempty report from nonempty actions."""
+    result = report(tasks)
+    assert "" != result