pre-commit-hooks: python3.6+

This commit is contained in:
Anthony Sottile 2020-02-05 11:10:42 -08:00
parent 551d1a07b3
commit f5c42a050b
60 changed files with 291 additions and 493 deletions

View file

@ -28,17 +28,22 @@ repos:
rev: v1.9.0 rev: v1.9.0
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
language_version: python3 args: [--py3-plus]
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v1.26.2 rev: v1.26.2
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py36-plus]
- repo: https://github.com/asottile/add-trailing-comma - repo: https://github.com/asottile/add-trailing-comma
rev: v1.5.0 rev: v1.5.0
hooks: hooks:
- id: add-trailing-comma - 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 - repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.761 rev: v0.761
hooks: hooks:
- id: mypy - id: mypy
language_version: python3

View file

@ -16,9 +16,9 @@ jobs:
- template: job--pre-commit.yml@asottile - template: job--pre-commit.yml@asottile
- template: job--python-tox.yml@asottile - template: job--python-tox.yml@asottile
parameters: parameters:
toxenvs: [py27, py37] toxenvs: [py38]
os: windows os: windows
- template: job--python-tox.yml@asottile - template: job--python-tox.yml@asottile
parameters: parameters:
toxenvs: [pypy, pypy3, py27, py36, py37] toxenvs: [pypy3, py36, py37, py38]
os: linux os: linux

View file

@ -1,9 +1,4 @@
from __future__ import absolute_import def main() -> int:
from __future__ import print_function
from __future__ import unicode_literals
def main(): # type: () -> int
raise SystemExit( raise SystemExit(
'autopep8-wrapper is deprecated. Instead use autopep8 directly via ' 'autopep8-wrapper is deprecated. Instead use autopep8 directly via '
'https://github.com/pre-commit/mirrors-autopep8', 'https://github.com/pre-commit/mirrors-autopep8',

View file

@ -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 argparse
import json import json
import math import math
import os import os
from typing import Iterable
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set from typing import Set
@ -17,7 +11,7 @@ from pre_commit_hooks.util import CalledProcessError
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output
def lfs_files(): # type: () -> Set[str] def lfs_files() -> Set[str]:
try: try:
# Introduced in git-lfs 2.2.0, first working in 2.2.1 # Introduced in git-lfs 2.2.0, first working in 2.2.1
lfs_ret = cmd_output('git', 'lfs', 'status', '--json') lfs_ret = cmd_output('git', 'lfs', 'status', '--json')
@ -27,23 +21,20 @@ def lfs_files(): # type: () -> Set[str]
return set(json.loads(lfs_ret)['files']) return set(json.loads(lfs_ret)['files'])
def find_large_added_files(filenames, maxkb): def find_large_added_files(filenames: Sequence[str], maxkb: int) -> int:
# type: (Iterable[str], int) -> int
# Find all added files that are also in the list of files pre-commit tells # Find all added files that are also in the list of files pre-commit tells
# us about # us about
filenames = (added_files() & set(filenames)) - lfs_files()
retv = 0 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)) kb = int(math.ceil(os.stat(filename).st_size / 1024))
if kb > maxkb: if kb > maxkb:
print('{} ({} KB) exceeds {} KB.'.format(filename, kb, maxkb)) print(f'{filename} ({kb} KB) exceeds {maxkb} KB.')
retv = 1 retv = 1
return retv return retv
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'filenames', nargs='*', 'filenames', nargs='*',

View file

@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import ast import ast
import platform import platform
@ -11,7 +7,7 @@ from typing import Optional
from typing import Sequence 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 = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv) args = parser.parse_args(argv)
@ -23,15 +19,11 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
ast.parse(f.read(), filename=filename) ast.parse(f.read(), filename=filename)
except SyntaxError: except SyntaxError:
print( impl = platform.python_implementation()
'{}: failed parsing with {} {}:'.format( version = sys.version.split()[0]
filename, print(f'{filename}: failed parsing with {impl} {version}:')
platform.python_implementation(),
sys.version.partition(' ')[0],
),
)
tb = ' ' + traceback.format_exc().replace('\n', '\n ') tb = ' ' + traceback.format_exc().replace('\n', '\n ')
print('\n{}'.format(tb)) print(f'\n{tb}')
retval = 1 retval = 1
return retval return retval

View file

@ -1,10 +1,7 @@
from __future__ import unicode_literals
import argparse import argparse
import ast import ast
import collections
import sys
from typing import List from typing import List
from typing import NamedTuple
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set from typing import Set
@ -21,23 +18,26 @@ BUILTIN_TYPES = {
} }
Call = collections.namedtuple('Call', ['name', 'line', 'column']) class Call(NamedTuple):
name: str
line: int
column: int
class Visitor(ast.NodeVisitor): class Visitor(ast.NodeVisitor):
def __init__(self, ignore=None, allow_dict_kwargs=True): def __init__(
# type: (Optional[Sequence[str]], bool) -> None self,
self.builtin_type_calls = [] # type: List[Call] 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.ignore = set(ignore) if ignore else set()
self.allow_dict_kwargs = allow_dict_kwargs self.allow_dict_kwargs = allow_dict_kwargs
def _check_dict_call(self, node): # type: (ast.Call) -> bool def _check_dict_call(self, node: ast.Call) -> bool:
return ( return self.allow_dict_kwargs and bool(node.keywords)
self.allow_dict_kwargs and
(getattr(node, 'kwargs', None) or getattr(node, 'keywords', None))
)
def visit_Call(self, node): # type: (ast.Call) -> None def visit_Call(self, node: ast.Call) -> None:
if not isinstance(node.func, ast.Name): if not isinstance(node.func, ast.Name):
# Ignore functions that are object attributes (`foo.bar()`). # Ignore functions that are object attributes (`foo.bar()`).
# Assume that if the user calls `builtins.list()`, they know what # Assume that if the user calls `builtins.list()`, they know what
@ -54,8 +54,11 @@ class Visitor(ast.NodeVisitor):
) )
def check_file(filename, ignore=None, allow_dict_kwargs=True): def check_file(
# type: (str, Optional[Sequence[str]], bool) -> List[Call] filename: str,
ignore: Optional[Sequence[str]] = None,
allow_dict_kwargs: bool = True,
) -> 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)
@ -63,11 +66,11 @@ def check_file(filename, ignore=None, allow_dict_kwargs=True):
return visitor.builtin_type_calls return visitor.builtin_type_calls
def parse_ignore(value): # type: (str) -> Set[str] def parse_ignore(value: str) -> Set[str]:
return set(value.split(',')) 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 = 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())
@ -93,15 +96,11 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
rc = rc or 1 rc = rc or 1
for call in calls: for call in calls:
print( print(
'{filename}:{call.line}:{call.column}: ' f'{filename}:{call.line}:{call.column}: '
'replace {call.name}() with {replacement}'.format( f'replace {call.name}() with {BUILTIN_TYPES[call.name]}',
filename=filename,
call=call,
replacement=BUILTIN_TYPES[call.name],
),
) )
return rc return rc
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,13 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
from typing import Optional from typing import Optional
from typing import Sequence 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 = 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)
@ -18,7 +14,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
if f.read(3) == b'\xef\xbb\xbf': if f.read(3) == b'\xef\xbb\xbf':
retv = 1 retv = 1
print('{}: Has a byte-order marker'.format(filename)) print(f'{filename}: Has a byte-order marker')
return retv return retv

