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

Skip to content

Commit 1f1cd2b

Browse files
authored
Merge pull request #839 from georgeyk/refactor-xargs-partition
Update xargs.partition with platform information
2 parents eecf347 + ead906a commit 1f1cd2b

2 files changed

Lines changed: 87 additions & 8 deletions

File tree

β€Žpre_commit/xargs.pyβ€Ž

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,63 @@
11
from __future__ import absolute_import
22
from __future__ import unicode_literals
33

4+
import sys
5+
6+
import six
7+
48
from pre_commit import parse_shebang
59
from pre_commit.util import cmd_output
610

711

8-
# Limit used previously to avoid "xargs ... Bad file number" on windows
9-
# This is slightly less than the posix mandated minimum
10-
MAX_LENGTH = 4000
12+
# TODO: properly compute max_length value
13+
def _get_platform_max_length():
14+
# posix minimum
15+
return 4 * 1024
16+
17+
18+
def _command_length(*cmd):
19+
full_cmd = ' '.join(cmd)
20+
21+
# win32 uses the amount of characters, more details at:
22+
# https://github.com/pre-commit/pre-commit/pull/839
23+
if sys.platform == 'win32':
24+
# the python2.x apis require bytes, we encode as UTF-8
25+
if six.PY2:
26+
return len(full_cmd.encode('utf-8'))
27+
else:
28+
return len(full_cmd.encode('utf-16le')) // 2
29+
else:
30+
return len(full_cmd.encode(sys.getfilesystemencoding()))
1131

1232

1333
class ArgumentTooLongError(RuntimeError):
1434
pass
1535

1636

17-
def partition(cmd, varargs, _max_length=MAX_LENGTH):
37+
def partition(cmd, varargs, _max_length=None):
38+
_max_length = _max_length or _get_platform_max_length()
1839
cmd = tuple(cmd)
1940
ret = []
2041

2142
ret_cmd = []
22-
total_len = len(' '.join(cmd))
2343
# Reversed so arguments are in order
2444
varargs = list(reversed(varargs))
2545

46+
total_length = _command_length(*cmd)
2647
while varargs:
2748
arg = varargs.pop()
2849

29-
if total_len + 1 + len(arg) <= _max_length:
50+
arg_length = _command_length(arg) + 1
51+
if total_length + arg_length <= _max_length:
3052
ret_cmd.append(arg)
31-
total_len += len(arg)
53+
total_length += arg_length
3254
elif not ret_cmd:
3355
raise ArgumentTooLongError(arg)
3456
else:
3557
# We've exceeded the length, yield a command
3658
ret.append(cmd + tuple(ret_cmd))
3759
ret_cmd = []
38-
total_len = len(' '.join(cmd))
60+
total_length = _command_length(*cmd)
3961
varargs.append(arg)
4062

4163
ret.append(cmd + tuple(ret_cmd))

β€Žtests/xargs_test.pyβ€Ž

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,39 @@
1+
# -*- coding: utf-8 -*-
12
from __future__ import absolute_import
23
from __future__ import unicode_literals
34

5+
import sys
6+
7+
import mock
48
import pytest
9+
import six
510

611
from pre_commit import xargs
712

813

14+
@pytest.fixture
15+
def win32_py2_mock():
16+
with mock.patch.object(sys, 'getfilesystemencoding', return_value='utf-8'):
17+
with mock.patch.object(sys, 'platform', 'win32'):
18+
with mock.patch.object(six, 'PY2', True):
19+
yield
20+
21+
22+
@pytest.fixture
23+
def win32_py3_mock():
24+
with mock.patch.object(sys, 'getfilesystemencoding', return_value='utf-8'):
25+
with mock.patch.object(sys, 'platform', 'win32'):
26+
with mock.patch.object(six, 'PY2', False):
27+
yield
28+
29+
30+
@pytest.fixture
31+
def linux_mock():
32+
with mock.patch.object(sys, 'getfilesystemencoding', return_value='utf-8'):
33+
with mock.patch.object(sys, 'platform', 'linux'):
34+
yield
35+
36+
937
def test_partition_trivial():
1038
assert xargs.partition(('cmd',), ()) == (('cmd',),)
1139

@@ -35,6 +63,35 @@ def test_partition_limits():
3563
)
3664

3765

66+
def test_partition_limit_win32_py3(win32_py3_mock):
67+
cmd = ('ninechars',)
68+
# counted as half because of utf-16 encode
69+
varargs = ('πŸ˜‘' * 5,)
70+
ret = xargs.partition(cmd, varargs, _max_length=20)
71+
assert ret == (cmd + varargs,)
72+
73+
74+
def test_partition_limit_win32_py2(win32_py2_mock):
75+
cmd = ('ninechars',)
76+
varargs = ('πŸ˜‘' * 5,) # 4 bytes * 5
77+
ret = xargs.partition(cmd, varargs, _max_length=30)
78+
assert ret == (cmd + varargs,)
79+
80+
81+
def test_partition_limit_linux(linux_mock):
82+
cmd = ('ninechars',)
83+
varargs = ('πŸ˜‘' * 5,)
84+
ret = xargs.partition(cmd, varargs, _max_length=30)
85+
assert ret == (cmd + varargs,)
86+
87+
88+
def test_argument_too_long_with_large_unicode(linux_mock):
89+
cmd = ('ninechars',)
90+
varargs = ('πŸ˜‘' * 10,) # 4 bytes * 10
91+
with pytest.raises(xargs.ArgumentTooLongError):
92+
xargs.partition(cmd, varargs, _max_length=20)
93+
94+
3895
def test_argument_too_long():
3996
with pytest.raises(xargs.ArgumentTooLongError):
4097
xargs.partition(('a' * 5,), ('a' * 5,), _max_length=10)

0 commit comments

Comments
Β (0)