pre-commit-hooks: python3.6+
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1bcc8c8..464609b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -28,17 +28,22 @@
rev: v1.9.0
hooks:
- id: reorder-python-imports
- language_version: python3
+ args: [--py3-plus]
- repo: https://github.com/asottile/pyupgrade
rev: v1.26.2
hooks:
- id: pyupgrade
+ args: [--py36-plus]
- repo: https://github.com/asottile/add-trailing-comma
rev: v1.5.0
hooks:
- id: add-trailing-comma
+ args: [--py36-plus]
+- repo: https://github.com/asottile/setup-cfg-fmt
+ rev: v1.6.0
+ hooks:
+ - id: setup-cfg-fmt
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.761
hooks:
- id: mypy
- language_version: python3
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 1337dc6..dc3a57a 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -16,9 +16,9 @@
- template: job--pre-commit.yml@asottile
- template: job--python-tox.yml@asottile
parameters:
- toxenvs: [py27, py37]
+ toxenvs: [py38]
os: windows
- template: job--python-tox.yml@asottile
parameters:
- toxenvs: [pypy, pypy3, py27, py36, py37]
+ toxenvs: [pypy3, py36, py37, py38]
os: linux
diff --git a/pre_commit_hooks/autopep8_wrapper.py b/pre_commit_hooks/autopep8_wrapper.py
index 8b69a04..78a1cce 100644
--- a/pre_commit_hooks/autopep8_wrapper.py
+++ b/pre_commit_hooks/autopep8_wrapper.py
@@ -1,9 +1,4 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
-
-def main(): # type: () -> int
+def main() -> int:
raise SystemExit(
'autopep8-wrapper is deprecated. Instead use autopep8 directly via '
'https://github.com/pre-commit/mirrors-autopep8',
diff --git a/pre_commit_hooks/check_added_large_files.py b/pre_commit_hooks/check_added_large_files.py
index be39498..91f5754 100644
--- a/pre_commit_hooks/check_added_large_files.py
+++ b/pre_commit_hooks/check_added_large_files.py
@@ -1,13 +1,7 @@
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import json
import math
import os
-from typing import Iterable
from typing import Optional
from typing import Sequence
from typing import Set
@@ -17,7 +11,7 @@
from pre_commit_hooks.util import cmd_output
-def lfs_files(): # type: () -> Set[str]
+def lfs_files() -> Set[str]:
try:
# Introduced in git-lfs 2.2.0, first working in 2.2.1
lfs_ret = cmd_output('git', 'lfs', 'status', '--json')
@@ -27,23 +21,20 @@
return set(json.loads(lfs_ret)['files'])
-def find_large_added_files(filenames, maxkb):
- # type: (Iterable[str], int) -> int
+def find_large_added_files(filenames: Sequence[str], maxkb: int) -> int:
# Find all added files that are also in the list of files pre-commit tells
# us about
- filenames = (added_files() & set(filenames)) - lfs_files()
-
retv = 0
- for filename in filenames:
+ for filename in (added_files() & set(filenames)) - lfs_files():
kb = int(math.ceil(os.stat(filename).st_size / 1024))
if kb > maxkb:
- print('{} ({} KB) exceeds {} KB.'.format(filename, kb, maxkb))
+ print(f'{filename} ({kb} KB) exceeds {maxkb} KB.')
retv = 1
return retv
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
'filenames', nargs='*',
diff --git a/pre_commit_hooks/check_ast.py b/pre_commit_hooks/check_ast.py
index cb33ea0..2be6e1a 100644
--- a/pre_commit_hooks/check_ast.py
+++ b/pre_commit_hooks/check_ast.py
@@ -1,7 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import ast
import platform
@@ -11,7 +7,7 @@
from typing import Sequence
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv)
@@ -23,15 +19,11 @@
with open(filename, 'rb') as f:
ast.parse(f.read(), filename=filename)
except SyntaxError:
- print(
- '{}: failed parsing with {} {}:'.format(
- filename,
- platform.python_implementation(),
- sys.version.partition(' ')[0],
- ),
- )
+ impl = platform.python_implementation()
+ version = sys.version.split()[0]
+ print(f'{filename}: failed parsing with {impl} {version}:')
tb = ' ' + traceback.format_exc().replace('\n', '\n ')
- print('\n{}'.format(tb))
+ print(f'\n{tb}')
retval = 1
return retval
diff --git a/pre_commit_hooks/check_builtin_literals.py b/pre_commit_hooks/check_builtin_literals.py
index 4ddaa8c..6bcd838 100644
--- a/pre_commit_hooks/check_builtin_literals.py
+++ b/pre_commit_hooks/check_builtin_literals.py
@@ -1,10 +1,7 @@
-from __future__ import unicode_literals
-
import argparse
import ast
-import collections
-import sys
from typing import List
+from typing import NamedTuple
from typing import Optional
from typing import Sequence
from typing import Set
@@ -21,23 +18,26 @@
}
-Call = collections.namedtuple('Call', ['name', 'line', 'column'])
+class Call(NamedTuple):
+ name: str
+ line: int
+ column: int
class Visitor(ast.NodeVisitor):
- def __init__(self, ignore=None, allow_dict_kwargs=True):
- # type: (Optional[Sequence[str]], bool) -> None
- self.builtin_type_calls = [] # type: List[Call]
+ def __init__(
+ self,
+ ignore: Optional[Sequence[str]] = None,
+ allow_dict_kwargs: bool = True,
+ ) -> None:
+ self.builtin_type_calls: List[Call] = []
self.ignore = set(ignore) if ignore else set()
self.allow_dict_kwargs = allow_dict_kwargs
- def _check_dict_call(self, node): # type: (ast.Call) -> bool
- return (
- self.allow_dict_kwargs and
- (getattr(node, 'kwargs', None) or getattr(node, 'keywords', None))
- )
+ def _check_dict_call(self, node: ast.Call) -> bool:
+ return self.allow_dict_kwargs and bool(node.keywords)
- def visit_Call(self, node): # type: (ast.Call) -> None
+ def visit_Call(self, node: ast.Call) -> None:
if not isinstance(node.func, ast.Name):
# Ignore functions that are object attributes (`foo.bar()`).
# Assume that if the user calls `builtins.list()`, they know what
@@ -54,8 +54,11 @@
)
-def check_file(filename, ignore=None, allow_dict_kwargs=True):
- # type: (str, Optional[Sequence[str]], bool) -> List[Call]
+def check_file(
+ filename: str,
+ ignore: Optional[Sequence[str]] = None,
+ allow_dict_kwargs: bool = True,
+) -> List[Call]:
with open(filename, 'rb') as f:
tree = ast.parse(f.read(), filename=filename)
visitor = Visitor(ignore=ignore, allow_dict_kwargs=allow_dict_kwargs)
@@ -63,11 +66,11 @@
return visitor.builtin_type_calls
-def parse_ignore(value): # type: (str) -> Set[str]
+def parse_ignore(value: str) -> Set[str]:
return set(value.split(','))
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*')
parser.add_argument('--ignore', type=parse_ignore, default=set())
@@ -93,15 +96,11 @@
rc = rc or 1
for call in calls:
print(
- '{filename}:{call.line}:{call.column}: '
- 'replace {call.name}() with {replacement}'.format(
- filename=filename,
- call=call,
- replacement=BUILTIN_TYPES[call.name],
- ),
+ f'{filename}:{call.line}:{call.column}: '
+ f'replace {call.name}() with {BUILTIN_TYPES[call.name]}',
)
return rc
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/check_byte_order_marker.py b/pre_commit_hooks/check_byte_order_marker.py
index 10667c3..c0c2969 100644
--- a/pre_commit_hooks/check_byte_order_marker.py
+++ b/pre_commit_hooks/check_byte_order_marker.py
@@ -1,13 +1,9 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
from typing import Optional
from typing import Sequence
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv)
@@ -18,7 +14,7 @@
with open(filename, 'rb') as f:
if f.read(3) == b'\xef\xbb\xbf':
retv = 1
- print('{}: Has a byte-order marker'.format(filename))
+ print(f'{filename}: Has a byte-order marker')
return retv
diff --git a/pre_commit_hooks/check_case_conflict.py b/pre_commit_hooks/check_case_conflict.py
index e343d61..6b8ba82 100644
--- a/pre_commit_hooks/check_case_conflict.py
+++ b/pre_commit_hooks/check_case_conflict.py
@@ -1,7 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
from typing import Iterable
from typing import Optional
@@ -12,11 +8,11 @@
from pre_commit_hooks.util import cmd_output
-def lower_set(iterable): # type: (Iterable[str]) -> Set[str]
+def lower_set(iterable: Iterable[str]) -> Set[str]:
return {x.lower() for x in iterable}
-def find_conflicting_filenames(filenames): # type: (Sequence[str]) -> int
+def find_conflicting_filenames(filenames: Sequence[str]) -> int:
repo_files = set(cmd_output('git', 'ls-files').splitlines())
relevant_files = set(filenames) | added_files()
repo_files -= relevant_files
@@ -39,13 +35,13 @@
if x.lower() in conflicts
]
for filename in sorted(conflicting_files):
- print('Case-insensitivity conflict found: {}'.format(filename))
+ print(f'Case-insensitivity conflict found: {filename}')
retv = 1
return retv
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
'filenames', nargs='*',
diff --git a/pre_commit_hooks/check_docstring_first.py b/pre_commit_hooks/check_docstring_first.py
index 6c19381..875c0fb 100644
--- a/pre_commit_hooks/check_docstring_first.py
+++ b/pre_commit_hooks/check_docstring_first.py
@@ -1,30 +1,17 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import io
import tokenize
+from tokenize import tokenize as tokenize_tokenize
from typing import Optional
from typing import Sequence
-import six
-
-if six.PY2: # pragma: no cover (PY2)
- from tokenize import generate_tokens as tokenize_tokenize
- OTHER_NON_CODE = ()
-else: # pragma: no cover (PY3)
- from tokenize import tokenize as tokenize_tokenize
- OTHER_NON_CODE = (tokenize.ENCODING,)
-
-NON_CODE_TOKENS = frozenset(
- (tokenize.COMMENT, tokenize.ENDMARKER, tokenize.NEWLINE, tokenize.NL) +
- OTHER_NON_CODE,
-)
+NON_CODE_TOKENS = frozenset((
+ tokenize.COMMENT, tokenize.ENDMARKER, tokenize.NEWLINE, tokenize.NL,
+ tokenize.ENCODING,
+))
-def check_docstring_first(src, filename='<unknown>'):
- # type: (bytes, str) -> int
+def check_docstring_first(src: bytes, filename: str = '<unknown>') -> int:
"""Returns nonzero if the source has what looks like a docstring that is
not at the beginning of the source.
@@ -40,18 +27,14 @@
if tok_type == tokenize.STRING and scol == 0:
if found_docstring_line is not None:
print(
- '{}:{} Multiple module docstrings '
- '(first docstring on line {}).'.format(
- filename, sline, found_docstring_line,
- ),
+ f'{filename}:{sline} Multiple module docstrings '
+ f'(first docstring on line {found_docstring_line}).',
)
return 1
elif found_code_line is not None:
print(
- '{}:{} Module docstring appears after code '
- '(code seen on line {}).'.format(
- filename, sline, found_code_line,
- ),
+ f'{filename}:{sline} Module docstring appears after code '
+ f'(code seen on line {found_code_line}).',
)
return 1
else:
@@ -62,7 +45,7 @@
return 0
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv)
diff --git a/pre_commit_hooks/check_executables_have_shebangs.py b/pre_commit_hooks/check_executables_have_shebangs.py
index 4db2f9f..c34c7b7 100644
--- a/pre_commit_hooks/check_executables_have_shebangs.py
+++ b/pre_commit_hooks/check_executables_have_shebangs.py
@@ -1,28 +1,22 @@
"""Check that executable text files have a shebang."""
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
-import pipes
+import shlex
import sys
from typing import Optional
from typing import Sequence
-def check_has_shebang(path): # type: (str) -> int
+def check_has_shebang(path: str) -> int:
with open(path, 'rb') as f:
first_bytes = f.read(2)
if first_bytes != b'#!':
+ quoted = shlex.quote(path)
print(
- '{path}: marked executable but has no (or invalid) shebang!\n'
- " If it isn't supposed to be executable, try: chmod -x {quoted}\n"
- ' If it is supposed to be executable, double-check its shebang.'
- .format(
- path=path,
- quoted=pipes.quote(path),
- ),
+ f'{path}: marked executable but has no (or invalid) shebang!\n'
+ f" If it isn't supposed to be executable, try: "
+ f'`chmod -x {quoted}`\n'
+ f' If it is supposed to be executable, double-check its shebang.',
file=sys.stderr,
)
return 1
@@ -30,7 +24,7 @@
return 0
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv)
diff --git a/pre_commit_hooks/check_json.py b/pre_commit_hooks/check_json.py
index f26e0a5..25dbfd9 100644
--- a/pre_commit_hooks/check_json.py
+++ b/pre_commit_hooks/check_json.py
@@ -1,27 +1,25 @@
-from __future__ import print_function
-
import argparse
-import io
import json
-import sys
from typing import Optional
from typing import Sequence
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check.')
args = parser.parse_args(argv)
retval = 0
for filename in args.filenames:
- try:
- json.load(io.open(filename, encoding='UTF-8'))
- except (ValueError, UnicodeDecodeError) as exc:
- print('{}: Failed to json decode ({})'.format(filename, exc))
- retval = 1
+ with open(filename, 'rb') as f:
+ try:
+ json.load(f)
+ # TODO: need UnicodeDecodeError?
+ except (ValueError, UnicodeDecodeError) as exc:
+ print(f'{filename}: Failed to json decode ({exc})')
+ retval = 1
return retval
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/check_merge_conflict.py b/pre_commit_hooks/check_merge_conflict.py
index 2a03c3a..c20a8af 100644
--- a/pre_commit_hooks/check_merge_conflict.py
+++ b/pre_commit_hooks/check_merge_conflict.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
import argparse
import os.path
from typing import Optional
@@ -12,10 +10,9 @@
b'=======\n',
b'>>>>>>> ',
]
-WARNING_MSG = 'Merge conflict string "{0}" found in {1}:{2}'
-def is_in_merge(): # type: () -> int
+def is_in_merge() -> int:
return (
os.path.exists(os.path.join('.git', 'MERGE_MSG')) and
(
@@ -26,7 +23,7 @@
)
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*')
parser.add_argument('--assume-in-merge', action='store_true')
@@ -42,9 +39,8 @@
for pattern in CONFLICT_PATTERNS:
if line.startswith(pattern):
print(
- WARNING_MSG.format(
- pattern.decode(), filename, i + 1,
- ),
+ f'Merge conflict string "{pattern.decode()}" '
+ f'found in {filename}:{i + 1}',
)
retcode = 1
diff --git a/pre_commit_hooks/check_symlinks.py b/pre_commit_hooks/check_symlinks.py
index 736bf99..f014714 100644
--- a/pre_commit_hooks/check_symlinks.py
+++ b/pre_commit_hooks/check_symlinks.py
@@ -1,14 +1,10 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import os.path
from typing import Optional
from typing import Sequence
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser(description='Checks for broken symlinks.')
parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv)
@@ -20,7 +16,7 @@
os.path.islink(filename) and
not os.path.exists(filename)
): # pragma: no cover (symlink support required)
- print('{}: Broken symlink'.format(filename))
+ print(f'{filename}: Broken symlink')
retv = 1
return retv
diff --git a/pre_commit_hooks/check_toml.py b/pre_commit_hooks/check_toml.py
index e16e17c..51a1f15 100644
--- a/pre_commit_hooks/check_toml.py
+++ b/pre_commit_hooks/check_toml.py
@@ -1,14 +1,11 @@
-from __future__ import print_function
-
import argparse
-import sys
from typing import Optional
from typing import Sequence
import toml
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check.')
args = parser.parse_args(argv)
@@ -19,10 +16,10 @@
with open(filename) as f:
toml.load(f)
except toml.TomlDecodeError as exc:
- print('{}: {}'.format(filename, exc))
+ print(f'{filename}: {exc}')
retval = 1
return retval
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/check_vcs_permalinks.py b/pre_commit_hooks/check_vcs_permalinks.py
index f6e2a7d..bf698e1 100644
--- a/pre_commit_hooks/check_vcs_permalinks.py
+++ b/pre_commit_hooks/check_vcs_permalinks.py
@@ -1,7 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import re
import sys
@@ -14,19 +10,19 @@
)
-def _check_filename(filename): # type: (str) -> int
+def _check_filename(filename: str) -> int:
retv = 0
with open(filename, 'rb') as f:
for i, line in enumerate(f, 1):
if GITHUB_NON_PERMALINK.search(line):
- sys.stdout.write('{}:{}:'.format(filename, i))
+ sys.stdout.write(f'{filename}:{i}:')
sys.stdout.flush()
- getattr(sys.stdout, 'buffer', sys.stdout).write(line)
+ sys.stdout.buffer.write(line)
retv = 1
return retv
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv)
diff --git a/pre_commit_hooks/check_xml.py b/pre_commit_hooks/check_xml.py
index 66e10ba..eddfdf9 100644
--- a/pre_commit_hooks/check_xml.py
+++ b/pre_commit_hooks/check_xml.py
@@ -1,30 +1,26 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
-import io
-import sys
import xml.sax.handler
from typing import Optional
from typing import Sequence
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='XML filenames to check.')
args = parser.parse_args(argv)
retval = 0
+ handler = xml.sax.handler.ContentHandler()
for filename in args.filenames:
try:
- with io.open(filename, 'rb') as xml_file:
- xml.sax.parse(xml_file, xml.sax.handler.ContentHandler())
+ with open(filename, 'rb') as xml_file:
+ # https://github.com/python/typeshed/pull/3725
+ xml.sax.parse(xml_file, handler) # type: ignore
except xml.sax.SAXException as exc:
- print('{}: Failed to xml parse ({})'.format(filename, exc))
+ print(f'{filename}: Failed to xml parse ({exc})')
retval = 1
return retval
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/check_yaml.py b/pre_commit_hooks/check_yaml.py
index 5b66485..7453f6f 100644
--- a/pre_commit_hooks/check_yaml.py
+++ b/pre_commit_hooks/check_yaml.py
@@ -1,11 +1,7 @@
-from __future__ import print_function
-
import argparse
-import collections
-import io
-import sys
from typing import Any
from typing import Generator
+from typing import NamedTuple
from typing import Optional
from typing import Sequence
@@ -14,20 +10,24 @@
yaml = ruamel.yaml.YAML(typ='safe')
-def _exhaust(gen): # type: (Generator[str, None, None]) -> None
+def _exhaust(gen: Generator[str, None, None]) -> None:
for _ in gen:
pass
-def _parse_unsafe(*args, **kwargs): # type: (*Any, **Any) -> None
+def _parse_unsafe(*args: Any, **kwargs: Any) -> None:
_exhaust(yaml.parse(*args, **kwargs))
-def _load_all(*args, **kwargs): # type: (*Any, **Any) -> None
+def _load_all(*args: Any, **kwargs: Any) -> None:
_exhaust(yaml.load_all(*args, **kwargs))
-Key = collections.namedtuple('Key', ('multi', 'unsafe'))
+class Key(NamedTuple):
+ multi: bool
+ unsafe: bool
+
+
LOAD_FNS = {
Key(multi=False, unsafe=False): yaml.load,
Key(multi=False, unsafe=True): _parse_unsafe,
@@ -36,7 +36,7 @@
}
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
'-m', '--multi', '--allow-multiple-documents', action='store_true',
@@ -59,7 +59,7 @@
retval = 0
for filename in args.filenames:
try:
- with io.open(filename, encoding='UTF-8') as f:
+ with open(filename, encoding='UTF-8') as f:
load_fn(f)
except ruamel.yaml.YAMLError as exc:
print(exc)
@@ -68,4 +68,4 @@
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/debug_statement_hook.py b/pre_commit_hooks/debug_statement_hook.py
index 4864873..00423ac 100644
--- a/pre_commit_hooks/debug_statement_hook.py
+++ b/pre_commit_hooks/debug_statement_hook.py
@@ -1,35 +1,38 @@
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import ast
-import collections
import traceback
from typing import List
+from typing import NamedTuple
from typing import Optional
from typing import Sequence
DEBUG_STATEMENTS = {'pdb', 'ipdb', 'pudb', 'q', 'rdb', 'rpdb'}
-Debug = collections.namedtuple('Debug', ('line', 'col', 'name', 'reason'))
+
+
+class Debug(NamedTuple):
+ line: int
+ col: int
+ name: str
+ reason: str
class DebugStatementParser(ast.NodeVisitor):
- def __init__(self): # type: () -> None
- self.breakpoints = [] # type: List[Debug]
+ def __init__(self) -> None:
+ self.breakpoints: List[Debug] = []
- def visit_Import(self, node): # type: (ast.Import) -> None
+ def visit_Import(self, node: ast.Import) -> None:
for name in node.names:
if name.name in DEBUG_STATEMENTS:
st = Debug(node.lineno, node.col_offset, name.name, 'imported')
self.breakpoints.append(st)
- def visit_ImportFrom(self, node): # type: (ast.ImportFrom) -> None
+ def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
if node.module in DEBUG_STATEMENTS:
st = Debug(node.lineno, node.col_offset, node.module, 'imported')
self.breakpoints.append(st)
- def visit_Call(self, node): # type: (ast.Call) -> None
+ def visit_Call(self, node: ast.Call) -> None:
"""python3.7+ breakpoint()"""
if isinstance(node.func, ast.Name) and node.func.id == 'breakpoint':
st = Debug(node.lineno, node.col_offset, node.func.id, 'called')
@@ -37,12 +40,12 @@
self.generic_visit(node)
-def check_file(filename): # type: (str) -> int
+def check_file(filename: str) -> int:
try:
with open(filename, 'rb') as f:
ast_obj = ast.parse(f.read(), filename=filename)
except SyntaxError:
- print('{} - Could not parse ast'.format(filename))
+ print(f'{filename} - Could not parse ast')
print()
print('\t' + traceback.format_exc().replace('\n', '\n\t'))
print()
@@ -52,16 +55,12 @@
visitor.visit(ast_obj)
for bp in visitor.breakpoints:
- print(
- '{}:{}:{} - {} {}'.format(
- filename, bp.line, bp.col, bp.name, bp.reason,
- ),
- )
+ print(f'{filename}:{bp.line}:{bp.col} - {bp.name} {bp.reason}')
return int(bool(visitor.breakpoints))
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to run')
args = parser.parse_args(argv)
diff --git a/pre_commit_hooks/detect_aws_credentials.py b/pre_commit_hooks/detect_aws_credentials.py
index da80ab4..fe18f4d 100644
--- a/pre_commit_hooks/detect_aws_credentials.py
+++ b/pre_commit_hooks/detect_aws_credentials.py
@@ -1,18 +1,19 @@
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
+import configparser
import os
-from typing import Dict
from typing import List
+from typing import NamedTuple
from typing import Optional
from typing import Sequence
from typing import Set
-from six.moves import configparser
+
+class BadFile(NamedTuple):
+ filename: str
+ key: str
-def get_aws_cred_files_from_env(): # type: () -> Set[str]
+def get_aws_cred_files_from_env() -> Set[str]:
"""Extract credential file paths from environment variables."""
return {
os.environ[env_var]
@@ -24,7 +25,7 @@
}
-def get_aws_secrets_from_env(): # type: () -> Set[str]
+def get_aws_secrets_from_env() -> Set[str]:
"""Extract AWS secrets from environment variables."""
keys = set()
for env_var in (
@@ -35,7 +36,7 @@
return keys
-def get_aws_secrets_from_file(credentials_file): # type: (str) -> Set[str]
+def get_aws_secrets_from_file(credentials_file: str) -> Set[str]:
"""Extract AWS secrets from configuration files.
Read an ini-style configuration file and return a set with all found AWS
@@ -66,8 +67,10 @@
return keys
-def check_file_for_aws_keys(filenames, keys):
- # type: (Sequence[str], Set[str]) -> List[Dict[str, str]]
+def check_file_for_aws_keys(
+ filenames: Sequence[str],
+ keys: Set[str],
+) -> List[BadFile]:
"""Check if files contain AWS secrets.
Return a list of all files containing AWS secrets and keys found, with all
@@ -82,13 +85,11 @@
# naively match the entire file, low chance of incorrect
# collision
if key in text_body:
- bad_files.append({
- 'filename': filename, 'key': key[:4] + '*' * 28,
- })
+ bad_files.append(BadFile(filename, key[:4].ljust(28, '*')))
return bad_files
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='+', help='Filenames to run')
parser.add_argument(
@@ -117,7 +118,7 @@
# of files to to gather AWS secrets from.
credential_files |= get_aws_cred_files_from_env()
- keys = set() # type: Set[str]
+ keys: Set[str] = set()
for credential_file in credential_files:
keys |= get_aws_secrets_from_file(credential_file)
@@ -139,7 +140,7 @@
bad_filenames = check_file_for_aws_keys(args.filenames, keys)
if bad_filenames:
for bad_file in bad_filenames:
- print('AWS secret found in {filename}: {key}'.format(**bad_file))
+ print(f'AWS secret found in {bad_file.filename}: {bad_file.key}')
return 1
else:
return 0
diff --git a/pre_commit_hooks/detect_private_key.py b/pre_commit_hooks/detect_private_key.py
index d31957d..7bbc2f9 100644
--- a/pre_commit_hooks/detect_private_key.py
+++ b/pre_commit_hooks/detect_private_key.py
@@ -1,7 +1,4 @@
-from __future__ import print_function
-
import argparse
-import sys
from typing import Optional
from typing import Sequence
@@ -17,7 +14,7 @@
]
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv)
@@ -32,11 +29,11 @@
if private_key_files:
for private_key_file in private_key_files:
- print('Private key found: {}'.format(private_key_file))
+ print(f'Private key found: {private_key_file}')
return 1
else:
return 0
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/end_of_file_fixer.py b/pre_commit_hooks/end_of_file_fixer.py
index 4e77c94..1c07379 100644
--- a/pre_commit_hooks/end_of_file_fixer.py
+++ b/pre_commit_hooks/end_of_file_fixer.py
@@ -1,20 +1,16 @@
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import os
-import sys
from typing import IO
from typing import Optional
from typing import Sequence
-def fix_file(file_obj): # type: (IO[bytes]) -> int
+def fix_file(file_obj: IO[bytes]) -> int:
# Test for newline at end of file
# Empty files will throw IOError here
try:
file_obj.seek(-1, os.SEEK_END)
- except IOError:
+ except OSError:
return 0
last_character = file_obj.read(1)
# last_character will be '' for an empty file
@@ -52,7 +48,7 @@
return 0
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to fix')
args = parser.parse_args(argv)
@@ -64,11 +60,11 @@
with open(filename, 'rb+') as file_obj:
ret_for_file = fix_file(file_obj)
if ret_for_file:
- print('Fixing {}'.format(filename))
+ print(f'Fixing {filename}')
retv |= ret_for_file
return retv
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/file_contents_sorter.py b/pre_commit_hooks/file_contents_sorter.py
index 1598d2d..41ce306 100644
--- a/pre_commit_hooks/file_contents_sorter.py
+++ b/pre_commit_hooks/file_contents_sorter.py
@@ -9,10 +9,7 @@
this hook on that file should reduce the instances of git merge
conflicts and keep the file nicely ordered.
"""
-from __future__ import print_function
-
import argparse
-import sys
from typing import IO
from typing import Optional
from typing import Sequence
@@ -21,7 +18,7 @@
FAIL = 1
-def sort_file_contents(f): # type: (IO[bytes]) -> int
+def sort_file_contents(f: IO[bytes]) -> int:
before = list(f)
after = sorted([line.strip(b'\n\r') for line in before if line.strip()])
@@ -37,7 +34,7 @@
return FAIL
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='+', help='Files to sort')
args = parser.parse_args(argv)
@@ -49,7 +46,7 @@
ret_for_file = sort_file_contents(file_obj)
if ret_for_file:
- print('Sorting {}'.format(arg))
+ print(f'Sorting {arg}')
retv |= ret_for_file
@@ -57,4 +54,4 @@
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/fix_encoding_pragma.py b/pre_commit_hooks/fix_encoding_pragma.py
index 31bb52c..88d72ed 100644
--- a/pre_commit_hooks/fix_encoding_pragma.py
+++ b/pre_commit_hooks/fix_encoding_pragma.py
@@ -1,18 +1,13 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
-import collections
from typing import IO
+from typing import NamedTuple
from typing import Optional
from typing import Sequence
-from typing import Union
DEFAULT_PRAGMA = b'# -*- coding: utf-8 -*-'
-def has_coding(line): # type: (bytes) -> bool
+def has_coding(line: bytes) -> bool:
if not line.strip():
return False
return (
@@ -25,30 +20,30 @@
)
-class ExpectedContents(
- collections.namedtuple(
- 'ExpectedContents', ('shebang', 'rest', 'pragma_status', 'ending'),
- ),
-):
- """
- pragma_status:
- - True: has exactly the coding pragma expected
- - False: missing coding pragma entirely
- - None: has a coding pragma, but it does not match
- """
- __slots__ = ()
+class ExpectedContents(NamedTuple):
+ shebang: bytes
+ rest: bytes
+ # True: has exactly the coding pragma expected
+ # False: missing coding pragma entirely
+ # None: has a coding pragma, but it does not match
+ pragma_status: Optional[bool]
+ ending: bytes
@property
- def has_any_pragma(self): # type: () -> bool
+ def has_any_pragma(self) -> bool:
return self.pragma_status is not False
- def is_expected_pragma(self, remove): # type: (bool) -> bool
+ def is_expected_pragma(self, remove: bool) -> bool:
expected_pragma_status = not remove
return self.pragma_status is expected_pragma_status
-def _get_expected_contents(first_line, second_line, rest, expected_pragma):
- # type: (bytes, bytes, bytes, bytes) -> ExpectedContents
+def _get_expected_contents(
+ first_line: bytes,
+ second_line: bytes,
+ rest: bytes,
+ expected_pragma: bytes,
+) -> ExpectedContents:
ending = b'\r\n' if first_line.endswith(b'\r\n') else b'\n'
if first_line.startswith(b'#!'):
@@ -60,7 +55,7 @@
rest = second_line + rest
if potential_coding.rstrip(b'\r\n') == expected_pragma:
- pragma_status = True # type: Optional[bool]
+ pragma_status: Optional[bool] = True
elif has_coding(potential_coding):
pragma_status = None
else:
@@ -72,8 +67,11 @@
)
-def fix_encoding_pragma(f, remove=False, expected_pragma=DEFAULT_PRAGMA):
- # type: (IO[bytes], bool, bytes) -> int
+def fix_encoding_pragma(
+ f: IO[bytes],
+ remove: bool = False,
+ expected_pragma: bytes = DEFAULT_PRAGMA,
+) -> int:
expected = _get_expected_contents(
f.readline(), f.readline(), f.read(), expected_pragma,
)
@@ -103,21 +101,20 @@
return 1
-def _normalize_pragma(pragma): # type: (Union[bytes, str]) -> bytes
- if not isinstance(pragma, bytes):
- pragma = pragma.encode('UTF-8')
- return pragma.rstrip()
+def _normalize_pragma(pragma: str) -> bytes:
+ return pragma.encode().rstrip()
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser(
'Fixes the encoding pragma of python files',
)
parser.add_argument('filenames', nargs='*', help='Filenames to fix')
parser.add_argument(
'--pragma', default=DEFAULT_PRAGMA, type=_normalize_pragma,
- help='The encoding pragma to use. Default: {}'.format(
- DEFAULT_PRAGMA.decode(),
+ help=(
+ f'The encoding pragma to use. '
+ f'Default: {DEFAULT_PRAGMA.decode()}'
),
)
parser.add_argument(
@@ -141,9 +138,7 @@
retv |= file_ret
if file_ret:
print(
- fmt.format(
- pragma=args.pragma.decode(), filename=filename,
- ),
+ fmt.format(pragma=args.pragma.decode(), filename=filename),
)
return retv
diff --git a/pre_commit_hooks/forbid_new_submodules.py b/pre_commit_hooks/forbid_new_submodules.py
index bdbd6f7..c144d72 100644
--- a/pre_commit_hooks/forbid_new_submodules.py
+++ b/pre_commit_hooks/forbid_new_submodules.py
@@ -1,14 +1,10 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
from typing import Optional
from typing import Sequence
from pre_commit_hooks.util import cmd_output
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
# `argv` is ignored, pre-commit will send us a list of files that we
# don't care about
added_diff = cmd_output(
@@ -19,7 +15,7 @@
metadata, filename = line.split('\t', 1)
new_mode = metadata.split(' ')[1]
if new_mode == '160000':
- print('{}: new submodule introduced'.format(filename))
+ print(f'{filename}: new submodule introduced')
retv = 1
if retv:
diff --git a/pre_commit_hooks/mixed_line_ending.py b/pre_commit_hooks/mixed_line_ending.py
index 90aef03..0ef8e2c 100644
--- a/pre_commit_hooks/mixed_line_ending.py
+++ b/pre_commit_hooks/mixed_line_ending.py
@@ -1,7 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import collections
from typing import Dict
@@ -17,7 +13,7 @@
FIX_TO_LINE_ENDING = {'cr': CR, 'crlf': CRLF, 'lf': LF}
-def _fix(filename, contents, ending): # type: (str, bytes, bytes) -> None
+def _fix(filename: str, contents: bytes, ending: bytes) -> None:
new_contents = b''.join(
line.rstrip(b'\r\n') + ending for line in contents.splitlines(True)
)
@@ -25,11 +21,11 @@
f.write(new_contents)
-def fix_filename(filename, fix): # type: (str, str) -> int
+def fix_filename(filename: str, fix: str) -> int:
with open(filename, 'rb') as f:
contents = f.read()
- counts = collections.defaultdict(int) # type: Dict[bytes, int]
+ counts: Dict[bytes, int] = collections.defaultdict(int)
for line in contents.splitlines(True):
for ending in ALL_ENDINGS:
@@ -66,7 +62,7 @@
return other_endings
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
'-f', '--fix',
@@ -81,9 +77,9 @@
for filename in args.filenames:
if fix_filename(filename, args.fix):
if args.fix == 'no':
- print('{}: mixed line endings'.format(filename))
+ print(f'{filename}: mixed line endings')
else:
- print('{}: fixed mixed line endings'.format(filename))
+ print(f'{filename}: fixed mixed line endings')
retv = 1
return retv
diff --git a/pre_commit_hooks/no_commit_to_branch.py b/pre_commit_hooks/no_commit_to_branch.py
index 3131059..fb1506f 100644
--- a/pre_commit_hooks/no_commit_to_branch.py
+++ b/pre_commit_hooks/no_commit_to_branch.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
import argparse
import re
from typing import AbstractSet
@@ -10,8 +8,10 @@
from pre_commit_hooks.util import cmd_output
-def is_on_branch(protected, patterns=frozenset()):
- # type: (AbstractSet[str], AbstractSet[str]) -> bool
+def is_on_branch(
+ protected: AbstractSet[str],
+ patterns: AbstractSet[str] = frozenset(),
+) -> bool:
try:
ref_name = cmd_output('git', 'symbolic-ref', 'HEAD')
except CalledProcessError:
@@ -23,7 +23,7 @@
)
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
'-b', '--branch', action='append',
diff --git a/pre_commit_hooks/pretty_format_json.py b/pre_commit_hooks/pretty_format_json.py
index 7d515f4..25827dc 100644
--- a/pre_commit_hooks/pretty_format_json.py
+++ b/pre_commit_hooks/pretty_format_json.py
@@ -1,10 +1,5 @@
-from __future__ import print_function
-
import argparse
-import io
import json
-import sys
-from collections import OrderedDict
from difflib import unified_diff
from typing import List
from typing import Mapping
@@ -13,38 +8,36 @@
from typing import Tuple
from typing import Union
-from six import text_type
-
def _get_pretty_format(
- contents, indent, ensure_ascii=True, sort_keys=True, top_keys=(),
-): # type: (str, str, bool, bool, Sequence[str]) -> str
- def pairs_first(pairs):
- # type: (Sequence[Tuple[str, str]]) -> Mapping[str, str]
+ contents: str,
+ indent: str,
+ ensure_ascii: bool = True,
+ sort_keys: bool = True,
+ top_keys: Sequence[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 = sorted(before, key=lambda x: top_keys.index(x[0]))
after = [pair for pair in pairs if pair[0] not in top_keys]
if sort_keys:
- after = sorted(after, key=lambda x: x[0])
- return OrderedDict(before + after)
+ after.sort()
+ return dict(before + after)
json_pretty = json.dumps(
json.loads(contents, object_pairs_hook=pairs_first),
indent=indent,
ensure_ascii=ensure_ascii,
- # Workaround for https://bugs.python.org/issue16333
- separators=(',', ': '),
)
- # Ensure unicode (Py2) and add the newline that dumps does not end with.
- return text_type(json_pretty) + '\n'
+ return f'{json_pretty}\n'
-def _autofix(filename, new_contents): # type: (str, str) -> None
- print('Fixing file {}'.format(filename))
- with io.open(filename, 'w', encoding='UTF-8') as f:
+def _autofix(filename: str, new_contents: str) -> None:
+ print(f'Fixing file {filename}')
+ with open(filename, 'w', encoding='UTF-8') as f:
f.write(new_contents)
-def parse_num_to_int(s): # type: (str) -> Union[int, str]
+def parse_num_to_int(s: str) -> Union[int, str]:
"""Convert string numbers to int, leaving strings as is."""
try:
return int(s)
@@ -52,18 +45,18 @@
return s
-def parse_topkeys(s): # type: (str) -> List[str]
+def parse_topkeys(s: str) -> List[str]:
return s.split(',')
-def get_diff(source, target, file): # type: (str, str, str) -> str
+def get_diff(source: str, target: str, file: str) -> str:
source_lines = source.splitlines(True)
target_lines = target.splitlines(True)
diff = unified_diff(source_lines, target_lines, fromfile=file, tofile=file)
return ''.join(diff)
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
'--autofix',
@@ -110,7 +103,7 @@
status = 0
for json_file in args.filenames:
- with io.open(json_file, encoding='UTF-8') as f:
+ with open(json_file, encoding='UTF-8') as f:
contents = f.read()
try:
@@ -131,8 +124,8 @@
status = 1
except ValueError:
print(
- 'Input File {} is not a valid JSON, consider using check-json'
- .format(json_file),
+ f'Input File {json_file} is not a valid JSON, consider using '
+ f'check-json',
)
return 1
@@ -140,4 +133,4 @@
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/requirements_txt_fixer.py b/pre_commit_hooks/requirements_txt_fixer.py
index 1aa0dff..dc41815 100644
--- a/pre_commit_hooks/requirements_txt_fixer.py
+++ b/pre_commit_hooks/requirements_txt_fixer.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
import argparse
from typing import IO
from typing import List
@@ -11,15 +9,13 @@
FAIL = 1
-class Requirement(object):
-
- def __init__(self): # type: () -> None
- super(Requirement, self).__init__()
- self.value = None # type: Optional[bytes]
- self.comments = [] # type: List[bytes]
+class Requirement:
+ def __init__(self) -> None:
+ self.value: Optional[bytes] = None
+ self.comments: List[bytes] = []
@property
- def name(self): # type: () -> bytes
+ def name(self) -> bytes:
assert self.value is not None, self.value
for egg in (b'#egg=', b'&egg='):
if egg in self.value:
@@ -27,7 +23,7 @@
return self.value.lower().partition(b'==')[0]
- def __lt__(self, requirement): # type: (Requirement) -> int
+ def __lt__(self, requirement: 'Requirement') -> int:
# \n means top of file comment, so always return True,
# otherwise just do a string comparison with value.
assert self.value is not None, self.value
@@ -39,10 +35,10 @@
return self.name < requirement.name
-def fix_requirements(f): # type: (IO[bytes]) -> int
- requirements = [] # type: List[Requirement]
+def fix_requirements(f: IO[bytes]) -> int:
+ requirements: List[Requirement] = []
before = list(f)
- after = [] # type: List[bytes]
+ after: List[bytes] = []
before_string = b''.join(before)
@@ -109,7 +105,7 @@
return FAIL
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to fix')
args = parser.parse_args(argv)
@@ -121,7 +117,7 @@
ret_for_file = fix_requirements(file_obj)
if ret_for_file:
- print('Sorting {}'.format(arg))
+ print(f'Sorting {arg}')
retv |= ret_for_file
diff --git a/pre_commit_hooks/sort_simple_yaml.py b/pre_commit_hooks/sort_simple_yaml.py
index a381679..8ebc84f 100755
--- a/pre_commit_hooks/sort_simple_yaml.py
+++ b/pre_commit_hooks/sort_simple_yaml.py
@@ -18,8 +18,6 @@
In other words, we don't sort deeper than the top layer, and might corrupt
complicated YAML files.
"""
-from __future__ import print_function
-
import argparse
from typing import List
from typing import Optional
@@ -29,7 +27,7 @@
QUOTES = ["'", '"']
-def sort(lines): # type: (List[str]) -> List[str]
+def sort(lines: List[str]) -> List[str]:
"""Sort a YAML file in alphabetical order, keeping blocks together.
:param lines: array of strings (without newlines)
@@ -47,7 +45,7 @@
return new_lines
-def parse_block(lines, header=False): # type: (List[str], bool) -> 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`.
If parsing a header block, we stop after we reach a line that is not a
@@ -63,7 +61,7 @@
return block_lines
-def parse_blocks(lines): # type: (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`.
:param lines: list of lines
@@ -80,7 +78,7 @@
return blocks
-def first_key(lines): # type: (List[str]) -> str
+def first_key(lines: List[str]) -> str:
"""Returns a string representing the sort key of a block.
The sort key is the first YAML key we encounter, ignoring comments, and
@@ -102,7 +100,7 @@
return '' # not actually reached in reality
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to fix')
args = parser.parse_args(argv)
@@ -115,7 +113,7 @@
new_lines = sort(lines)
if lines != new_lines:
- print('Fixing file `{filename}`'.format(filename=filename))
+ print(f'Fixing file `{filename}`')
f.seek(0)
f.write('\n'.join(new_lines) + '\n')
f.truncate()
diff --git a/pre_commit_hooks/string_fixer.py b/pre_commit_hooks/string_fixer.py
index 813ef64..3fdb6e2 100644
--- a/pre_commit_hooks/string_fixer.py
+++ b/pre_commit_hooks/string_fixer.py
@@ -1,7 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import argparse
import io
import re
@@ -13,7 +9,7 @@
START_QUOTE_RE = re.compile('^[a-zA-Z]*"')
-def handle_match(token_text): # type: (str) -> str
+def handle_match(token_text: str) -> str:
if '"""' in token_text or "'''" in token_text:
return token_text
@@ -28,7 +24,7 @@
return token_text
-def get_line_offsets_by_line_no(src): # type: (str) -> List[int]
+def get_line_offsets_by_line_no(src: str) -> List[int]:
# Padded so we can index with line number
offsets = [-1, 0]
for line in src.splitlines(True):
@@ -36,8 +32,8 @@
return offsets
-def fix_strings(filename): # type: (str) -> int
- with io.open(filename, encoding='UTF-8', newline='') as f:
+def fix_strings(filename: str) -> int:
+ with open(filename, encoding='UTF-8', newline='') as f:
contents = f.read()
line_offsets = get_line_offsets_by_line_no(contents)
@@ -57,14 +53,14 @@
new_contents = ''.join(splitcontents)
if contents != new_contents:
- with io.open(filename, 'w', encoding='UTF-8', newline='') as f:
+ with open(filename, 'w', encoding='UTF-8', newline='') as f:
f.write(new_contents)
return 1
else:
return 0
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to fix')
args = parser.parse_args(argv)
@@ -74,7 +70,7 @@
for filename in args.filenames:
return_value = fix_strings(filename)
if return_value != 0:
- print('Fixing strings in {}'.format(filename))
+ print(f'Fixing strings in {filename}')
retv |= return_value
return retv
diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py
index d93595f..b8cf915 100644
--- a/pre_commit_hooks/tests_should_end_in_test.py
+++ b/pre_commit_hooks/tests_should_end_in_test.py
@@ -1,14 +1,11 @@
-from __future__ import print_function
-
import argparse
import os.path
import re
-import sys
from typing import Optional
from typing import Sequence
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*')
parser.add_argument(
@@ -27,14 +24,10 @@
not base == 'conftest.py'
):
retcode = 1
- print(
- '{} does not match pattern "{}"'.format(
- filename, test_name_pattern,
- ),
- )
+ print(f'{filename} does not match pattern "{test_name_pattern}"')
return retcode
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/trailing_whitespace_fixer.py b/pre_commit_hooks/trailing_whitespace_fixer.py
index a21b54f..05ed999 100644
--- a/pre_commit_hooks/trailing_whitespace_fixer.py
+++ b/pre_commit_hooks/trailing_whitespace_fixer.py
@@ -1,14 +1,14 @@
-from __future__ import print_function
-
import argparse
import os
-import sys
from typing import Optional
from typing import Sequence
-def _fix_file(filename, is_markdown, chars):
- # type: (str, bool, Optional[bytes]) -> bool
+def _fix_file(
+ filename: str,
+ is_markdown: bool,
+ chars: Optional[bytes],
+) -> bool:
with open(filename, mode='rb') as file_processed:
lines = file_processed.readlines()
newlines = [_process_line(line, is_markdown, chars) for line in lines]
@@ -21,8 +21,11 @@
return False
-def _process_line(line, is_markdown, chars):
- # type: (bytes, bool, Optional[bytes]) -> bytes
+def _process_line(
+ line: bytes,
+ is_markdown: bool,
+ chars: Optional[bytes],
+) -> bytes:
if line[-2:] == b'\r\n':
eol = b'\r\n'
line = line[:-2]
@@ -37,7 +40,7 @@
return line.rstrip(chars) + eol
-def main(argv=None): # type: (Optional[Sequence[str]]) -> int
+def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
'--no-markdown-linebreak-ext',
@@ -80,20 +83,20 @@
for ext in md_exts:
if any(c in ext[1:] for c in r'./\:'):
parser.error(
- 'bad --markdown-linebreak-ext extension {!r} (has . / \\ :)\n'
- " (probably filename; use '--markdown-linebreak-ext=EXT')"
- .format(ext),
+ f'bad --markdown-linebreak-ext extension '
+ f'{ext!r} (has . / \\ :)\n'
+ f" (probably filename; use '--markdown-linebreak-ext=EXT')",
)
- chars = None if args.chars is None else args.chars.encode('utf-8')
+ chars = None if args.chars is None else args.chars.encode()
return_code = 0
for filename in args.filenames:
_, extension = os.path.splitext(filename.lower())
md = all_markdown or extension in md_exts
if _fix_file(filename, md, chars):
- print('Fixing {}'.format(filename))
+ print(f'Fixing {filename}')
return_code = 1
return return_code
if __name__ == '__main__':
- sys.exit(main())
+ exit(main())
diff --git a/pre_commit_hooks/util.py b/pre_commit_hooks/util.py
index 3b960e3..e04b015 100644
--- a/pre_commit_hooks/util.py
+++ b/pre_commit_hooks/util.py
@@ -1,9 +1,6 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import subprocess
from typing import Any
+from typing import Optional
from typing import Set
@@ -11,18 +8,17 @@
pass
-def added_files(): # type: () -> Set[str]
+def added_files() -> Set[str]:
cmd = ('git', 'diff', '--staged', '--name-only', '--diff-filter=A')
return set(cmd_output(*cmd).splitlines())
-def cmd_output(*cmd, **kwargs): # type: (*str, **Any) -> str
- retcode = kwargs.pop('retcode', 0)
+def cmd_output(*cmd: str, retcode: Optional[int] = 0, **kwargs: Any) -> str:
kwargs.setdefault('stdout', subprocess.PIPE)
kwargs.setdefault('stderr', subprocess.PIPE)
proc = subprocess.Popen(cmd, **kwargs)
stdout, stderr = proc.communicate()
- stdout = stdout.decode('UTF-8')
+ stdout = stdout.decode()
if retcode is not None and proc.returncode != retcode:
raise CalledProcessError(cmd, retcode, proc.returncode, stdout, stderr)
return stdout
diff --git a/setup.cfg b/setup.cfg
index 4b793f7..6b1a34d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -11,13 +11,11 @@
license_file = LICENSE
classifiers =
License :: OSI Approved :: MIT License
- Programming Language :: Python :: 2
- Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.4
- Programming Language :: Python :: 3.5
+ Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
+ Programming Language :: Python :: 3.8
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy
@@ -27,9 +25,7 @@
flake8
ruamel.yaml>=0.15
toml
- six
- typing; python_version<"3.5"
-python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+python_requires = >=3.6
[options.entry_points]
console_scripts =
diff --git a/testing/util.py b/testing/util.py
index fac498c..8e468d6 100644
--- a/testing/util.py
+++ b/testing/util.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import os.path
diff --git a/tests/autopep8_wrapper_test.py b/tests/autopep8_wrapper_test.py
index 615ec25..f8030b5 100644
--- a/tests/autopep8_wrapper_test.py
+++ b/tests/autopep8_wrapper_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.autopep8_wrapper import main
diff --git a/tests/check_added_large_files_test.py b/tests/check_added_large_files_test.py
index 2f67d1b..c33a9ca 100644
--- a/tests/check_added_large_files_test.py
+++ b/tests/check_added_large_files_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import distutils.spawn
import pytest
@@ -78,7 +75,7 @@
@xfailif_no_gitlfs
def test_allows_gitlfs(temp_git_dir, monkeypatch): # pragma: no cover
with temp_git_dir.as_cwd():
- monkeypatch.setenv(str('HOME'), str(temp_git_dir.strpath))
+ monkeypatch.setenv('HOME', str(temp_git_dir.strpath))
cmd_output('git', 'lfs', 'install')
temp_git_dir.join('f.py').write('a' * 10000)
cmd_output('git', 'lfs', 'track', 'f.py')
@@ -90,7 +87,7 @@
@xfailif_no_gitlfs
def test_moves_with_gitlfs(temp_git_dir, monkeypatch): # pragma: no cover
with temp_git_dir.as_cwd():
- monkeypatch.setenv(str('HOME'), str(temp_git_dir.strpath))
+ monkeypatch.setenv('HOME', str(temp_git_dir.strpath))
cmd_output('git', 'lfs', 'install')
cmd_output('git', 'lfs', 'track', 'a.bin', 'b.bin')
# First add the file we're going to move
diff --git a/tests/check_ast_test.py b/tests/check_ast_test.py
index c16f5fc..686fd11 100644
--- a/tests/check_ast_test.py
+++ b/tests/check_ast_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
from pre_commit_hooks.check_ast import main
from testing.util import get_resource_path
diff --git a/tests/check_builtin_literals_test.py b/tests/check_builtin_literals_test.py
index 8e18854..01193e8 100644
--- a/tests/check_builtin_literals_test.py
+++ b/tests/check_builtin_literals_test.py
@@ -7,7 +7,7 @@
from pre_commit_hooks.check_builtin_literals import Visitor
BUILTIN_CONSTRUCTORS = '''\
-from six.moves import builtins
+import builtins
c1 = complex()
d1 = dict()
diff --git a/tests/check_byte_order_marker_test.py b/tests/check_byte_order_marker_test.py
index 53cb4a1..9995200 100644
--- a/tests/check_byte_order_marker_test.py
+++ b/tests/check_byte_order_marker_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
from pre_commit_hooks import check_byte_order_marker
diff --git a/tests/check_case_conflict_test.py b/tests/check_case_conflict_test.py
index 077b41b..53de852 100644
--- a/tests/check_case_conflict_test.py
+++ b/tests/check_case_conflict_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
from pre_commit_hooks.check_case_conflict import find_conflicting_filenames
from pre_commit_hooks.check_case_conflict import main
from pre_commit_hooks.util import cmd_output
diff --git a/tests/check_docstring_first_test.py b/tests/check_docstring_first_test.py
index 0973a58..7ad876f 100644
--- a/tests/check_docstring_first_test.py
+++ b/tests/check_docstring_first_test.py
@@ -1,7 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.check_docstring_first import check_docstring_first
diff --git a/tests/check_executables_have_shebangs_test.py b/tests/check_executables_have_shebangs_test.py
index 0cb9dcf..15f0c79 100644
--- a/tests/check_executables_have_shebangs_test.py
+++ b/tests/check_executables_have_shebangs_test.py
@@ -1,7 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.check_executables_have_shebangs import main
@@ -12,7 +8,7 @@
b'#!/bin/bash\nhello world\n',
b'#!/usr/bin/env python3.6',
b'#!python',
- '#!☃'.encode('UTF-8'),
+ '#!☃'.encode(),
),
)
def test_has_shebang(content, tmpdir):
@@ -27,7 +23,7 @@
b' #!python\n',
b'\n#!python\n',
b'python\n',
- '☃'.encode('UTF-8'),
+ '☃'.encode(),
),
)
@@ -36,4 +32,4 @@
path.write(content, 'wb')
assert main((path.strpath,)) == 1
_, stderr = capsys.readouterr()
- assert stderr.startswith('{}: marked executable but'.format(path.strpath))
+ assert stderr.startswith(f'{path}: marked executable but')
diff --git a/tests/check_merge_conflict_test.py b/tests/check_merge_conflict_test.py
index af7cc43..9968507 100644
--- a/tests/check_merge_conflict_test.py
+++ b/tests/check_merge_conflict_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import os
import shutil
diff --git a/tests/check_toml_test.py b/tests/check_toml_test.py
index 1172c40..9f186d1 100644
--- a/tests/check_toml_test.py
+++ b/tests/check_toml_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
from pre_commit_hooks.check_toml import main
diff --git a/tests/check_vcs_permalinks_test.py b/tests/check_vcs_permalinks_test.py
index 00e5396..b893c98 100644
--- a/tests/check_vcs_permalinks_test.py
+++ b/tests/check_vcs_permalinks_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
from pre_commit_hooks.check_vcs_permalinks import main
diff --git a/tests/check_yaml_test.py b/tests/check_yaml_test.py
index d267150..2f869d1 100644
--- a/tests/check_yaml_test.py
+++ b/tests/check_yaml_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.check_yaml import main
diff --git a/tests/conftest.py b/tests/conftest.py
index da206cb..f98ae34 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,7 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.util import cmd_output
diff --git a/tests/debug_statement_hook_test.py b/tests/debug_statement_hook_test.py
index d15f5f7..f2cabc1 100644
--- a/tests/debug_statement_hook_test.py
+++ b/tests/debug_statement_hook_test.py
@@ -1,7 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import ast
from pre_commit_hooks.debug_statement_hook import Debug
diff --git a/tests/fix_encoding_pragma_test.py b/tests/fix_encoding_pragma_test.py
index d94b725..f3531f2 100644
--- a/tests/fix_encoding_pragma_test.py
+++ b/tests/fix_encoding_pragma_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import io
import pytest
@@ -129,9 +126,6 @@
@pytest.mark.parametrize(
('input_s', 'expected'),
(
- # Python 2 cli parameters are bytes
- (b'# coding: utf-8', b'# coding: utf-8'),
- # Python 3 cli parameters are text
('# coding: utf-8', b'# coding: utf-8'),
# trailing whitespace
('# coding: utf-8\n', b'# coding: utf-8'),
@@ -149,7 +143,7 @@
assert main((f.strpath, '--pragma', pragma)) == 1
assert f.read() == '# coding: utf-8\nx = 1\n'
out, _ = capsys.readouterr()
- assert out == 'Added `# coding: utf-8` to {}\n'.format(f.strpath)
+ assert out == f'Added `# coding: utf-8` to {f.strpath}\n'
def test_crlf_ok(tmpdir):
diff --git a/tests/forbid_new_submodules_test.py b/tests/forbid_new_submodules_test.py
index 523628d..7619182 100644
--- a/tests/forbid_new_submodules_test.py
+++ b/tests/forbid_new_submodules_test.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import
-
import subprocess
import pytest
diff --git a/tests/mixed_line_ending_test.py b/tests/mixed_line_ending_test.py
index 8ae9354..c438f74 100644
--- a/tests/mixed_line_ending_test.py
+++ b/tests/mixed_line_ending_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.mixed_line_ending import main
@@ -86,7 +83,7 @@
assert ret == 1
assert path.read_binary() == contents
out, _ = capsys.readouterr()
- assert out == '{}: mixed line endings\n'.format(path)
+ assert out == f'{path}: mixed line endings\n'
def test_fix_lf(tmpdir, capsys):
@@ -97,7 +94,7 @@
assert ret == 1
assert path.read_binary() == b'foo\nbar\nbaz\n'
out, _ = capsys.readouterr()
- assert out == '{}: fixed mixed line endings\n'.format(path)
+ assert out == f'{path}: fixed mixed line endings\n'
def test_fix_crlf(tmpdir):
diff --git a/tests/no_commit_to_branch_test.py b/tests/no_commit_to_branch_test.py
index a2ab1f1..72b32e6 100644
--- a/tests/no_commit_to_branch_test.py
+++ b/tests/no_commit_to_branch_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.no_commit_to_branch import is_on_branch
diff --git a/tests/pretty_format_json_test.py b/tests/pretty_format_json_test.py
index b42e504..59a87f0 100644
--- a/tests/pretty_format_json_test.py
+++ b/tests/pretty_format_json_test.py
@@ -2,7 +2,6 @@
import shutil
import pytest
-from six import PY2
from pre_commit_hooks.pretty_format_json import main
from pre_commit_hooks.pretty_format_json import parse_num_to_int
@@ -42,7 +41,6 @@
assert ret == expected_retval
-@pytest.mark.skipif(PY2, reason='Requires Python3')
@pytest.mark.parametrize(
('filename', 'expected_retval'), (
('not_pretty_formatted_json.json', 1),
@@ -52,7 +50,7 @@
('tab_pretty_formatted_json.json', 0),
),
)
-def test_tab_main(filename, expected_retval): # pragma: no cover
+def test_tab_main(filename, expected_retval):
ret = main(['--indent', '\t', get_resource_path(filename)])
assert ret == expected_retval
@@ -113,9 +111,9 @@
expected_retval = 1
a = os.path.join('a', resource_path)
b = os.path.join('b', resource_path)
- expected_out = '''\
---- {}
-+++ {}
+ expected_out = f'''\
+--- {a}
++++ {b}
@@ -1,6 +1,9 @@
{{
- "foo":
@@ -130,7 +128,7 @@
+ "blah": null,
+ "foo": "bar"
}}
-'''.format(a, b)
+'''
actual_retval = main([resource_path])
actual_out, actual_err = capsys.readouterr()
diff --git a/tests/readme_test.py b/tests/readme_test.py
index fd6d265..7df7fcf 100644
--- a/tests/readme_test.py
+++ b/tests/readme_test.py
@@ -1,15 +1,10 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
-import io
-
from pre_commit_hooks.check_yaml import yaml
def test_readme_contains_all_hooks():
- with io.open('README.md', encoding='UTF-8') as f:
+ with open('README.md', encoding='UTF-8') as f:
readme_contents = f.read()
- with io.open('.pre-commit-hooks.yaml', encoding='UTF-8') as f:
+ with open('.pre-commit-hooks.yaml', encoding='UTF-8') as f:
hooks = yaml.load(f)
for hook in hooks:
- assert '`{}`'.format(hook['id']) in readme_contents
+ assert f'`{hook["id"]}`' in readme_contents
diff --git a/tests/sort_simple_yaml_test.py b/tests/sort_simple_yaml_test.py
index 4261d5d..69ad388 100644
--- a/tests/sort_simple_yaml_test.py
+++ b/tests/sort_simple_yaml_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import os
import pytest
diff --git a/tests/string_fixer_test.py b/tests/string_fixer_test.py
index 4adca4a..77a51cf 100644
--- a/tests/string_fixer_test.py
+++ b/tests/string_fixer_test.py
@@ -1,7 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-from __future__ import unicode_literals
-
import textwrap
import pytest
diff --git a/tests/trailing_whitespace_fixer_test.py b/tests/trailing_whitespace_fixer_test.py
index 97f9aef..53177ac 100644
--- a/tests/trailing_whitespace_fixer_test.py
+++ b/tests/trailing_whitespace_fixer_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.trailing_whitespace_fixer import main
@@ -46,7 +43,7 @@
'\t\n' # trailing tabs are stripped anyway
'\n ', # whitespace at the end of the file is removed
)
- ret = main((path.strpath, '--markdown-linebreak-ext={}'.format(ext)))
+ ret = main((path.strpath, f'--markdown-linebreak-ext={ext}'))
assert ret == 1
assert path.read() == (
'foo \n'
diff --git a/tests/util_test.py b/tests/util_test.py
index 9b2d723..b42ee6f 100644
--- a/tests/util_test.py
+++ b/tests/util_test.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
import pytest
from pre_commit_hooks.util import CalledProcessError
diff --git a/tox.ini b/tox.ini
index a6b3fb4..cb58fee 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py27,py36,py37,pypy,pypy3,pre-commit
+envlist = py36,py37,py38,pypy3,pre-commit
[testenv]
deps = -rrequirements-dev.txt