View file

@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
from typing import Iterable from typing import Iterable
from typing import Optional from typing import Optional
@ -12,11 +8,11 @@ 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): # type: (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}
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()) repo_files = set(cmd_output('git', 'ls-files').splitlines())
relevant_files = set(filenames) | added_files() relevant_files = set(filenames) | added_files()
repo_files -= relevant_files repo_files -= relevant_files
@ -39,13 +35,13 @@ def find_conflicting_filenames(filenames): # type: (Sequence[str]) -> int
if x.lower() in conflicts if x.lower() in conflicts
] ]
for filename in sorted(conflicting_files): for filename in sorted(conflicting_files):
print('Case-insensitivity conflict found: {}'.format(filename)) print(f'Case-insensitivity conflict found: {filename}')
retv = 1 retv = 1
return retv return retv
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'filenames', nargs='*', 'filenames', nargs='*',

View file

@ -1,30 +1,17 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import io import io
import tokenize import tokenize
from tokenize import tokenize as tokenize_tokenize
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
import six NON_CODE_TOKENS = frozenset((
tokenize.COMMENT, tokenize.ENDMARKER, tokenize.NEWLINE, tokenize.NL,
if six.PY2: # pragma: no cover (PY2) tokenize.ENCODING,
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,
)
def check_docstring_first(src, filename='<unknown>'): def check_docstring_first(src: bytes, filename: str = '<unknown>') -> int:
# type: (bytes, str) -> int
"""Returns nonzero if the source has what looks like a docstring that is """Returns nonzero if the source has what looks like a docstring that is
not at the beginning of the source. not at the beginning of the source.
@ -40,18 +27,14 @@ def check_docstring_first(src, filename='<unknown>'):
if tok_type == tokenize.STRING and scol == 0: if tok_type == tokenize.STRING and scol == 0:
if found_docstring_line is not None: if found_docstring_line is not None:
print( print(
'{}:{} Multiple module docstrings ' f'{filename}:{sline} Multiple module docstrings '
'(first docstring on line {}).'.format( f'(first docstring on line {found_docstring_line}).',
filename, sline, found_docstring_line,
),
) )
return 1 return 1
elif found_code_line is not None: elif found_code_line is not None:
print( print(
'{}:{} Module docstring appears after code ' f'{filename}:{sline} Module docstring appears after code '
'(code seen on line {}).'.format( f'(code seen on line {found_code_line}).',
filename, sline, found_code_line,
),
) )
return 1 return 1
else: else:
@ -62,7 +45,7 @@ def check_docstring_first(src, filename='<unknown>'):
return 0 return 0
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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,28 +1,22 @@
"""Check that executable text files have a shebang.""" """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 argparse
import pipes import shlex
import sys import sys
from typing import Optional from typing import Optional
from typing import Sequence 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: with open(path, 'rb') as f:
first_bytes = f.read(2) first_bytes = f.read(2)
if first_bytes != b'#!': if first_bytes != b'#!':
quoted = shlex.quote(path)
print( print(
'{path}: marked executable but has no (or invalid) shebang!\n' f'{path}: marked executable but has no (or invalid) shebang!\n'
" If it isn't supposed to be executable, try: chmod -x {quoted}\n" f" If it isn't supposed to be executable, try: "
' If it is supposed to be executable, double-check its shebang.' f'`chmod -x {quoted}`\n'
.format( f' If it is supposed to be executable, double-check its shebang.',
path=path,
quoted=pipes.quote(path),
),
file=sys.stderr, file=sys.stderr,
) )
return 1 return 1
@ -30,7 +24,7 @@ def check_has_shebang(path): # type: (str) -> int
return 0 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 = 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,27 +1,25 @@
from __future__ import print_function
import argparse import argparse
import io
import json import json
import sys
from typing import Optional from typing import Optional
from typing import Sequence 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 = 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)
retval = 0 retval = 0
for filename in args.filenames: for filename in args.filenames:
with open(filename, 'rb') as f:
try: try:
json.load(io.open(filename, encoding='UTF-8')) json.load(f)
# TODO: need UnicodeDecodeError?
except (ValueError, UnicodeDecodeError) as exc: except (ValueError, UnicodeDecodeError) as exc:
print('{}: Failed to json decode ({})'.format(filename, exc)) print(f'{filename}: Failed to json decode ({exc})')
retval = 1 retval = 1
return retval return retval
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,5 +1,3 @@
from __future__ import print_function
import argparse import argparse
import os.path import os.path
from typing import Optional from typing import Optional
@ -12,10 +10,9 @@ CONFLICT_PATTERNS = [
b'=======\n', b'=======\n',
b'>>>>>>> ', b'>>>>>>> ',
] ]
WARNING_MSG = 'Merge conflict string "{0}" found in {1}:{2}'
def is_in_merge(): # type: () -> int def is_in_merge() -> int:
return ( return (
os.path.exists(os.path.join('.git', 'MERGE_MSG')) and os.path.exists(os.path.join('.git', 'MERGE_MSG')) and
( (
@ -26,7 +23,7 @@ def is_in_merge(): # type: () -> int
) )
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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')
@ -42,9 +39,8 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
for pattern in CONFLICT_PATTERNS: for pattern in CONFLICT_PATTERNS:
if line.startswith(pattern): if line.startswith(pattern):
print( print(
WARNING_MSG.format( f'Merge conflict string "{pattern.decode()}" '
pattern.decode(), filename, i + 1, f'found in {filename}:{i + 1}',
),
) )
retcode = 1 retcode = 1

View file

@ -1,14 +1,10 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import os.path import os.path
from typing import Optional from typing import Optional
from typing import Sequence 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 = 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)
@ -20,7 +16,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
os.path.islink(filename) and os.path.islink(filename) and
not os.path.exists(filename) not os.path.exists(filename)
): # pragma: no cover (symlink support required) ): # pragma: no cover (symlink support required)
print('{}: Broken symlink'.format(filename)) print(f'{filename}: Broken symlink')
retv = 1 retv = 1
return retv return retv

View file

@ -1,14 +1,11 @@
from __future__ import print_function
import argparse import argparse
import sys
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
import toml import toml
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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)
@ -19,10 +16,10 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
with open(filename) as f: with open(filename) as f:
toml.load(f) toml.load(f)
except toml.TomlDecodeError as exc: except toml.TomlDecodeError as exc:
print('{}: {}'.format(filename, exc)) print(f'{filename}: {exc}')
retval = 1 retval = 1
return retval return retval
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import re import re
import sys import sys
@ -14,19 +10,19 @@ GITHUB_NON_PERMALINK = re.compile(
) )
def _check_filename(filename): # type: (str) -> int def _check_filename(filename: str) -> 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):
if GITHUB_NON_PERMALINK.search(line): if GITHUB_NON_PERMALINK.search(line):
sys.stdout.write('{}:{}:'.format(filename, i)) sys.stdout.write(f'{filename}:{i}:')
sys.stdout.flush() sys.stdout.flush()
getattr(sys.stdout, 'buffer', sys.stdout).write(line) sys.stdout.buffer.write(line)
retv = 1 retv = 1
return retv return retv
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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,30 +1,26 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import io
import sys
import xml.sax.handler import xml.sax.handler
from typing import Optional from typing import Optional
from typing import Sequence 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 = 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)
retval = 0 retval = 0
handler = xml.sax.handler.ContentHandler()
for filename in args.filenames: for filename in args.filenames:
try: try:
with io.open(filename, 'rb') as xml_file: with open(filename, 'rb') as xml_file:
xml.sax.parse(xml_file, xml.sax.handler.ContentHandler()) # https://github.com/python/typeshed/pull/3725
xml.sax.parse(xml_file, handler) # type: ignore
except xml.sax.SAXException as exc: except xml.sax.SAXException as exc:
print('{}: Failed to xml parse ({})'.format(filename, exc)) print(f'{filename}: Failed to xml parse ({exc})')
retval = 1 retval = 1
return retval return retval
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,11 +1,7 @@
from __future__ import print_function
import argparse import argparse
import collections
import io
import sys
from typing import Any from typing import Any
from typing import Generator from typing import Generator
from typing import NamedTuple
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
@ -14,20 +10,24 @@ import ruamel.yaml
yaml = ruamel.yaml.YAML(typ='safe') 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: for _ in gen:
pass pass
def _parse_unsafe(*args, **kwargs): # type: (*Any, **Any) -> None def _parse_unsafe(*args: Any, **kwargs: Any) -> None:
_exhaust(yaml.parse(*args, **kwargs)) _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)) _exhaust(yaml.load_all(*args, **kwargs))
Key = collections.namedtuple('Key', ('multi', 'unsafe')) class Key(NamedTuple):
multi: bool
unsafe: bool
LOAD_FNS = { LOAD_FNS = {
Key(multi=False, unsafe=False): yaml.load, Key(multi=False, unsafe=False): yaml.load,
Key(multi=False, unsafe=True): _parse_unsafe, Key(multi=False, unsafe=True): _parse_unsafe,
@ -36,7 +36,7 @@ LOAD_FNS = {
} }
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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',
@ -59,7 +59,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
retval = 0 retval = 0
for filename in args.filenames: for filename in args.filenames:
try: try:
with io.open(filename, encoding='UTF-8') as f: with open(filename, encoding='UTF-8') as f:
load_fn(f) load_fn(f)
except ruamel.yaml.YAMLError as exc: except ruamel.yaml.YAMLError as exc:
print(exc) print(exc)
@ -68,4 +68,4 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,35 +1,38 @@
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import ast import ast
import collections
import traceback import traceback
from typing import List from typing import List
from typing import NamedTuple
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
DEBUG_STATEMENTS = {'pdb', 'ipdb', 'pudb', 'q', 'rdb', 'rpdb'} 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): class DebugStatementParser(ast.NodeVisitor):
def __init__(self): # type: () -> None def __init__(self) -> None:
self.breakpoints = [] # type: List[Debug] 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: for name in node.names:
if name.name in DEBUG_STATEMENTS: if name.name in DEBUG_STATEMENTS:
st = Debug(node.lineno, node.col_offset, name.name, 'imported') st = Debug(node.lineno, node.col_offset, name.name, 'imported')
self.breakpoints.append(st) 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: if node.module in DEBUG_STATEMENTS:
st = Debug(node.lineno, node.col_offset, node.module, 'imported') st = Debug(node.lineno, node.col_offset, node.module, 'imported')
self.breakpoints.append(st) self.breakpoints.append(st)
def visit_Call(self, node): # type: (ast.Call) -> None def visit_Call(self, node: ast.Call) -> None:
"""python3.7+ breakpoint()""" """python3.7+ breakpoint()"""
if isinstance(node.func, ast.Name) and node.func.id == 'breakpoint': if isinstance(node.func, ast.Name) and node.func.id == 'breakpoint':
st = Debug(node.lineno, node.col_offset, node.func.id, 'called') st = Debug(node.lineno, node.col_offset, node.func.id, 'called')
@ -37,12 +40,12 @@ class DebugStatementParser(ast.NodeVisitor):
self.generic_visit(node) self.generic_visit(node)
def check_file(filename): # type: (str) -> int def check_file(filename: str) -> int:
try: try:
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
ast_obj = ast.parse(f.read(), filename=filename) ast_obj = ast.parse(f.read(), filename=filename)
except SyntaxError: except SyntaxError:
print('{} - Could not parse ast'.format(filename)) print(f'{filename} - Could not parse ast')
print() print()
print('\t' + traceback.format_exc().replace('\n', '\n\t')) print('\t' + traceback.format_exc().replace('\n', '\n\t'))
print() print()
@ -52,16 +55,12 @@ def check_file(filename): # type: (str) -> int
visitor.visit(ast_obj) visitor.visit(ast_obj)
for bp in visitor.breakpoints: for bp in visitor.breakpoints:
print( print(f'{filename}:{bp.line}:{bp.col} - {bp.name} {bp.reason}')
'{}:{}:{} - {} {}'.format(
filename, bp.line, bp.col, bp.name, bp.reason,
),
)
return int(bool(visitor.breakpoints)) 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 = 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,18 +1,19 @@
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import configparser
import os import os
from typing import Dict
from typing import List from typing import List
from typing import NamedTuple
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set 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.""" """Extract credential file paths from environment variables."""
return { return {
os.environ[env_var] os.environ[env_var]
@ -24,7 +25,7 @@ def get_aws_cred_files_from_env(): # type: () -> Set[str]
} }
def get_aws_secrets_from_env(): # type: () -> 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 (
@ -35,7 +36,7 @@ def get_aws_secrets_from_env(): # type: () -> Set[str]
return keys 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. """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
@ -66,8 +67,10 @@ def get_aws_secrets_from_file(credentials_file): # type: (str) -> Set[str]
return keys return keys
def check_file_for_aws_keys(filenames, keys): def check_file_for_aws_keys(
# type: (Sequence[str], Set[str]) -> List[Dict[str, str]] filenames: Sequence[str],
keys: Set[str],
) -> 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
@ -82,13 +85,11 @@ def check_file_for_aws_keys(filenames, keys):
# naively match the entire file, low chance of incorrect # naively match the entire file, low chance of incorrect
# collision # collision
if key in text_body: if key in text_body:
bad_files.append({ bad_files.append(BadFile(filename, key[:4].ljust(28, '*')))
'filename': filename, 'key': key[:4] + '*' * 28,
})
return bad_files return bad_files
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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(
@ -117,7 +118,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> 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() # type: Set[str] 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)
@ -139,7 +140,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
bad_filenames = check_file_for_aws_keys(args.filenames, keys) bad_filenames = check_file_for_aws_keys(args.filenames, keys)
if bad_filenames: if bad_filenames:
for bad_file in 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 return 1
else: else:
return 0 return 0

View file

@ -1,7 +1,4 @@
from __future__ import print_function
import argparse import argparse
import sys
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
@ -17,7 +14,7 @@ BLACKLIST = [
] ]
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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)
@ -32,11 +29,11 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
if private_key_files: if private_key_files:
for private_key_file in 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 return 1
else: else:
return 0 return 0
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,20 +1,16 @@
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import os import os
import sys
from typing import IO from typing import IO
from typing import Optional from typing import Optional
from typing import Sequence 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 # Test for newline at end of file
# Empty files will throw IOError here # Empty files will throw IOError here
try: try:
file_obj.seek(-1, os.SEEK_END) file_obj.seek(-1, os.SEEK_END)
except IOError: except OSError:
return 0 return 0
last_character = file_obj.read(1) last_character = file_obj.read(1)
# last_character will be '' for an empty file # last_character will be '' for an empty file
@ -52,7 +48,7 @@ def fix_file(file_obj): # type: (IO[bytes]) -> int
return 0 return 0
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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)
@ -64,11 +60,11 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
with open(filename, 'rb+') as file_obj: with open(filename, 'rb+') as file_obj:
ret_for_file = fix_file(file_obj) ret_for_file = fix_file(file_obj)
if ret_for_file: if ret_for_file:
print('Fixing {}'.format(filename)) print(f'Fixing {filename}')
retv |= ret_for_file retv |= ret_for_file
return retv return retv
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -9,10 +9,7 @@ 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 print_function
import argparse import argparse
import sys
from typing import IO from typing import IO
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
@ -21,7 +18,7 @@ PASS = 0
FAIL = 1 FAIL = 1
def sort_file_contents(f): # type: (IO[bytes]) -> int def sort_file_contents(f: IO[bytes]) -> int:
before = list(f) before = list(f)
after = sorted([line.strip(b'\n\r') for line in before if line.strip()]) after = sorted([line.strip(b'\n\r') for line in before if line.strip()])
@ -37,7 +34,7 @@ def sort_file_contents(f): # type: (IO[bytes]) -> int
return FAIL return FAIL
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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')
args = parser.parse_args(argv) args = parser.parse_args(argv)
@ -49,7 +46,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
ret_for_file = sort_file_contents(file_obj) ret_for_file = sort_file_contents(file_obj)
if ret_for_file: if ret_for_file:
print('Sorting {}'.format(arg)) print(f'Sorting {arg}')
retv |= ret_for_file retv |= ret_for_file
@ -57,4 +54,4 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,18 +1,13 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import collections
from typing import IO from typing import IO
from typing import NamedTuple
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Union
DEFAULT_PRAGMA = b'# -*- coding: utf-8 -*-' DEFAULT_PRAGMA = b'# -*- coding: utf-8 -*-'
def has_coding(line): # type: (bytes) -> bool def has_coding(line: bytes) -> bool:
if not line.strip(): if not line.strip():
return False return False
return ( return (
@ -25,30 +20,30 @@ def has_coding(line): # type: (bytes) -> bool
) )
class ExpectedContents( class ExpectedContents(NamedTuple):
collections.namedtuple( shebang: bytes
'ExpectedContents', ('shebang', 'rest', 'pragma_status', 'ending'), 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: pragma_status: Optional[bool]
- True: has exactly the coding pragma expected ending: bytes
- False: missing coding pragma entirely
- None: has a coding pragma, but it does not match
"""
__slots__ = ()
@property @property
def has_any_pragma(self): # type: () -> bool def has_any_pragma(self) -> bool:
return self.pragma_status is not False 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 expected_pragma_status = not remove
return self.pragma_status is expected_pragma_status return self.pragma_status is expected_pragma_status
def _get_expected_contents(first_line, second_line, rest, expected_pragma): def _get_expected_contents(
# type: (bytes, bytes, bytes, bytes) -> ExpectedContents 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' ending = b'\r\n' if first_line.endswith(b'\r\n') else b'\n'
if first_line.startswith(b'#!'): if first_line.startswith(b'#!'):
@ -60,7 +55,7 @@ def _get_expected_contents(first_line, second_line, rest, expected_pragma):
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 = True # type: Optional[bool] pragma_status: Optional[bool] = True
elif has_coding(potential_coding): elif has_coding(potential_coding):
pragma_status = None pragma_status = None
else: else:
@ -72,8 +67,11 @@ def _get_expected_contents(first_line, second_line, rest, expected_pragma):
) )
def fix_encoding_pragma(f, remove=False, expected_pragma=DEFAULT_PRAGMA): def fix_encoding_pragma(
# type: (IO[bytes], bool, bytes) -> int f: IO[bytes],
remove: bool = False,
expected_pragma: bytes = DEFAULT_PRAGMA,
) -> int:
expected = _get_expected_contents( expected = _get_expected_contents(
f.readline(), f.readline(), f.read(), expected_pragma, f.readline(), f.readline(), f.read(), expected_pragma,
) )
@ -103,21 +101,20 @@ def fix_encoding_pragma(f, remove=False, expected_pragma=DEFAULT_PRAGMA):
return 1 return 1
def _normalize_pragma(pragma): # type: (Union[bytes, str]) -> bytes def _normalize_pragma(pragma: str) -> bytes:
if not isinstance(pragma, bytes): return pragma.encode().rstrip()
pragma = pragma.encode('UTF-8')
return pragma.rstrip()
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
'Fixes the encoding pragma of python files', 'Fixes the encoding pragma of python files',
) )
parser.add_argument('filenames', nargs='*', help='Filenames to fix') parser.add_argument('filenames', nargs='*', help='Filenames to fix')
parser.add_argument( parser.add_argument(
'--pragma', default=DEFAULT_PRAGMA, type=_normalize_pragma, '--pragma', default=DEFAULT_PRAGMA, type=_normalize_pragma,
help='The encoding pragma to use. Default: {}'.format( help=(
DEFAULT_PRAGMA.decode(), f'The encoding pragma to use. '
f'Default: {DEFAULT_PRAGMA.decode()}'
), ),
) )
parser.add_argument( parser.add_argument(
@ -141,9 +138,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
retv |= file_ret retv |= file_ret
if file_ret: if file_ret:
print( print(
fmt.format( fmt.format(pragma=args.pragma.decode(), filename=filename),
pragma=args.pragma.decode(), filename=filename,
),
) )
return retv return retv

View file

@ -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 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=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 # `argv` is ignored, pre-commit will send us a list of files that we
# don't care about # don't care about
added_diff = cmd_output( added_diff = cmd_output(
@ -19,7 +15,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
metadata, filename = line.split('\t', 1) metadata, filename = line.split('\t', 1)
new_mode = metadata.split(' ')[1] new_mode = metadata.split(' ')[1]
if new_mode == '160000': if new_mode == '160000':
print('{}: new submodule introduced'.format(filename)) print(f'{filename}: new submodule introduced')
retv = 1 retv = 1
if retv: if retv:

View file

@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import collections import collections
from typing import Dict from typing import Dict
@ -17,7 +13,7 @@ ALL_ENDINGS = (CR, CRLF, LF)
FIX_TO_LINE_ENDING = {'cr': CR, 'crlf': CRLF, 'lf': LF} 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( new_contents = b''.join(
line.rstrip(b'\r\n') + ending for line in contents.splitlines(True) line.rstrip(b'\r\n') + ending for line in contents.splitlines(True)
) )
@ -25,11 +21,11 @@ def _fix(filename, contents, ending): # type: (str, bytes, bytes) -> None
f.write(new_contents) 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: with open(filename, 'rb') as f:
contents = f.read() 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 line in contents.splitlines(True):
for ending in ALL_ENDINGS: for ending in ALL_ENDINGS:
@ -66,7 +62,7 @@ def fix_filename(filename, fix): # type: (str, str) -> int
return other_endings return other_endings
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'-f', '--fix', '-f', '--fix',
@ -81,9 +77,9 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
for filename in args.filenames: for filename in args.filenames:
if fix_filename(filename, args.fix): if fix_filename(filename, args.fix):
if args.fix == 'no': if args.fix == 'no':
print('{}: mixed line endings'.format(filename)) print(f'{filename}: mixed line endings')
else: else:
print('{}: fixed mixed line endings'.format(filename)) print(f'{filename}: fixed mixed line endings')
retv = 1 retv = 1
return retv return retv

View file

@ -1,5 +1,3 @@
from __future__ import print_function
import argparse import argparse
import re import re
from typing import AbstractSet from typing import AbstractSet
@ -10,8 +8,10 @@ from pre_commit_hooks.util import CalledProcessError
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output
def is_on_branch(protected, patterns=frozenset()): def is_on_branch(
# type: (AbstractSet[str], AbstractSet[str]) -> bool protected: AbstractSet[str],
patterns: AbstractSet[str] = frozenset(),
) -> bool:
try: try:
ref_name = cmd_output('git', 'symbolic-ref', 'HEAD') ref_name = cmd_output('git', 'symbolic-ref', 'HEAD')
except CalledProcessError: except CalledProcessError:
@ -23,7 +23,7 @@ def is_on_branch(protected, patterns=frozenset()):
) )
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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,10 +1,5 @@
from __future__ import print_function
import argparse import argparse
import io
import json import json
import sys
from collections import OrderedDict
from difflib import unified_diff from difflib import unified_diff
from typing import List from typing import List
from typing import Mapping from typing import Mapping
@ -13,38 +8,36 @@ from typing import Sequence
from typing import Tuple from typing import Tuple
from typing import Union from typing import Union
from six import text_type
def _get_pretty_format( def _get_pretty_format(
contents, indent, ensure_ascii=True, sort_keys=True, top_keys=(), contents: str,
): # type: (str, str, bool, bool, Sequence[str]) -> str indent: str,
def pairs_first(pairs): ensure_ascii: bool = True,
# type: (Sequence[Tuple[str, str]]) -> Mapping[str, str] 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 = [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]
if sort_keys: if sort_keys:
after = sorted(after, key=lambda x: x[0]) after.sort()
return OrderedDict(before + after) return dict(before + after)
json_pretty = json.dumps( json_pretty = json.dumps(
json.loads(contents, object_pairs_hook=pairs_first), json.loads(contents, object_pairs_hook=pairs_first),
indent=indent, indent=indent,
ensure_ascii=ensure_ascii, 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 f'{json_pretty}\n'
return text_type(json_pretty) + '\n'
def _autofix(filename, new_contents): # type: (str, str) -> None def _autofix(filename: str, new_contents: str) -> None:
print('Fixing file {}'.format(filename)) print(f'Fixing file {filename}')
with io.open(filename, 'w', encoding='UTF-8') as f: with open(filename, 'w', encoding='UTF-8') as f:
f.write(new_contents) 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.""" """Convert string numbers to int, leaving strings as is."""
try: try:
return int(s) return int(s)
@ -52,18 +45,18 @@ def parse_num_to_int(s): # type: (str) -> Union[int, str]
return s return s
def parse_topkeys(s): # type: (str) -> List[str] def parse_topkeys(s: str) -> List[str]:
return s.split(',') 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) source_lines = source.splitlines(True)
target_lines = target.splitlines(True) target_lines = target.splitlines(True)
diff = unified_diff(source_lines, target_lines, fromfile=file, tofile=file) diff = unified_diff(source_lines, target_lines, fromfile=file, tofile=file)
return ''.join(diff) return ''.join(diff)
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'--autofix', '--autofix',
@ -110,7 +103,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
status = 0 status = 0
for json_file in args.filenames: 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() contents = f.read()
try: try:
@ -131,8 +124,8 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
status = 1 status = 1
except ValueError: except ValueError:
print( print(
'Input File {} is not a valid JSON, consider using check-json' f'Input File {json_file} is not a valid JSON, consider using '
.format(json_file), f'check-json',
) )
return 1 return 1
@ -140,4 +133,4 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,5 +1,3 @@
from __future__ import print_function
import argparse import argparse
from typing import IO from typing import IO
from typing import List from typing import List
@ -11,15 +9,13 @@ PASS = 0
FAIL = 1 FAIL = 1
class Requirement(object): class Requirement:
def __init__(self) -> None:
def __init__(self): # type: () -> None self.value: Optional[bytes] = None
super(Requirement, self).__init__() self.comments: List[bytes] = []
self.value = None # type: Optional[bytes]
self.comments = [] # type: List[bytes]
@property @property
def name(self): # type: () -> bytes def name(self) -> bytes:
assert self.value is not None, self.value assert self.value is not None, self.value
for egg in (b'#egg=', b'&egg='): for egg in (b'#egg=', b'&egg='):
if egg in self.value: if egg in self.value:
@ -27,7 +23,7 @@ class Requirement(object):
return self.value.lower().partition(b'==')[0] 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, # \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
@ -39,10 +35,10 @@ class Requirement(object):
return self.name < requirement.name return self.name < requirement.name
def fix_requirements(f): # type: (IO[bytes]) -> int def fix_requirements(f: IO[bytes]) -> int:
requirements = [] # type: List[Requirement] requirements: List[Requirement] = []
before = list(f) before = list(f)
after = [] # type: List[bytes] after: List[bytes] = []
before_string = b''.join(before) before_string = b''.join(before)
@ -109,7 +105,7 @@ def fix_requirements(f): # type: (IO[bytes]) -> int
return FAIL return FAIL
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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)
@ -121,7 +117,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
ret_for_file = fix_requirements(file_obj) ret_for_file = fix_requirements(file_obj)
if ret_for_file: if ret_for_file:
print('Sorting {}'.format(arg)) print(f'Sorting {arg}')
retv |= ret_for_file retv |= ret_for_file

View file

@ -18,8 +18,6 @@ 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 print_function
import argparse import argparse
from typing import List from typing import List
from typing import Optional from typing import Optional
@ -29,7 +27,7 @@ from typing import Sequence
QUOTES = ["'", '"'] 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. """Sort a YAML file in alphabetical order, keeping blocks together.
:param lines: array of strings (without newlines) :param lines: array of strings (without newlines)
@ -47,7 +45,7 @@ def sort(lines): # type: (List[str]) -> List[str]
return new_lines 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`. """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
@ -63,7 +61,7 @@ def parse_block(lines, header=False): # type: (List[str], bool) -> List[str]
return block_lines 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`. """Parse and return all possible blocks, popping off the start of `lines`.
:param lines: list of lines :param lines: list of lines
@ -80,7 +78,7 @@ def parse_blocks(lines): # type: (List[str]) -> List[List[str]]
return blocks 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. """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
@ -102,7 +100,7 @@ def first_key(lines): # type: (List[str]) -> str
return '' # not actually reached in reality 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 = 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)
@ -115,7 +113,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
new_lines = sort(lines) new_lines = sort(lines)
if lines != new_lines: if lines != new_lines:
print('Fixing file `{filename}`'.format(filename=filename)) print(f'Fixing file `{filename}`')
f.seek(0) f.seek(0)
f.write('\n'.join(new_lines) + '\n') f.write('\n'.join(new_lines) + '\n')
f.truncate() f.truncate()

View file

@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import io import io
import re import re
@ -13,7 +9,7 @@ from typing import Sequence
START_QUOTE_RE = re.compile('^[a-zA-Z]*"') 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: if '"""' in token_text or "'''" in token_text:
return token_text return token_text
@ -28,7 +24,7 @@ def handle_match(token_text): # type: (str) -> str
return token_text 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 # 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):
@ -36,8 +32,8 @@ def get_line_offsets_by_line_no(src): # type: (str) -> List[int]
return offsets return offsets
def fix_strings(filename): # type: (str) -> int def fix_strings(filename: str) -> int:
with io.open(filename, encoding='UTF-8', newline='') as f: with open(filename, encoding='UTF-8', newline='') as f:
contents = f.read() contents = f.read()
line_offsets = get_line_offsets_by_line_no(contents) line_offsets = get_line_offsets_by_line_no(contents)
@ -57,14 +53,14 @@ def fix_strings(filename): # type: (str) -> int
new_contents = ''.join(splitcontents) new_contents = ''.join(splitcontents)
if contents != new_contents: 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) f.write(new_contents)
return 1 return 1
else: else:
return 0 return 0
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv: Optional[Sequence[str]] = 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)
@ -74,7 +70,7 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
for filename in args.filenames: for filename in args.filenames:
return_value = fix_strings(filename) return_value = fix_strings(filename)
if return_value != 0: if return_value != 0:
print('Fixing strings in {}'.format(filename)) print(f'Fixing strings in {filename}')
retv |= return_value retv |= return_value
return retv return retv

View file

@ -1,14 +1,11 @@
from __future__ import print_function
import argparse import argparse
import os.path import os.path
import re import re
import sys
from typing import Optional from typing import Optional
from typing import Sequence 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 = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*') parser.add_argument('filenames', nargs='*')
parser.add_argument( parser.add_argument(
@ -27,14 +24,10 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
not base == 'conftest.py' not base == 'conftest.py'
): ):
retcode = 1 retcode = 1
print( print(f'{filename} does not match pattern "{test_name_pattern}"')
'{} does not match pattern "{}"'.format(
filename, test_name_pattern,
),
)
return retcode return retcode
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,14 +1,14 @@
from __future__ import print_function
import argparse import argparse
import os import os
import sys
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
def _fix_file(filename, is_markdown, chars): def _fix_file(
# type: (str, bool, Optional[bytes]) -> bool filename: str,
is_markdown: bool,
chars: Optional[bytes],
) -> 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()
newlines = [_process_line(line, is_markdown, chars) for line in lines] newlines = [_process_line(line, is_markdown, chars) for line in lines]
@ -21,8 +21,11 @@ def _fix_file(filename, is_markdown, chars):
return False return False
def _process_line(line, is_markdown, chars): def _process_line(
# type: (bytes, bool, Optional[bytes]) -> bytes line: bytes,
is_markdown: bool,
chars: Optional[bytes],
) -> bytes:
if line[-2:] == b'\r\n': if line[-2:] == b'\r\n':
eol = b'\r\n' eol = b'\r\n'
line = line[:-2] line = line[:-2]
@ -37,7 +40,7 @@ def _process_line(line, is_markdown, chars):
return line.rstrip(chars) + eol 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 = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'--no-markdown-linebreak-ext', '--no-markdown-linebreak-ext',
@ -80,20 +83,20 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
for ext in md_exts: for ext in md_exts:
if any(c in ext[1:] for c in r'./\:'): if any(c in ext[1:] for c in r'./\:'):
parser.error( parser.error(
'bad --markdown-linebreak-ext extension {!r} (has . / \\ :)\n' f'bad --markdown-linebreak-ext extension '
" (probably filename; use '--markdown-linebreak-ext=EXT')" f'{ext!r} (has . / \\ :)\n'
.format(ext), 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 return_code = 0
for filename in args.filenames: for filename in args.filenames:
_, extension = os.path.splitext(filename.lower()) _, extension = os.path.splitext(filename.lower())
md = all_markdown or extension in md_exts md = all_markdown or extension in md_exts
if _fix_file(filename, md, chars): if _fix_file(filename, md, chars):
print('Fixing {}'.format(filename)) print(f'Fixing {filename}')
return_code = 1 return_code = 1
return return_code return return_code
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) exit(main())

View file

@ -1,9 +1,6 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import subprocess import subprocess
from typing import Any from typing import Any
from typing import Optional
from typing import Set from typing import Set
@ -11,18 +8,17 @@ class CalledProcessError(RuntimeError):
pass pass
def added_files(): # type: () -> 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, **kwargs): # type: (*str, **Any) -> str def cmd_output(*cmd: str, retcode: Optional[int] = 0, **kwargs: Any) -> str:
retcode = kwargs.pop('retcode', 0)
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)
stdout, stderr = proc.communicate() stdout, stderr = proc.communicate()
stdout = stdout.decode('UTF-8') stdout = stdout.decode()
if retcode is not None and proc.returncode != retcode: if retcode is not None and proc.returncode != retcode:
raise CalledProcessError(cmd, retcode, proc.returncode, stdout, stderr) raise CalledProcessError(cmd, retcode, proc.returncode, stdout, stderr)
return stdout return stdout

View file

@ -11,13 +11,11 @@ license = MIT
license_file = LICENSE license_file = LICENSE
classifiers = classifiers =
License :: OSI Approved :: MIT License License :: OSI Approved :: MIT License
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3.4 Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy Programming Language :: Python :: Implementation :: PyPy
@ -27,9 +25,7 @@ install_requires =
flake8 flake8
ruamel.yaml>=0.15 ruamel.yaml>=0.15
toml toml
six python_requires = >=3.6
typing; python_version<"3.5"
python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import os.path import os.path

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import pytest import pytest
from pre_commit_hooks.autopep8_wrapper import main from pre_commit_hooks.autopep8_wrapper import main

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import distutils.spawn import distutils.spawn
import pytest import pytest
@ -78,7 +75,7 @@ xfailif_no_gitlfs = pytest.mark.xfail(
@xfailif_no_gitlfs @xfailif_no_gitlfs
def test_allows_gitlfs(temp_git_dir, monkeypatch): # pragma: no cover def test_allows_gitlfs(temp_git_dir, monkeypatch): # pragma: no cover
with temp_git_dir.as_cwd(): 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', 'install')
temp_git_dir.join('f.py').write('a' * 10000) temp_git_dir.join('f.py').write('a' * 10000)
cmd_output('git', 'lfs', 'track', 'f.py') cmd_output('git', 'lfs', 'track', 'f.py')
@ -90,7 +87,7 @@ def test_allows_gitlfs(temp_git_dir, monkeypatch): # pragma: no cover
@xfailif_no_gitlfs @xfailif_no_gitlfs
def test_moves_with_gitlfs(temp_git_dir, monkeypatch): # pragma: no cover def test_moves_with_gitlfs(temp_git_dir, monkeypatch): # pragma: no cover
with temp_git_dir.as_cwd(): 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', 'install')
cmd_output('git', 'lfs', 'track', 'a.bin', 'b.bin') cmd_output('git', 'lfs', 'track', 'a.bin', 'b.bin')
# First add the file we're going to move # First add the file we're going to move

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
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

@ -7,7 +7,7 @@ from pre_commit_hooks.check_builtin_literals import main
from pre_commit_hooks.check_builtin_literals import Visitor from pre_commit_hooks.check_builtin_literals import Visitor
BUILTIN_CONSTRUCTORS = '''\ BUILTIN_CONSTRUCTORS = '''\
from six.moves import builtins import builtins
c1 = complex() c1 = complex()
d1 = dict() d1 = dict()

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from pre_commit_hooks import check_byte_order_marker from pre_commit_hooks import check_byte_order_marker

View file

@ -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 find_conflicting_filenames
from pre_commit_hooks.check_case_conflict import main from pre_commit_hooks.check_case_conflict import main
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output

View file

@ -1,7 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
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,7 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
import pytest import pytest
from pre_commit_hooks.check_executables_have_shebangs import main from pre_commit_hooks.check_executables_have_shebangs import main
@ -12,7 +8,7 @@ from pre_commit_hooks.check_executables_have_shebangs import main
b'#!/bin/bash\nhello world\n', b'#!/bin/bash\nhello world\n',
b'#!/usr/bin/env python3.6', b'#!/usr/bin/env python3.6',
b'#!python', b'#!python',
'#!☃'.encode('UTF-8'), '#!☃'.encode(),
), ),
) )
def test_has_shebang(content, tmpdir): def test_has_shebang(content, tmpdir):
@ -27,7 +23,7 @@ def test_has_shebang(content, tmpdir):
b' #!python\n', b' #!python\n',
b'\n#!python\n', b'\n#!python\n',
b'python\n', b'python\n',
''.encode('UTF-8'), ''.encode(),
), ),
) )
@ -36,4 +32,4 @@ def test_bad_shebang(content, tmpdir, capsys):
path.write(content, 'wb') path.write(content, 'wb')
assert main((path.strpath,)) == 1 assert main((path.strpath,)) == 1
_, stderr = capsys.readouterr() _, stderr = capsys.readouterr()
assert stderr.startswith('{}: marked executable but'.format(path.strpath)) assert stderr.startswith(f'{path}: marked executable but')

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import os import os
import shutil import shutil

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from pre_commit_hooks.check_toml import main from pre_commit_hooks.check_toml import main

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from pre_commit_hooks.check_vcs_permalinks import main from pre_commit_hooks.check_vcs_permalinks import main

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import pytest import pytest
from pre_commit_hooks.check_yaml import main from pre_commit_hooks.check_yaml import main

