From b3f645433a3b50c776d0a12135a1a2790fa7436f Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Fri, 4 May 2018 16:46:44 -0400 Subject: [PATCH 1/5] Fix grouping of extras requirements in notpip --- pipenv/patched/notpip/index.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/pipenv/patched/notpip/index.py b/pipenv/patched/notpip/index.py index bf1cba9c..07881b08 100644 --- a/pipenv/patched/notpip/index.py +++ b/pipenv/patched/notpip/index.py @@ -210,20 +210,16 @@ class PackageFinder(object): requires = [] extras = {} - current_section = None + current_list = requires for link in links: if not link: - current_section = None - - if not current_section: - if not (link.startswith('[')): - requires.append(link) - else: - current_section = link[1:-1] - extras[current_section] = [] + current_list = requires + if link.startswith('['): + current_list = [] + extras[link[1:-1]] = current_list else: - extras[current_section].append(link) + current_list.append(link) return extras From b53a365a277bcc793a240134144574802a01e8a1 Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Fri, 4 May 2018 22:43:11 -0400 Subject: [PATCH 2/5] Modify existing patch to include this modification --- tasks/vendoring/patches/patched/pip.patch | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tasks/vendoring/patches/patched/pip.patch b/tasks/vendoring/patches/patched/pip.patch index b8555c5b..9e4b714d 100644 --- a/tasks/vendoring/patches/patched/pip.patch +++ b/tasks/vendoring/patches/patched/pip.patch @@ -1366,25 +1366,25 @@ index f653f6e..48aaa35 100644 + requires = [] + extras = {} + -+ current_section = None ++ current_list = requires + + for link in links: + if not link: -+ current_section = None -+ -+ if not current_section: -+ if not (link.startswith('[')): -+ requires.append(link) -+ else: -+ current_section = link[1:-1] -+ extras[current_section] = [] ++ current_list = requires ++ if link.startswith('['): ++ current_list = [] ++ extras[link[1:-1]] = current_list + else: -+ extras[current_section].append(link) ++ current_list.append(link) + + return extras + + + ++ ++ ++ ++ + @staticmethod def _sort_locations(locations, expand_dir=False): From 1eb56b458bc0b0333d730121dd464983ddb140c5 Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Wed, 16 May 2018 21:56:49 -0400 Subject: [PATCH 3/5] @classmethod PackageFinder.get_extras_links() until it moves Not that I like @classmethod but it is honest here and lets the function be tested directly. --- pipenv/patched/notpip/index.py | 4 ++-- tasks/vendoring/patches/patched/pip.patch | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pipenv/patched/notpip/index.py b/pipenv/patched/notpip/index.py index 07881b08..f452c8c4 100644 --- a/pipenv/patched/notpip/index.py +++ b/pipenv/patched/notpip/index.py @@ -206,7 +206,8 @@ class PackageFinder(object): ) self.dependency_links.extend(links) - def get_extras_links(self, links): + @staticmethod + def get_extras_links(links): requires = [] extras = {} @@ -225,7 +226,6 @@ class PackageFinder(object): - @staticmethod def _sort_locations(locations, expand_dir=False): """ diff --git a/tasks/vendoring/patches/patched/pip.patch b/tasks/vendoring/patches/patched/pip.patch index 9e4b714d..8e068806 100644 --- a/tasks/vendoring/patches/patched/pip.patch +++ b/tasks/vendoring/patches/patched/pip.patch @@ -1362,7 +1362,8 @@ index f653f6e..48aaa35 100644 ) self.dependency_links.extend(links) -+ def get_extras_links(self, links): ++ @classmethod ++ def get_extras_links(links): + requires = [] + extras = {} + @@ -1384,7 +1385,6 @@ index f653f6e..48aaa35 100644 + + + -+ + @staticmethod def _sort_locations(locations, expand_dir=False): From 3b7929daa8cd6370ae64b1294d8f0dddb18e4ede Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Wed, 16 May 2018 23:07:09 -0400 Subject: [PATCH 4/5] Add a few test cases for PackageFinder.get_extras_links() --- tests/unit/test_patched.py | 131 +++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tests/unit/test_patched.py diff --git a/tests/unit/test_patched.py b/tests/unit/test_patched.py new file mode 100644 index 00000000..8d396539 --- /dev/null +++ b/tests/unit/test_patched.py @@ -0,0 +1,131 @@ +from __future__ import absolute_import + +import pytest + +from notpip.index import PackageFinder + + +get_extras_links_scenarios = { + 'windows and not windows': ( + [ + 'chardet', + '[:platform_system != "windows"]', + 'twisted', + '[:platform_system == "windows"]', + 'twisted[windows_platform]', + ], + { + ':platform_system != "windows"': [ + 'twisted', + ], + ':platform_system == "windows"': [ + 'twisted[windows_platform]', + ], + }, + ), + 'requests': ( + [ + 'chardet<3.1.0,>=3.0.2', + 'idna<2.7,>=2.5', + 'urllib3<1.23,>=1.21.1', + 'certifi>=2017.4.17', + '[security]', + 'pyOpenSSL>=0.14', + 'cryptography>=1.3.4', + 'idna>=2.0.0', + '[socks]', + 'PySocks!=1.5.7,>=1.5.6', + ( + '[socks:sys_platform == "win32"' + ' and (python_version == "2.7" or python_version == "2.6")]' + ), + 'win_inet_pton', + ], + { + 'security': [ + 'pyOpenSSL>=0.14', + 'cryptography>=1.3.4', + 'idna>=2.0.0', + ], + 'socks': [ + 'PySocks!=1.5.7,>=1.5.6', + ], + 'socks:sys_platform == "win32" ' + 'and (python_version == "2.7" or python_version == "2.6")': [ + 'win_inet_pton', + ], + } + ), + 'attrs': ( + [ + '[dev]', + 'coverage', + 'hypothesis', + 'pympler', + 'pytest', + 'six', + 'zope.interface', + 'sphinx', + 'zope.interface', + '[docs]', + 'sphinx', + 'zope.interface', + '[tests]', + 'coverage', + 'hypothesis', + 'pympler', + 'pytest', + 'six', + 'zope.interface', + ], + { + 'dev': [ + 'coverage', + 'hypothesis', + 'pympler', + 'pytest', + 'six', + 'zope.interface', + 'sphinx', + 'zope.interface', + ], + 'docs': [ + 'sphinx', + 'zope.interface', + ], + 'tests': [ + 'coverage', + 'hypothesis', + 'pympler', + 'pytest', + 'six', + 'zope.interface', + ], + }, + ), + 'misc': ( + [ + 'chardet', + '[:platform_system != "windows"]', + 'attrs', + '[:platform_system == "windows"]', + 'pytz', + ], + { + ':platform_system != "windows"': [ + 'attrs', + ], + ':platform_system == "windows"': [ + 'pytz', + ], + }, + ), +} + +@pytest.mark.parametrize( + 'scenarios,expected', + list(get_extras_links_scenarios.values()), + ids=list(get_extras_links_scenarios.keys()), +) +def test_get_extras_links(scenarios, expected): + assert PackageFinder.get_extras_links(scenarios) == expected From 0567c10d685b0ee25d629d9abe0692a23880811f Mon Sep 17 00:00:00 2001 From: Jeremy Fleischman Date: Wed, 2 May 2018 10:03:05 -0700 Subject: [PATCH 5/5] When formatting a requirement, only lowercase its name. This fixes https://github.com/pypa/pipenv/issues/2113. This bug was introduced in as a band-aid fix to . Pipenv then copied that code in , and inherited this latent bug. Maybe the right fix is for pypa/packaging to lowercase the name? There's a comment here about normalizing the requirement's name, which might be what this is referring to. To test this, I invented a new, very simple python package called `depends-on-marked-package`. The setup.py for this package is just: ```python import setuptools setuptools.setup( name="depends-on-marked-package", version="0.0.1", packages=setuptools.find_packages(), install_requires=['pytz; platform_python_implementation=="CPython"'], ) ``` This is a simplified version of gevent's setup.py's install_requires upon greenlet. --- pipenv/patched/piptools/utils.py | 32 +++++++++++++++++- tests/integration/test_install_markers.py | 20 +++++++++++ ..._marked_package-0.0.1-py2.py3-none-any.whl | Bin 0 -> 1215 bytes 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/pypi/depends-on-marked-package/depends_on_marked_package-0.0.1-py2.py3-none-any.whl diff --git a/pipenv/patched/piptools/utils.py b/pipenv/patched/piptools/utils.py index db8bb9b3..f3de3dd3 100644 --- a/pipenv/patched/piptools/utils.py +++ b/pipenv/patched/piptools/utils.py @@ -58,6 +58,36 @@ def make_install_requirement(name, version, extras, markers, constraint=False): constraint=constraint) +def _requirement_to_str_lowercase_name(requirement): + """ + Formats a packaging.requirements.Requirement with a lowercase name. + + This is simply a copy of + https://github.com/pypa/packaging/blob/16.8/packaging/requirements.py#L109-L124 + modified to lowercase the dependency name. + + Previously, we were invoking the original Requirement.__str__ method and + lowercasing the entire result, which would lowercase the name, *and* other, + important stuff that should not be lowercased (such as the marker). See + this issue for more information: https://github.com/pypa/pipenv/issues/2113. + """ + parts = [requirement.name.lower()] + + if requirement.extras: + parts.append("[{0}]".format(",".join(sorted(requirement.extras)))) + + if requirement.specifier: + parts.append(str(requirement.specifier)) + + if requirement.url: + parts.append("@ {0}".format(requirement.url)) + + if requirement.marker: + parts.append("; {0}".format(requirement.marker)) + + return "".join(parts) + + def format_requirement(ireq, marker=None): """ Generic formatter for pretty printing InstallRequirements to the terminal @@ -66,7 +96,7 @@ def format_requirement(ireq, marker=None): if ireq.editable: line = '-e {}'.format(ireq.link) else: - line = str(ireq.req).lower() + line = _requirement_to_str_lowercase_name(ireq.req) if marker: line = '{}; {}'.format(line, marker) diff --git a/tests/integration/test_install_markers.py b/tests/integration/test_install_markers.py index 87235d4a..9e3fa1d2 100644 --- a/tests/integration/test_install_markers.py +++ b/tests/integration/test_install_markers.py @@ -33,6 +33,26 @@ tablib = {version = "*", markers="os_name=='splashwear'"} c = p.pipenv('run python -c "import tablib;"') assert c.return_code == 1 +@pytest.mark.markers +@flaky +def test_platform_python_implementation_marker(PipenvInstance, pypi): + """Markers should be converted during locking to help users who input this incorrectly + """ + with PipenvInstance(pypi=pypi) as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[packages] +depends-on-marked-package = "*" + """.strip() + f.write(contents) + + c = p.pipenv('install') + assert c.return_code == 0 + + # depends-on-marked-package has an install_requires of 'pytz; platform_python_implementation=="CPython"' + # Verify that that marker shows up in our lockfile unaltered. + assert p.lockfile['default']['pytz']['markers'] == "platform_python_implementation == 'CPython'" + @pytest.mark.run @pytest.mark.alt diff --git a/tests/pypi/depends-on-marked-package/depends_on_marked_package-0.0.1-py2.py3-none-any.whl b/tests/pypi/depends-on-marked-package/depends_on_marked_package-0.0.1-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..d320deb3a4d39d987501a55af6abdc192c263cb4 GIT binary patch literal 1215 zcmWIWW@Zs#U|`^2SZJ`_XENJ$BW56v5s1x!I3=|pH7}(&K0hx$H?b%?H6^|vF*!Ri zJyq90&p^*mFD0|ML^m@pEnmMRzaTy*wJbG9ucV^nF%v@oin+o9f7;`L=H&sgK1t?= zd$_v#aG&?^J$sSYTUYDcne&^246YbIDDpq+t?Q+8Qh!sBhu29Ry-t1hm4O!wE*P6# zF~0JI>(qJui(hrF@oMOLojvKZDM&+8%l9dlr_c5vjTI)B&YnK+ebx8O>XuFkWtSaj zZrzZ`F0}&a#(zL;M3P&5T|*pQ977yq`)s)n8Hl)k|I>BBcBz${RR>>_RQIC3rx)#R zUwC#{MfTNt^Ny&(JwBkBj11|e+Rib`vj-ph-PpT_bxroP`U{VSjApmQ4&gIwmm zB#z6!qrH!RO#Zms;G}(V^3u~XR(B%A1*bfj>a(`?)8lFV9j1tr~mXAAW%R_r_y&$08-xgDp1%QF6)x|u!Ie|`S(42EZA#moWTj7%cTxU&i{ z#K2%lBZwk1j3CJpa4teO96g~U3}*o*0pfiCOZMpIqbDeY`F}_< YACkNRyjj^mCa?fu15m#vsGMQ|0Hc4WyZ`_I literal 0 HcmV?d00001