drop python3.6 support

python 3.6 reached end of life on 2021-12-23

Committed via https://github.com/asottile/all-repos
This commit is contained in:
Anthony Sottile 2022-01-15 19:24:05 -05:00
parent 2aef4c777f
commit 8f6152921e
76 changed files with 229 additions and 145 deletions

View file

@ -25,12 +25,12 @@ repos:
rev: v2.6.0 rev: v2.6.0
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
args: [--py3-plus] args: [--py37-plus, --add-import, 'from __future__ import annotations']
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.31.0 rev: v2.31.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py36-plus] args: [--py37-plus]
- repo: https://github.com/asottile/add-trailing-comma - repo: https://github.com/asottile/add-trailing-comma
rev: v2.2.1 rev: v2.2.1
hooks: hooks:

View file

@ -10,7 +10,7 @@ resources:
type: github type: github
endpoint: github endpoint: github
name: asottile/azure-pipeline-templates name: asottile/azure-pipeline-templates
ref: refs/tags/v2.1.0 ref: refs/tags/v2.4.0
jobs: jobs:
- template: job--python-tox.yml@asottile - template: job--python-tox.yml@asottile
@ -19,5 +19,5 @@ jobs:
os: windows os: windows
- template: job--python-tox.yml@asottile - template: job--python-tox.yml@asottile
parameters: parameters:
toxenvs: [pypy3, py36, py37, py38] toxenvs: [py37, py38, py39, py310]
os: linux os: linux

View file

@ -1,16 +1,16 @@
from __future__ import annotations
import argparse import argparse
import math import math
import os import os
import subprocess import subprocess
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set
from pre_commit_hooks.util import added_files from pre_commit_hooks.util import added_files
from pre_commit_hooks.util import zsplit from pre_commit_hooks.util import zsplit
def filter_lfs_files(filenames: Set[str]) -> None: # pragma: no cover (lfs) def filter_lfs_files(filenames: set[str]) -> None: # pragma: no cover (lfs)
"""Remove files tracked by git-lfs from the set.""" """Remove files tracked by git-lfs from the set."""
if not filenames: if not filenames:
return return
@ -54,7 +54,7 @@ def find_large_added_files(
return retv return retv
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'filenames', nargs='*', 'filenames', nargs='*',

View file

@ -1,13 +1,14 @@
from __future__ import annotations
import argparse import argparse
import ast import ast
import platform import platform
import sys import sys
import traceback import traceback
from typing import Optional
from typing import Sequence from typing import Sequence
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,10 +1,9 @@
from __future__ import annotations
import argparse import argparse
import ast import ast
from typing import List
from typing import NamedTuple from typing import NamedTuple
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set
BUILTIN_TYPES = { BUILTIN_TYPES = {
@ -27,10 +26,10 @@ class Call(NamedTuple):
class Visitor(ast.NodeVisitor): class Visitor(ast.NodeVisitor):
def __init__( def __init__(
self, self,
ignore: Optional[Sequence[str]] = None, ignore: Sequence[str] | None = None,
allow_dict_kwargs: bool = True, allow_dict_kwargs: bool = True,
) -> None: ) -> None:
self.builtin_type_calls: List[Call] = [] self.builtin_type_calls: list[Call] = []
self.ignore = set(ignore) if ignore else set() self.ignore = set(ignore) if ignore else set()
self.allow_dict_kwargs = allow_dict_kwargs self.allow_dict_kwargs = allow_dict_kwargs
@ -56,9 +55,9 @@ class Visitor(ast.NodeVisitor):
def check_file( def check_file(
filename: str, filename: str,
ignore: Optional[Sequence[str]] = None, ignore: Sequence[str] | None = None,
allow_dict_kwargs: bool = True, allow_dict_kwargs: bool = True,
) -> List[Call]: ) -> list[Call]:
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
tree = ast.parse(f.read(), filename=filename) tree = ast.parse(f.read(), filename=filename)
visitor = Visitor(ignore=ignore, allow_dict_kwargs=allow_dict_kwargs) visitor = Visitor(ignore=ignore, allow_dict_kwargs=allow_dict_kwargs)
@ -66,11 +65,11 @@ def check_file(
return visitor.builtin_type_calls return visitor.builtin_type_calls
def parse_ignore(value: str) -> Set[str]: def parse_ignore(value: str) -> set[str]:
return set(value.split(',')) return set(value.split(','))
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
parser.add_argument('--ignore', type=parse_ignore, default=set()) parser.add_argument('--ignore', type=parse_ignore, default=set())

View file

@ -1,9 +1,10 @@
from __future__ import annotations
import argparse import argparse
from typing import Optional
from typing import Sequence from typing import Sequence
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check') parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,15 +1,15 @@
from __future__ import annotations
import argparse import argparse
from typing import Iterable from typing import Iterable
from typing import Iterator from typing import Iterator
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set
from pre_commit_hooks.util import added_files from pre_commit_hooks.util import added_files
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output
def lower_set(iterable: Iterable[str]) -> Set[str]: def lower_set(iterable: Iterable[str]) -> set[str]:
return {x.lower() for x in iterable} return {x.lower() for x in iterable}
@ -21,7 +21,7 @@ def parents(file: str) -> Iterator[str]:
path_parts.pop() path_parts.pop()
def directories_for(files: Set[str]) -> Set[str]: def directories_for(files: set[str]) -> set[str]:
return {parent for file in files for parent in parents(file)} return {parent for file in files for parent in parents(file)}
@ -56,7 +56,7 @@ def find_conflicting_filenames(filenames: Sequence[str]) -> int:
return retv return retv
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'filenames', nargs='*', 'filenames', nargs='*',

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import argparse import argparse
import io import io
import tokenize import tokenize
from tokenize import tokenize as tokenize_tokenize from tokenize import tokenize as tokenize_tokenize
from typing import Optional
from typing import Sequence from typing import Sequence
NON_CODE_TOKENS = frozenset(( NON_CODE_TOKENS = frozenset((
@ -45,7 +46,7 @@ def check_docstring_first(src: bytes, filename: str = '<unknown>') -> int:
return 0 return 0
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,13 +1,12 @@
"""Check that executable text files have a shebang.""" """Check that executable text files have a shebang."""
from __future__ import annotations
import argparse import argparse
import shlex import shlex
import sys import sys
from typing import Generator from typing import Generator
from typing import List
from typing import NamedTuple from typing import NamedTuple
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output
from pre_commit_hooks.util import zsplit from pre_commit_hooks.util import zsplit
@ -15,7 +14,7 @@ from pre_commit_hooks.util import zsplit
EXECUTABLE_VALUES = frozenset(('1', '3', '5', '7')) EXECUTABLE_VALUES = frozenset(('1', '3', '5', '7'))
def check_executables(paths: List[str]) -> int: def check_executables(paths: list[str]) -> int:
if sys.platform == 'win32': # pragma: win32 cover if sys.platform == 'win32': # pragma: win32 cover
return _check_git_filemode(paths) return _check_git_filemode(paths)
else: # pragma: win32 no cover else: # pragma: win32 no cover
@ -42,7 +41,7 @@ def git_ls_files(paths: Sequence[str]) -> Generator[GitLsFile, None, None]:
def _check_git_filemode(paths: Sequence[str]) -> int: def _check_git_filemode(paths: Sequence[str]) -> int:
seen: Set[str] = set() seen: set[str] = set()
for ls_file in git_ls_files(paths): for ls_file in git_ls_files(paths):
is_executable = any(b in EXECUTABLE_VALUES for b in ls_file.mode[-3:]) is_executable = any(b in EXECUTABLE_VALUES for b in ls_file.mode[-3:])
if is_executable and not has_shebang(ls_file.filename): if is_executable and not has_shebang(ls_file.filename):
@ -71,7 +70,7 @@ def _message(path: str) -> None:
) )
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,16 +1,14 @@
from __future__ import annotations
import argparse import argparse
import json import json
from typing import Any from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Tuple
def raise_duplicate_keys( def raise_duplicate_keys(
ordered_pairs: List[Tuple[str, Any]], ordered_pairs: list[tuple[str, Any]],
) -> Dict[str, Any]: ) -> dict[str, Any]:
d = {} d = {}
for key, val in ordered_pairs: for key, val in ordered_pairs:
if key in d: if key in d:
@ -20,7 +18,7 @@ def raise_duplicate_keys(
return d return d
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check.') parser.add_argument('filenames', nargs='*', help='Filenames to check.')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import argparse import argparse
import os.path import os.path
from typing import Optional
from typing import Sequence from typing import Sequence
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output
@ -26,7 +27,7 @@ def is_in_merge() -> bool:
) )
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
parser.add_argument('--assume-in-merge', action='store_true') parser.add_argument('--assume-in-merge', action='store_true')

View file

@ -1,18 +1,17 @@
"""Check that text files with a shebang are executable.""" """Check that text files with a shebang are executable."""
from __future__ import annotations
import argparse import argparse
import shlex import shlex
import sys import sys
from typing import List
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set
from pre_commit_hooks.check_executables_have_shebangs import EXECUTABLE_VALUES from pre_commit_hooks.check_executables_have_shebangs import EXECUTABLE_VALUES
from pre_commit_hooks.check_executables_have_shebangs import git_ls_files from pre_commit_hooks.check_executables_have_shebangs import git_ls_files
from pre_commit_hooks.check_executables_have_shebangs import has_shebang from pre_commit_hooks.check_executables_have_shebangs import has_shebang
def check_shebangs(paths: List[str]) -> int: def check_shebangs(paths: list[str]) -> int:
# Cannot optimize on non-executability here if we intend this check to # Cannot optimize on non-executability here if we intend this check to
# work on win32 -- and that's where problems caused by non-executability # work on win32 -- and that's where problems caused by non-executability
# (elsewhere) are most likely to arise from. # (elsewhere) are most likely to arise from.
@ -20,7 +19,7 @@ def check_shebangs(paths: List[str]) -> int:
def _check_git_filemode(paths: Sequence[str]) -> int: def _check_git_filemode(paths: Sequence[str]) -> int:
seen: Set[str] = set() seen: set[str] = set()
for ls_file in git_ls_files(paths): for ls_file in git_ls_files(paths):
is_executable = any(b in EXECUTABLE_VALUES for b in ls_file.mode[-3:]) is_executable = any(b in EXECUTABLE_VALUES for b in ls_file.mode[-3:])
if not is_executable and has_shebang(ls_file.filename): if not is_executable and has_shebang(ls_file.filename):
@ -41,7 +40,7 @@ def _message(path: str) -> None:
) )
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,10 +1,11 @@
from __future__ import annotations
import argparse import argparse
import os.path import os.path
from typing import Optional
from typing import Sequence from typing import Sequence
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser(description='Checks for broken symlinks.') parser = argparse.ArgumentParser(description='Checks for broken symlinks.')
parser.add_argument('filenames', nargs='*', help='Filenames to check') parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,11 +1,12 @@
from __future__ import annotations
import argparse import argparse
from typing import Optional
from typing import Sequence from typing import Sequence
import toml import toml
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check.') parser.add_argument('filenames', nargs='*', help='Filenames to check.')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,8 +1,8 @@
from __future__ import annotations
import argparse import argparse
import re import re
import sys import sys
from typing import List
from typing import Optional
from typing import Pattern from typing import Pattern
from typing import Sequence from typing import Sequence
@ -15,7 +15,7 @@ def _get_pattern(domain: str) -> Pattern[bytes]:
return re.compile(regex.encode()) return re.compile(regex.encode())
def _check_filename(filename: str, patterns: List[Pattern[bytes]]) -> int: def _check_filename(filename: str, patterns: list[Pattern[bytes]]) -> int:
retv = 0 retv = 0
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
for i, line in enumerate(f, 1): for i, line in enumerate(f, 1):
@ -28,7 +28,7 @@ def _check_filename(filename: str, patterns: List[Pattern[bytes]]) -> int:
return retv return retv
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
parser.add_argument( parser.add_argument(

View file

@ -1,10 +1,11 @@
from __future__ import annotations
import argparse import argparse
import xml.sax.handler import xml.sax.handler
from typing import Optional
from typing import Sequence from typing import Sequence
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='XML filenames to check.') parser.add_argument('filenames', nargs='*', help='XML filenames to check.')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import argparse import argparse
from typing import Any from typing import Any
from typing import Generator from typing import Generator
from typing import NamedTuple from typing import NamedTuple
from typing import Optional
from typing import Sequence from typing import Sequence
import ruamel.yaml import ruamel.yaml
@ -36,7 +37,7 @@ LOAD_FNS = {
} }
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'-m', '--multi', '--allow-multiple-documents', action='store_true', '-m', '--multi', '--allow-multiple-documents', action='store_true',

View file

@ -1,9 +1,9 @@
from __future__ import annotations
import argparse import argparse
import ast import ast
import traceback import traceback
from typing import List
from typing import NamedTuple from typing import NamedTuple
from typing import Optional
from typing import Sequence from typing import Sequence
@ -29,7 +29,7 @@ class Debug(NamedTuple):
class DebugStatementParser(ast.NodeVisitor): class DebugStatementParser(ast.NodeVisitor):
def __init__(self) -> None: def __init__(self) -> None:
self.breakpoints: List[Debug] = [] self.breakpoints: list[Debug] = []
def visit_Import(self, node: ast.Import) -> None: def visit_Import(self, node: ast.Import) -> None:
for name in node.names: for name in node.names:
@ -70,7 +70,7 @@ def check_file(filename: str) -> int:
return int(bool(visitor.breakpoints)) return int(bool(visitor.breakpoints))
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to run') parser.add_argument('filenames', nargs='*', help='Filenames to run')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,8 +1,8 @@
from __future__ import annotations
import argparse import argparse
import shlex import shlex
import subprocess import subprocess
from typing import List
from typing import Optional
from typing import Sequence from typing import Sequence
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output
@ -13,8 +13,8 @@ PERMS_LINK = '120000'
PERMS_NONEXIST = '000000' PERMS_NONEXIST = '000000'
def find_destroyed_symlinks(files: Sequence[str]) -> List[str]: def find_destroyed_symlinks(files: Sequence[str]) -> list[str]:
destroyed_links: List[str] = [] destroyed_links: list[str] = []
if not files: if not files:
return destroyed_links return destroyed_links
for line in zsplit( for line in zsplit(
@ -66,7 +66,7 @@ def find_destroyed_symlinks(files: Sequence[str]) -> List[str]:
return destroyed_links return destroyed_links
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check.') parser.add_argument('filenames', nargs='*', help='Filenames to check.')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,11 +1,10 @@
from __future__ import annotations
import argparse import argparse
import configparser import configparser
import os import os
from typing import List
from typing import NamedTuple from typing import NamedTuple
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set
class BadFile(NamedTuple): class BadFile(NamedTuple):
@ -13,7 +12,7 @@ class BadFile(NamedTuple):
key: str key: str
def get_aws_cred_files_from_env() -> Set[str]: def get_aws_cred_files_from_env() -> set[str]:
"""Extract credential file paths from environment variables.""" """Extract credential file paths from environment variables."""
return { return {
os.environ[env_var] os.environ[env_var]
@ -25,7 +24,7 @@ def get_aws_cred_files_from_env() -> Set[str]:
} }
def get_aws_secrets_from_env() -> Set[str]: def get_aws_secrets_from_env() -> set[str]:
"""Extract AWS secrets from environment variables.""" """Extract AWS secrets from environment variables."""
keys = set() keys = set()
for env_var in ( for env_var in (
@ -36,7 +35,7 @@ def get_aws_secrets_from_env() -> Set[str]:
return keys return keys
def get_aws_secrets_from_file(credentials_file: str) -> Set[str]: def get_aws_secrets_from_file(credentials_file: str) -> set[str]:
"""Extract AWS secrets from configuration files. """Extract AWS secrets from configuration files.
Read an ini-style configuration file and return a set with all found AWS Read an ini-style configuration file and return a set with all found AWS
@ -69,8 +68,8 @@ def get_aws_secrets_from_file(credentials_file: str) -> Set[str]:
def check_file_for_aws_keys( def check_file_for_aws_keys(
filenames: Sequence[str], filenames: Sequence[str],
keys: Set[bytes], keys: set[bytes],
) -> List[BadFile]: ) -> list[BadFile]:
"""Check if files contain AWS secrets. """Check if files contain AWS secrets.
Return a list of all files containing AWS secrets and keys found, with all Return a list of all files containing AWS secrets and keys found, with all
@ -90,7 +89,7 @@ def check_file_for_aws_keys(
return bad_files return bad_files
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='+', help='Filenames to run') parser.add_argument('filenames', nargs='+', help='Filenames to run')
parser.add_argument( parser.add_argument(
@ -119,7 +118,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
# of files to to gather AWS secrets from. # of files to to gather AWS secrets from.
credential_files |= get_aws_cred_files_from_env() credential_files |= get_aws_cred_files_from_env()
keys: Set[str] = set() keys: set[str] = set()
for credential_file in credential_files: for credential_file in credential_files:
keys |= get_aws_secrets_from_file(credential_file) keys |= get_aws_secrets_from_file(credential_file)

View file

@ -1,5 +1,6 @@
from __future__ import annotations
import argparse import argparse
from typing import Optional
from typing import Sequence from typing import Sequence
BLACKLIST = [ BLACKLIST = [
@ -16,7 +17,7 @@ BLACKLIST = [
] ]
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check') parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,7 +1,8 @@
from __future__ import annotations
import argparse import argparse
import os import os
from typing import IO from typing import IO
from typing import Optional
from typing import Sequence from typing import Sequence
@ -48,7 +49,7 @@ def fix_file(file_obj: IO[bytes]) -> int:
return 0 return 0
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to fix') parser.add_argument('filenames', nargs='*', help='Filenames to fix')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -9,12 +9,13 @@ per line. Various users are adding/removing lines from this file; using
this hook on that file should reduce the instances of git merge this hook on that file should reduce the instances of git merge
conflicts and keep the file nicely ordered. conflicts and keep the file nicely ordered.
""" """
from __future__ import annotations
import argparse import argparse
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import IO from typing import IO
from typing import Iterable from typing import Iterable
from typing import Optional
from typing import Sequence from typing import Sequence
PASS = 0 PASS = 0
@ -23,7 +24,7 @@ FAIL = 1
def sort_file_contents( def sort_file_contents(
f: IO[bytes], f: IO[bytes],
key: Optional[Callable[[bytes], Any]], key: Callable[[bytes], Any] | None,
*, *,
unique: bool = False, unique: bool = False,
) -> int: ) -> int:
@ -47,7 +48,7 @@ def sort_file_contents(
return FAIL return FAIL
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='+', help='Files to sort') parser.add_argument('filenames', nargs='+', help='Files to sort')
parser.add_argument( parser.add_argument(

View file

@ -1,9 +1,10 @@
from __future__ import annotations
import argparse import argparse
from typing import Optional
from typing import Sequence from typing import Sequence
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check') parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,7 +1,8 @@
from __future__ import annotations
import argparse import argparse
from typing import IO from typing import IO
from typing import NamedTuple from typing import NamedTuple
from typing import Optional
from typing import Sequence from typing import Sequence
DEFAULT_PRAGMA = b'# -*- coding: utf-8 -*-' DEFAULT_PRAGMA = b'# -*- coding: utf-8 -*-'
@ -26,7 +27,7 @@ class ExpectedContents(NamedTuple):
# True: has exactly the coding pragma expected # True: has exactly the coding pragma expected
# False: missing coding pragma entirely # False: missing coding pragma entirely
# None: has a coding pragma, but it does not match # None: has a coding pragma, but it does not match
pragma_status: Optional[bool] pragma_status: bool | None
ending: bytes ending: bytes
@property @property
@ -55,7 +56,7 @@ def _get_expected_contents(
rest = second_line + rest rest = second_line + rest
if potential_coding.rstrip(b'\r\n') == expected_pragma: if potential_coding.rstrip(b'\r\n') == expected_pragma:
pragma_status: Optional[bool] = True pragma_status: bool | None = True
elif has_coding(potential_coding): elif has_coding(potential_coding):
pragma_status = None pragma_status = None
else: else:
@ -105,7 +106,7 @@ def _normalize_pragma(pragma: str) -> bytes:
return pragma.encode().rstrip() return pragma.encode().rstrip()
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
'Fixes the encoding pragma of python files', 'Fixes the encoding pragma of python files',
) )

View file

@ -1,12 +1,13 @@
from __future__ import annotations
import argparse import argparse
import os import os
from typing import Optional
from typing import Sequence from typing import Sequence
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,7 +1,7 @@
from __future__ import annotations
import argparse import argparse
import collections import collections
from typing import Dict
from typing import Optional
from typing import Sequence from typing import Sequence
@ -25,7 +25,7 @@ def fix_filename(filename: str, fix: str) -> int:
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
contents = f.read() contents = f.read()
counts: Dict[bytes, int] = collections.defaultdict(int) counts: dict[bytes, int] = collections.defaultdict(int)
for line in contents.splitlines(True): for line in contents.splitlines(True):
for ending in ALL_ENDINGS: for ending in ALL_ENDINGS:
@ -62,7 +62,7 @@ def fix_filename(filename: str, fix: str) -> int:
return other_endings return other_endings
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'-f', '--fix', '-f', '--fix',

View file

@ -1,7 +1,8 @@
from __future__ import annotations
import argparse import argparse
import re import re
from typing import AbstractSet from typing import AbstractSet
from typing import Optional
from typing import Sequence from typing import Sequence
from pre_commit_hooks.util import CalledProcessError from pre_commit_hooks.util import CalledProcessError
@ -23,7 +24,7 @@ def is_on_branch(
) )
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'-b', '--branch', action='append', '-b', '--branch', action='append',

View file

@ -1,13 +1,11 @@
from __future__ import annotations
import argparse import argparse
import json import json
import sys import sys
from difflib import unified_diff from difflib import unified_diff
from typing import List
from typing import Mapping from typing import Mapping
from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Tuple
from typing import Union
def _get_pretty_format( def _get_pretty_format(
@ -17,7 +15,7 @@ def _get_pretty_format(
sort_keys: bool = True, sort_keys: bool = True,
top_keys: Sequence[str] = (), top_keys: Sequence[str] = (),
) -> str: ) -> str:
def pairs_first(pairs: Sequence[Tuple[str, str]]) -> Mapping[str, str]: def pairs_first(pairs: Sequence[tuple[str, str]]) -> Mapping[str, str]:
before = [pair for pair in pairs if pair[0] in top_keys] before = [pair for pair in pairs if pair[0] in top_keys]
before = sorted(before, key=lambda x: top_keys.index(x[0])) before = sorted(before, key=lambda x: top_keys.index(x[0]))
after = [pair for pair in pairs if pair[0] not in top_keys] after = [pair for pair in pairs if pair[0] not in top_keys]
@ -38,7 +36,7 @@ def _autofix(filename: str, new_contents: str) -> None:
f.write(new_contents) f.write(new_contents)
def parse_num_to_int(s: str) -> Union[int, str]: def parse_num_to_int(s: str) -> int | str:
"""Convert string numbers to int, leaving strings as is.""" """Convert string numbers to int, leaving strings as is."""
try: try:
return int(s) return int(s)
@ -46,7 +44,7 @@ def parse_num_to_int(s: str) -> Union[int, str]:
return s return s
def parse_topkeys(s: str) -> List[str]: def parse_topkeys(s: str) -> list[str]:
return s.split(',') return s.split(',')
@ -57,7 +55,7 @@ def get_diff(source: str, target: str, file: str) -> str:
return ''.join(diff) return ''.join(diff)
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'--autofix', '--autofix',

View file

@ -1,9 +1,10 @@
from __future__ import annotations
import sys import sys
from typing import Optional
from typing import Sequence from typing import Sequence
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
argv = argv if argv is not None else sys.argv[1:] argv = argv if argv is not None else sys.argv[1:]
hookid, new_hookid, url = argv[:3] hookid, new_hookid, url = argv[:3]
raise SystemExit( raise SystemExit(

View file

@ -1,8 +1,8 @@
from __future__ import annotations
import argparse import argparse
import re import re
from typing import IO from typing import IO
from typing import List
from typing import Optional
from typing import Sequence from typing import Sequence
@ -15,8 +15,8 @@ class Requirement:
UNTIL_SEP = re.compile(rb'[^;\s]+') UNTIL_SEP = re.compile(rb'[^;\s]+')
def __init__(self) -> None: def __init__(self) -> None:
self.value: Optional[bytes] = None self.value: bytes | None = None
self.comments: List[bytes] = [] self.comments: list[bytes] = []
@property @property
def name(self) -> bytes: def name(self) -> bytes:
@ -36,7 +36,7 @@ class Requirement:
return name[:m.start()] return name[:m.start()]
def __lt__(self, requirement: 'Requirement') -> bool: def __lt__(self, requirement: Requirement) -> bool:
# \n means top of file comment, so always return True, # \n means top of file comment, so always return True,
# otherwise just do a string comparison with value. # otherwise just do a string comparison with value.
assert self.value is not None, self.value assert self.value is not None, self.value
@ -61,9 +61,9 @@ class Requirement:
def fix_requirements(f: IO[bytes]) -> int: def fix_requirements(f: IO[bytes]) -> int:
requirements: List[Requirement] = [] requirements: list[Requirement] = []
before = list(f) before = list(f)
after: List[bytes] = [] after: list[bytes] = []
before_string = b''.join(before) before_string = b''.join(before)
@ -130,7 +130,7 @@ def fix_requirements(f: IO[bytes]) -> int:
return FAIL return FAIL
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to fix') parser.add_argument('filenames', nargs='*', help='Filenames to fix')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -17,16 +17,16 @@ We assume a strict subset of YAML that looks like:
In other words, we don't sort deeper than the top layer, and might corrupt In other words, we don't sort deeper than the top layer, and might corrupt
complicated YAML files. complicated YAML files.
""" """
from __future__ import annotations
import argparse import argparse
from typing import List
from typing import Optional
from typing import Sequence from typing import Sequence
QUOTES = ["'", '"'] QUOTES = ["'", '"']
def sort(lines: List[str]) -> List[str]: def sort(lines: list[str]) -> list[str]:
"""Sort a YAML file in alphabetical order, keeping blocks together. """Sort a YAML file in alphabetical order, keeping blocks together.
:param lines: array of strings (without newlines) :param lines: array of strings (without newlines)
@ -44,7 +44,7 @@ def sort(lines: List[str]) -> List[str]:
return new_lines return new_lines
def parse_block(lines: List[str], header: bool = False) -> List[str]: def parse_block(lines: list[str], header: bool = False) -> list[str]:
"""Parse and return a single block, popping off the start of `lines`. """Parse and return a single block, popping off the start of `lines`.
If parsing a header block, we stop after we reach a line that is not a If parsing a header block, we stop after we reach a line that is not a
@ -60,7 +60,7 @@ def parse_block(lines: List[str], header: bool = False) -> List[str]:
return block_lines return block_lines
def parse_blocks(lines: List[str]) -> List[List[str]]: def parse_blocks(lines: list[str]) -> list[list[str]]:
"""Parse and return all possible blocks, popping off the start of `lines`. """Parse and return all possible blocks, popping off the start of `lines`.
:param lines: list of lines :param lines: list of lines
@ -77,7 +77,7 @@ def parse_blocks(lines: List[str]) -> List[List[str]]:
return blocks return blocks
def first_key(lines: List[str]) -> str: def first_key(lines: list[str]) -> str:
"""Returns a string representing the sort key of a block. """Returns a string representing the sort key of a block.
The sort key is the first YAML key we encounter, ignoring comments, and The sort key is the first YAML key we encounter, ignoring comments, and
@ -99,7 +99,7 @@ def first_key(lines: List[str]) -> str:
return '' # not actually reached in reality return '' # not actually reached in reality
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to fix') parser.add_argument('filenames', nargs='*', help='Filenames to fix')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,9 +1,9 @@
from __future__ import annotations
import argparse import argparse
import io import io
import re import re
import tokenize import tokenize
from typing import List
from typing import Optional
from typing import Sequence from typing import Sequence
START_QUOTE_RE = re.compile('^[a-zA-Z]*"') START_QUOTE_RE = re.compile('^[a-zA-Z]*"')
@ -24,7 +24,7 @@ def handle_match(token_text: str) -> str:
return token_text return token_text
def get_line_offsets_by_line_no(src: str) -> List[int]: def get_line_offsets_by_line_no(src: str) -> list[int]:
# Padded so we can index with line number # Padded so we can index with line number
offsets = [-1, 0] offsets = [-1, 0]
for line in src.splitlines(True): for line in src.splitlines(True):
@ -60,7 +60,7 @@ def fix_strings(filename: str) -> int:
return 0 return 0
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to fix') parser.add_argument('filenames', nargs='*', help='Filenames to fix')
args = parser.parse_args(argv) args = parser.parse_args(argv)

View file

@ -1,11 +1,12 @@
from __future__ import annotations
import argparse import argparse
import os.path import os.path
import re import re
from typing import Optional
from typing import Sequence from typing import Sequence
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
parser.add_argument( parser.add_argument(

View file

@ -1,13 +1,14 @@
from __future__ import annotations
import argparse import argparse
import os import os
from typing import Optional
from typing import Sequence from typing import Sequence
def _fix_file( def _fix_file(
filename: str, filename: str,
is_markdown: bool, is_markdown: bool,
chars: Optional[bytes], chars: bytes | None,
) -> bool: ) -> bool:
with open(filename, mode='rb') as file_processed: with open(filename, mode='rb') as file_processed:
lines = file_processed.readlines() lines = file_processed.readlines()
@ -24,7 +25,7 @@ def _fix_file(
def _process_line( def _process_line(
line: bytes, line: bytes,
is_markdown: bool, is_markdown: bool,
chars: Optional[bytes], chars: bytes | None,
) -> bytes: ) -> bytes:
if line[-2:] == b'\r\n': if line[-2:] == b'\r\n':
eol = b'\r\n' eol = b'\r\n'
@ -40,7 +41,7 @@ def _process_line(
return line.rstrip(chars) + eol return line.rstrip(chars) + eol
def main(argv: Optional[Sequence[str]] = None) -> int: def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'--no-markdown-linebreak-ext', '--no-markdown-linebreak-ext',

View file

@ -1,20 +1,19 @@
from __future__ import annotations
import subprocess import subprocess
from typing import Any from typing import Any
from typing import List
from typing import Optional
from typing import Set
class CalledProcessError(RuntimeError): class CalledProcessError(RuntimeError):
pass pass
def added_files() -> Set[str]: def added_files() -> set[str]:
cmd = ('git', 'diff', '--staged', '--name-only', '--diff-filter=A') cmd = ('git', 'diff', '--staged', '--name-only', '--diff-filter=A')
return set(cmd_output(*cmd).splitlines()) return set(cmd_output(*cmd).splitlines())
def cmd_output(*cmd: str, retcode: Optional[int] = 0, **kwargs: Any) -> str: def cmd_output(*cmd: str, retcode: int | None = 0, **kwargs: Any) -> str:
kwargs.setdefault('stdout', subprocess.PIPE) kwargs.setdefault('stdout', subprocess.PIPE)
kwargs.setdefault('stderr', subprocess.PIPE) kwargs.setdefault('stderr', subprocess.PIPE)
proc = subprocess.Popen(cmd, **kwargs) proc = subprocess.Popen(cmd, **kwargs)
@ -25,7 +24,7 @@ def cmd_output(*cmd: str, retcode: Optional[int] = 0, **kwargs: Any) -> str:
return stdout return stdout
def zsplit(s: str) -> List[str]: def zsplit(s: str) -> list[str]:
s = s.strip('\0') s = s.strip('\0')
if s: if s:
return s.split('\0') return s.split('\0')

View file

@ -13,7 +13,6 @@ classifiers =
License :: OSI Approved :: MIT License License :: OSI Approved :: MIT License
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.9
@ -26,7 +25,7 @@ packages = find:
install_requires = install_requires =
ruamel.yaml>=0.15 ruamel.yaml>=0.15
toml toml
python_requires = >=3.6.1 python_requires = >=3.7
[options.packages.find] [options.packages.find]
exclude = exclude =

View file

@ -1,2 +1,4 @@
from __future__ import annotations
from setuptools import setup from setuptools import setup
setup() setup()

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os.path import os.path
import subprocess import subprocess

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import shutil import shutil
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit_hooks.check_ast import main from pre_commit_hooks.check_ast import main
from testing.util import get_resource_path from testing.util import get_resource_path

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import ast import ast
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit_hooks import check_byte_order_marker from pre_commit_hooks import check_byte_order_marker

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import sys import sys
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.check_docstring_first import check_docstring_first from pre_commit_hooks.check_docstring_first import check_docstring_first

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import sys import sys

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.check_json import main from pre_commit_hooks.check_json import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import shutil import shutil

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit_hooks.check_toml import main from pre_commit_hooks.check_toml import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit_hooks.check_vcs_permalinks import main from pre_commit_hooks.check_vcs_permalinks import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.check_xml import main from pre_commit_hooks.check_xml import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.check_yaml import main from pre_commit_hooks.check_yaml import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import ast import ast
from pre_commit_hooks.debug_statement_hook import Debug from pre_commit_hooks.debug_statement_hook import Debug

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import subprocess import subprocess

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from unittest.mock import patch from unittest.mock import patch
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.detect_private_key import main from pre_commit_hooks.detect_private_key import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import io import io
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.file_contents_sorter import FAIL from pre_commit_hooks.file_contents_sorter import FAIL

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit_hooks import fix_byte_order_marker from pre_commit_hooks import fix_byte_order_marker

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import io import io
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import subprocess import subprocess
from unittest import mock from unittest import mock

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.mixed_line_ending import main from pre_commit_hooks.mixed_line_ending import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.no_commit_to_branch import is_on_branch from pre_commit_hooks.no_commit_to_branch import is_on_branch

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import shutil import shutil

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit_hooks.check_yaml import yaml from pre_commit_hooks.check_yaml import yaml

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.removed import main from pre_commit_hooks.removed import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.requirements_txt_fixer import FAIL from pre_commit_hooks.requirements_txt_fixer import FAIL

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import textwrap import textwrap
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from pre_commit_hooks.tests_should_end_in_test import main from pre_commit_hooks.tests_should_end_in_test import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.trailing_whitespace_fixer import main from pre_commit_hooks.trailing_whitespace_fixer import main

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from pre_commit_hooks.util import CalledProcessError from pre_commit_hooks.util import CalledProcessError

View file

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py36,py37,py38,pypy3,pre-commit envlist = py37,py38,pypy3,pre-commit
[testenv] [testenv]
deps = -rrequirements-dev.txt deps = -rrequirements-dev.txt