View file

@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import pytest import pytest
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output

View file

@ -1,7 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
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,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import io import io
import pytest import pytest
@ -129,9 +126,6 @@ def test_not_ok_input_alternate_pragma():
@pytest.mark.parametrize( @pytest.mark.parametrize(
('input_s', 'expected'), ('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'), ('# coding: utf-8', b'# coding: utf-8'),
# trailing whitespace # trailing whitespace
('# coding: utf-8\n', b'# coding: utf-8'), ('# coding: utf-8\n', b'# coding: utf-8'),
@ -149,7 +143,7 @@ def test_integration_alternate_pragma(tmpdir, capsys):
assert main((f.strpath, '--pragma', pragma)) == 1 assert main((f.strpath, '--pragma', pragma)) == 1
assert f.read() == '# coding: utf-8\nx = 1\n' assert f.read() == '# coding: utf-8\nx = 1\n'
out, _ = capsys.readouterr() 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): def test_crlf_ok(tmpdir):

View file

@ -1,5 +1,3 @@
from __future__ import absolute_import
import subprocess import subprocess
import pytest import pytest

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import pytest import pytest
from pre_commit_hooks.mixed_line_ending import main from pre_commit_hooks.mixed_line_ending import main
@ -86,7 +83,7 @@ def test_no_fix_does_not_modify(tmpdir, capsys):
assert ret == 1 assert ret == 1
assert path.read_binary() == contents assert path.read_binary() == contents
out, _ = capsys.readouterr() 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): def test_fix_lf(tmpdir, capsys):
@ -97,7 +94,7 @@ def test_fix_lf(tmpdir, capsys):
assert ret == 1 assert ret == 1
assert path.read_binary() == b'foo\nbar\nbaz\n' assert path.read_binary() == b'foo\nbar\nbaz\n'
out, _ = capsys.readouterr() 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): def test_fix_crlf(tmpdir):

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
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

