Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 47c6882

Browse files
committed
Merge pull request #74 from pre-commit/py3
Python 3 compatibility.
2 parents ddebb83 + 064c54d commit 47c6882

11 files changed

Lines changed: 110 additions & 28 deletions

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ language: python
22
env: # These should match the tox env list
33
- TOXENV=py26
44
- TOXENV=py27
5+
- TOXENV=py33
6+
- TOXENV=pypy
57
install: pip install tox --use-mirrors
68
script: tox
79

pre_commit/five.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""five: six, redux"""
2+
# pylint:disable=invalid-name
3+
PY2 = (str is bytes)
4+
PY3 = (str is not bytes)
5+
6+
# provide a symettrical `text` type to `bytes`
7+
if PY2:
8+
text = unicode # flake8: noqa
9+
else:
10+
text = str
11+
12+
13+
def n(obj):
14+
"""Produce a native string.
15+
16+
Similar in behavior to str(), but uses US-ASCII encoding when necessary.
17+
"""
18+
if isinstance(obj, str):
19+
return obj
20+
elif PY2 and isinstance(obj, text):
21+
return obj.encode('US-ASCII')
22+
elif PY3 and isinstance(obj, bytes):
23+
return obj.decode('US-ASCII')
24+
else:
25+
return str(obj)
26+
27+
28+
def u(obj):
29+
"""Produces text.
30+
31+
Similar in behavior to str() in python3 or unicode() in python2,
32+
but uses US-ASCII encoding when necessary.
33+
"""
34+
if isinstance(obj, text):
35+
return obj
36+
elif isinstance(obj, bytes):
37+
return obj.decode('US-ASCII')
38+
else:
39+
return text(obj)
40+
41+
42+
def b(obj):
43+
"""Produces bytes.
44+
45+
Similar in behavior to bytes(), but uses US-ASCII encoding when necessary.
46+
"""
47+
if isinstance(obj, bytes):
48+
return obj
49+
elif isinstance(obj, text):
50+
return obj.encode('US-ASCII')
51+
else:
52+
return bytes(obj)
53+
54+
55+
def udict(*args, **kwargs):
56+
"""Similar to dict(), but keyword-keys are text."""
57+
kwargs = dict([
58+
(u(key), val)
59+
for key, val in kwargs.items()
60+
])
61+
62+
return dict(*args, **kwargs)
63+
64+
def ndict(*args, **kwargs):
65+
"""Similar to dict(), but keyword-keys are forced to native strings."""
66+
# I hate this :(
67+
kwargs = dict([
68+
(n(key), val)
69+
for key, val in kwargs.items()
70+
])
71+
72+
return dict(*args, **kwargs)
73+
74+
def open(*args, **kwargs):
75+
"""Override the builtin open() to return text and use utf8 by default."""
76+
from io import open
77+
kwargs.setdefault('encoding', 'UTF-8')
78+
return open(*args, **kwargs)

pre_commit/jsonschema_extensions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ def new_properties(validator, properties, instance, schema):
2121

2222

2323
def default_values(properties, instance):
24-
for property, subschema in properties.iteritems():
24+
for property, subschema in properties.items():
2525
if 'default' in subschema:
2626
instance.setdefault(
2727
property, copy.deepcopy(subschema['default']),
2828
)
2929

3030

3131
def remove_default_values(properties, instance):
32-
for property, subschema in properties.iteritems():
32+
for property, subschema in properties.items():
3333
if (
3434
'default' in subschema and
3535
instance.get(property) == subschema['default']

pre_commit/logging_handler.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414

1515

1616
class LoggingHandler(logging.Handler):
17-
def __init__(self, use_color):
17+
def __init__(self, use_color, print_fn=print):
1818
logging.Handler.__init__(self)
1919
self.use_color = use_color
20+
self.__print_fn = print_fn
2021

2122
def emit(self, record):
22-
print(
23+
self.__print_fn(
2324
u'{0}{1}'.format(
2425
color.format_color(
2526
'[{0}]'.format(record.levelname),

pre_commit/prefixed_command_runner.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ def run(self, cmd, retcode=0, stdin=None, **kwargs):
5555
'stdout': subprocess.PIPE,
5656
'stderr': subprocess.PIPE,
5757
}
58+
if stdin is not None:
59+
stdin = stdin.encode('utf-8')
60+
5861
popen_kwargs.update(kwargs)
5962
self._create_path_if_not_exists()
6063
replaced_cmd = _replace_cmd(cmd, prefix=self.prefix_dir)

pre_commit/repository.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from plumbum import local
55

66
import pre_commit.constants as C
7+
from pre_commit import five
78
from pre_commit.clientlib.validate_manifest import load_manifest
89
from pre_commit.hooks_workspace import in_hooks_workspace
910
from pre_commit.languages.all import languages
@@ -70,7 +71,7 @@ def create(self):
7071
logger.info('Installing environment for {0}.'.format(self.repo_url))
7172
logger.info('Once installed this environment will be reused.')
7273
logger.info('This may take a few minutes...')
73-
with clean_path_on_failure(unicode(local.path(self.sha))):
74+
with clean_path_on_failure(five.u(local.path(self.sha))):
7475
local['git']['clone', '--no-checkout', self.repo_url, self.sha]()
7576
with self.in_checkout():
7677
local['git']['checkout', self.sha]()

pre_commit/run.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ def _run_single_hook(runner, repository, hook_id, args):
4545
get_filenames(hook['files'], hook['exclude']),
4646
)
4747

48-
output = '\n'.join([stdout, stderr]).strip()
4948
if retcode != repository.hooks[hook_id]['expected_return_value']:
5049
retcode = 1
5150
print_color = color.RED
@@ -57,8 +56,12 @@ def _run_single_hook(runner, repository, hook_id, args):
5756

5857
print(color.format_color(pass_fail, print_color, args.color))
5958

60-
if output and (retcode or args.verbose):
61-
print('\n' + output)
59+
if (stdout or stderr) and (retcode or args.verbose):
60+
print()
61+
for output in (stdout, stderr):
62+
if output.strip():
63+
print(output.strip())
64+
print()
6265

6366
return retcode
6467

tests/logging_handler_test.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
1-
import __builtin__
21
import mock
3-
import pytest
42

53
from pre_commit import color
64
from pre_commit.logging_handler import LoggingHandler
75

86

9-
@pytest.yield_fixture
10-
def print_mock():
11-
with mock.patch.object(__builtin__, 'print', autospec=True) as print_mock:
12-
yield print_mock
13-
14-
157
class FakeLogRecord(object):
168
def __init__(self, message, levelname, levelno):
179
self.message = message
@@ -22,16 +14,18 @@ def getMessage(self):
2214
return self.message
2315

2416

25-
def test_logging_handler_color(print_mock):
26-
handler = LoggingHandler(True)
17+
def test_logging_handler_color():
18+
print_mock = mock.Mock()
19+
handler = LoggingHandler(True, print_mock)
2720
handler.emit(FakeLogRecord('hi', 'WARNING', 30))
2821
print_mock.assert_called_once_with(
2922
color.YELLOW + '[WARNING]' + color.NORMAL + ' hi',
3023
)
3124

3225

33-
def test_logging_handler_no_color(print_mock):
34-
handler = LoggingHandler(False)
26+
def test_logging_handler_no_color():
27+
print_mock = mock.Mock()
28+
handler = LoggingHandler(False, print_mock)
3529
handler.emit(FakeLogRecord('hi', 'WARNING', 30))
3630
print_mock.assert_called_once_with(
3731
'[WARNING] hi',

tests/repository_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def test_run_a_python_hook(config_for_python_hooks_repo):
5555
)
5656

5757
assert ret[0] == 0
58-
assert ret[1] == "['/dev/null']\nHello World\n"
58+
assert ret[1] == b"['/dev/null']\nHello World\n"
5959

6060

6161
@pytest.mark.integration
@@ -77,7 +77,7 @@ def test_cwd_of_hook(config_for_prints_cwd_repo):
7777
)
7878

7979
assert ret[0] == 0
80-
assert ret[1] == '{0}\n'.format(repo.repo_url)
80+
assert ret[1] == repo.repo_url.encode('utf-8') + b'\n'
8181

8282

8383
@pytest.mark.skipif(
@@ -90,7 +90,7 @@ def test_run_a_node_hook(config_for_node_hooks_repo):
9090
ret = repo.run_hook(PrefixedCommandRunner(C.HOOKS_WORKSPACE), 'foo', [])
9191

9292
assert ret[0] == 0
93-
assert ret[1] == 'Hello World\n'
93+
assert ret[1] == b'Hello World\n'
9494

9595

9696
@pytest.mark.integration
@@ -101,7 +101,7 @@ def test_run_a_script_hook(config_for_script_hooks_repo):
101101
)
102102

103103
assert ret[0] == 0
104-
assert ret[1] == 'bar\nHello World\n'
104+
assert ret[1] == b'bar\nHello World\n'
105105

106106

107107
@pytest.fixture

tests/staged_files_only_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ def img_staged(empty_git_dir):
118118
def _test_img_state(path, expected_file='img1.jpg', status='A'):
119119
assert os.path.exists(path.img_filename)
120120
assert (
121-
open(path.img_filename).read() ==
122-
open(get_resource_path(expected_file)).read()
121+
open(path.img_filename, 'rb').read() ==
122+
open(get_resource_path(expected_file), 'rb').read()
123123
)
124124
actual_status = get_short_git_status()['img.jpg']
125125
assert status == actual_status

0 commit comments

Comments
 (0)