forked from jazzband/pip-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix unicode encoding errors inside pip
On Python 2, if a distribution package contains non-ASCII file names, the Resolver failed with the following UnicodeDecodeError, because RequirementSet was initialized with unicode paths rather than str. Fix this by making sure that the path components used in PyPIRepository are all filesystem encoded str. Drop the unicode_literals import from `_compat/tempfile.py`, because the strings used in there should also be str, not unicode. Also make sure that the InstallRequirement is always constructed from str, not unicode. Traceback of the error looked like this: Traceback (most recent call last): File ".../piptools/scripts/compile.py", line 184, in cli results = resolver.resolve(max_rounds=max_rounds) File ".../piptools/resolver.py", line 107, in resolve has_changed, best_matches = self._resolve_one_round() File ".../piptools/resolver.py", line 195, in _resolve_one_round for dep in self._iter_dependencies(best_match): File ".../piptools/resolver.py", line 274, in _iter_dependencies dependencies = self.repository.get_dependencies(ireq) File ".../piptools/repositories/pypi.py", line 145, in get_dependencies self._dependencies_cache[ireq] = reqset._prepare_file(self.finder, ireq) File ".../pip/req/req_set.py", line 620, in _prepare_file session=self.session, hashes=hashes) File ".../pip/download.py", line 821, in unpack_url hashes=hashes File ".../pip/download.py", line 663, in unpack_http_url unpack_file(from_path, location, content_type, link) File ".../pip/utils/__init__.py", line 605, in unpack_file untar_file(filename, location) File ".../pip/utils/__init__.py", line 551, in untar_file path = os.path.join(location, fn) File "/usr/local/lib/python2.7/posixpath.py", line 73, in join path += '/' + b UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 66: ordinal not in range(128) Fixes jazzband#564
- Loading branch information
1 parent
b3c6a11
commit 3cb15eb
Showing
4 changed files
with
98 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from mock import MagicMock, patch | ||
from pip.index import PackageFinder | ||
from pip.req import InstallRequirement | ||
|
||
from piptools.repositories.pypi import PyPIRepository | ||
from piptools.scripts.compile import get_pip_command | ||
|
||
|
||
def test_pypirepo_build_dir_is_str(): | ||
assert isinstance(get_pypi_repository().build_dir, str) | ||
|
||
|
||
def test_pypirepo_source_dir_is_str(): | ||
assert isinstance(get_pypi_repository().source_dir, str) | ||
|
||
|
||
def test_pypirepo_calls_reqset_with_str_paths(): | ||
""" | ||
Make sure that paths passed to RequirementSet init are str. | ||
Passing unicode paths on Python 2 could make pip fail later on | ||
unpack, if the package contains non-ASCII file names, because | ||
non-ASCII str and unicode paths cannot be combined. | ||
""" | ||
with patch('piptools.repositories.pypi.RequirementSet') as mocked_init: | ||
repo = get_pypi_repository() | ||
ireq = InstallRequirement.from_line('ansible==2.4.0.0') | ||
|
||
# Setup a mock object to be returned from the RequirementSet call | ||
mocked_reqset = MagicMock() | ||
mocked_init.return_value = mocked_reqset | ||
|
||
# Do the call | ||
repo.get_dependencies(ireq) | ||
|
||
# Check that RequirementSet init is called with correct type arguments | ||
assert mocked_init.call_count == 1 | ||
(init_call_args, init_call_kwargs) = mocked_init.call_args | ||
assert isinstance(init_call_args[0], str) | ||
assert isinstance(init_call_args[1], str) | ||
assert isinstance(init_call_kwargs.get('download_dir'), str) | ||
assert isinstance(init_call_kwargs.get('wheel_download_dir'), str) | ||
|
||
# Check that _prepare_file is called correctly | ||
assert mocked_reqset._prepare_file.call_count == 1 | ||
(pf_call_args, pf_call_kwargs) = mocked_reqset._prepare_file.call_args | ||
(called_with_finder, called_with_ireq) = pf_call_args | ||
assert isinstance(called_with_finder, PackageFinder) | ||
assert called_with_ireq == ireq | ||
assert not pf_call_kwargs | ||
|
||
|
||
def get_pypi_repository(): | ||
""" | ||
Get a PyPIRepository object for the tests. | ||
:rtype: PyPIRepository | ||
""" | ||
pip_command = get_pip_command() | ||
pip_options = pip_command.parse_args([])[0] | ||
session = pip_command._build_session(pip_options) | ||
return PyPIRepository(pip_options, session) |