@ -2,7 +2,6 @@ import os
import shutil import shutil
import pytest import pytest
from six import PY2
from pre_commit_hooks.pretty_format_json import main from pre_commit_hooks.pretty_format_json import main
from pre_commit_hooks.pretty_format_json import parse_num_to_int from pre_commit_hooks.pretty_format_json import parse_num_to_int
@ -42,7 +41,6 @@ def test_unsorted_main(filename, expected_retval):
assert ret == expected_retval assert ret == expected_retval
@pytest.mark.skipif(PY2, reason='Requires Python3')
@pytest.mark.parametrize( @pytest.mark.parametrize(
('filename', 'expected_retval'), ( ('filename', 'expected_retval'), (
('not_pretty_formatted_json.json', 1), ('not_pretty_formatted_json.json', 1),
@ -52,7 +50,7 @@ def test_unsorted_main(filename, expected_retval):
('tab_pretty_formatted_json.json', 0), ('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)]) ret = main(['--indent', '\t', get_resource_path(filename)])
assert ret == expected_retval assert ret == expected_retval
@ -113,9 +111,9 @@ def test_diffing_output(capsys):
expected_retval = 1 expected_retval = 1
a = os.path.join('a', resource_path) a = os.path.join('a', resource_path)
b = os.path.join('b', resource_path) b = os.path.join('b', resource_path)
expected_out = '''\ expected_out = f'''\
--- {} --- {a}
+++ {} +++ {b}
@@ -1,6 +1,9 @@ @@ -1,6 +1,9 @@
{{ {{
- "foo": - "foo":
@ -130,7 +128,7 @@ def test_diffing_output(capsys):
+ "blah": null, + "blah": null,
+ "foo": "bar" + "foo": "bar"
}} }}
'''.format(a, b) '''
actual_retval = main([resource_path]) actual_retval = main([resource_path])
actual_out, actual_err = capsys.readouterr() actual_out, actual_err = capsys.readouterr()

View file

@ -1,15 +1,10 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import io
from pre_commit_hooks.check_yaml import yaml from pre_commit_hooks.check_yaml import yaml
def test_readme_contains_all_hooks(): 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() 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) hooks = yaml.load(f)
for hook in hooks: for hook in hooks:
assert '`{}`'.format(hook['id']) in readme_contents assert f'`{hook["id"]}`' in readme_contents

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import os import os
import pytest import pytest

View file

@ -1,7 +1,3 @@
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
import textwrap import textwrap
import pytest import pytest

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import pytest import pytest
from pre_commit_hooks.trailing_whitespace_fixer import main from pre_commit_hooks.trailing_whitespace_fixer import main
@ -46,7 +43,7 @@ def test_fixes_markdown_files(tmpdir, ext):
'\t\n' # trailing tabs are stripped anyway '\t\n' # trailing tabs are stripped anyway
'\n ', # whitespace at the end of the file is removed '\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 ret == 1
assert path.read() == ( assert path.read() == (
'foo \n' 'foo \n'

View file

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
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 = py27,py36,py37,pypy,pypy3,pre-commit envlist = py36,py37,py38,pypy3,pre-commit
[testenv] [testenv]
deps = -rrequirements-dev.txt deps = -rrequirements-dev.txt