pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/pre-commit/pre-commit/commit/2aaaddb5cc414ddca7018d803e1e6b6865bb3951

" href="https://github.githubassets.com/assets/global-d18f184ea1a06a2c.css" /> Fail gracefully on undecodable install output. · pre-commit/pre-commit@2aaaddb · GitHub
Skip to content

Commit 2aaaddb

Browse files
committed
Fail gracefully on undecodable install output.
1 parent 75aaadd commit 2aaaddb

9 files changed

Lines changed: 105 additions & 25 deletions

File tree

pre_commit/error_handler.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,19 @@ class PreCommitSystemExit(SystemExit):
1818
pass
1919

2020

21+
def _to_bytes(exc):
22+
try:
23+
return bytes(exc)
24+
except Exception:
25+
return five.text(exc).encode('UTF-8')
26+
27+
2128
def _log_and_exit(msg, exc, formatted, write_fn=sys_stdout_write_wrapper):
22-
error_msg = '{0}: {1}: {2}\n'.format(msg, type(exc).__name__, exc)
29+
error_msg = b''.join((
30+
five.to_bytes(msg), b': ',
31+
five.to_bytes(type(exc).__name__), b': ',
32+
_to_bytes(exc), b'\n',
33+
))
2334
write_fn(error_msg)
2435
write_fn('Check the log at ~/.pre-commit/pre-commit.log\n')
2536
store = Store()

pre_commit/languages/node.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@ def install_environment(
4545
repo_cmd_runner.run(cmd)
4646

4747
with in_env(repo_cmd_runner, version) as node_env:
48-
node_env.run("cd '{prefix}' && npm install -g")
48+
node_env.run("cd '{prefix}' && npm install -g", encoding=None)
4949
if additional_dependencies:
5050
node_env.run(
5151
"cd '{prefix}' && npm install -g " +
5252
' '.join(
5353
shell_escape(dep) for dep in additional_dependencies
54-
)
54+
),
55+
encoding=None,
5556
)
5657

5758

pre_commit/languages/python.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,14 @@ def install_environment(
6363
venv_cmd.extend(['-p', norm_version(version)])
6464
repo_cmd_runner.run(venv_cmd)
6565
with in_env(repo_cmd_runner, version) as env:
66-
env.run("cd '{prefix}' && pip install .")
66+
env.run("cd '{prefix}' && pip install .", encoding=None)
6767
if additional_dependencies:
6868
env.run(
6969
"cd '{prefix}' && pip install " +
7070
' '.join(
7171
shell_escape(dep) for dep in additional_dependencies
72-
)
72+
),
73+
encoding=None,
7374
)
7475

7576

pre_commit/languages/ruby.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,15 @@ def install_environment(
9595
ruby_env.run(
9696
'cd {prefix} && gem build *.gemspec && '
9797
'gem install --no-ri --no-rdoc *.gem',
98+
encoding=None,
9899
)
99100
if additional_dependencies:
100101
ruby_env.run(
101102
'cd {prefix} && gem install --no-ri --no-rdoc ' +
102103
' '.join(
103104
shell_escape(dep) for dep in additional_dependencies
104-
)
105+
),
106+
encoding=None,
105107
)
106108

107109

pre_commit/util.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -124,26 +124,38 @@ def __init__(self, returncode, cmd, expected_returncode, output=None):
124124
self.expected_returncode = expected_returncode
125125
self.output = output
126126

127-
def __str__(self):
127+
def to_bytes(self):
128128
output = []
129-
for text in self.output:
130-
if text:
131-
output.append('\n ' + text.replace('\n', '\n '))
129+
for maybe_text in self.output:
130+
if maybe_text:
131+
output.append(
132+
b'\n ' +
133+
five.to_bytes(maybe_text).replace(b'\n', b'\n ')
134+
)
132135
else:
133-
output.append('(none)')
134-
135-
return (
136-
'Command: {0!r}\n'
137-
'Return code: {1}\n'
138-
'Expected return code: {2}\n'
139-
'Output: {3}\n'
140-
'Errors: {4}\n'.format(
141-
self.cmd,
142-
self.returncode,
143-
self.expected_returncode,
144-
*output
145-
)
146-
)
136+
output.append(b'(none)')
137+
138+
return b''.join((
139+
five.to_bytes(
140+
'Command: {0!r}\n'
141+
'Return code: {1}\n'
142+
'Expected return code: {2}\n'.format(
143+
self.cmd, self.returncode, self.expected_returncode
144+
)
145+
),
146+
b'Output: ', output[0], b'\n',
147+
b'Errors: ', output[1], b'\n',
148+
))
149+
150+
def to_text(self):
151+
return self.to_bytes().decode('UTF-8')
152+
153+
if five.PY3: # pragma: no cover
154+
__bytes__ = to_bytes
155+
__str__ = to_text
156+
else:
157+
__str__ = to_bytes
158+
__unicode__ = to_text
147159

148160

149161
def cmd_output(*cmd, **kwargs):
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- id: foo
2+
name: Foo
3+
entry: foo
4+
language: python
5+
language_version: python2.7
6+
files: \.py$
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import print_function
3+
from __future__ import unicode_literals
4+
5+
import sys
6+
7+
8+
def main():
9+
# Intentionally write mixed encoding to the output. This should not crash
10+
# pre-commit and should write bytes to the output.
11+
sys.stderr.write('☃'.encode('UTF-8') + '²'.encode('latin1') + b'\n')
12+
# Return 1 to indicate failures
13+
return 1
14+
15+
16+
if __name__ == '__main__':
17+
exit(main())

tests/commands/run_test.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,33 @@ def test_stdout_write_bug_py26(
371371
assert 'UnicodeDecodeError' not in stdout
372372

373373

374+
def test_hook_install_failure(mock_out_store_directory, tempdir_factory):
375+
git_path = make_consuming_repo(tempdir_factory, 'not_installable_repo')
376+
with cwd(git_path):
377+
install(Runner(git_path))
378+
379+
# Don't want to write to home directory
380+
env = dict(os.environ, PRE_COMMIT_HOME=tempdir_factory.get())
381+
_, stdout, _ = cmd_output(
382+
'git', 'commit', '-m', 'Commit!',
383+
# git commit puts pre-commit to stderr
384+
stderr=subprocess.STDOUT,
385+
env=env,
386+
retcode=None,
387+
encoding=None,
388+
)
389+
assert b'UnicodeDecodeError' not in stdout
390+
# Doesn't actually happen, but a reasonable assertion
391+
assert b'UnicodeEncodeError' not in stdout
392+
393+
# Sanity check our output
394+
assert (
395+
b'An unexpected error has occurred: CalledProcessError: ' in
396+
stdout
397+
)
398+
assert '☃'.encode('UTF-8') + '²'.encode('latin1') in stdout
399+
400+
374401
def test_get_changed_files():
375402
files = get_changed_files(
376403
'78c682a1d13ba20e7cb735313b9314a74365cd3a',

tests/error_handler_test.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import pytest
1212

1313
from pre_commit import error_handler
14+
from pre_commit import five
1415
from pre_commit.errors import FatalError
1516
from pre_commit.util import cmd_output
1617

@@ -82,7 +83,9 @@ def test_log_and_exit(mock_out_store_directory):
8283
write_fn=mocked_write,
8384
)
8485

85-
printed = ''.join(call[0][0] for call in mocked_write.call_args_list)
86+
printed = ''.join(
87+
five.to_text(call[0][0]) for call in mocked_write.call_args_list
88+
)
8689
assert printed == (
8790
'msg: FatalError: hai\n'
8891
'Check the log at ~/.pre-commit/pre-commit.log\n'

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy