From b626a111502e73d34b4bef022d439a9061eaa7a7 Mon Sep 17 00:00:00 2001 From: Dan Ryan Date: Sat, 25 Aug 2018 13:15:47 -0400 Subject: [PATCH] Update requirementslib to 1.1.2 Signed-off-by: Dan Ryan --- pipenv/vendor/requirementslib/LICENSE | 24 +- pipenv/vendor/requirementslib/LICENSE.APACHE | 177 ----- pipenv/vendor/requirementslib/LICENSE.BSD | 23 - pipenv/vendor/requirementslib/__init__.py | 2 +- pipenv/vendor/requirementslib/_compat.py | 56 -- .../requirementslib/_vendor/__init__.py | 1 - .../requirementslib/_vendor/pipfile/LICENSE | 3 - .../_vendor/pipfile/LICENSE.APACHE | 177 ----- .../_vendor/pipfile/LICENSE.BSD | 23 - .../_vendor/pipfile/__about__.py | 21 - .../_vendor/pipfile/__init__.py | 11 - .../requirementslib/_vendor/pipfile/api.py | 230 ------- pipenv/vendor/requirementslib/exceptions.py | 9 + .../vendor/requirementslib/models/__init__.py | 3 +- pipenv/vendor/requirementslib/models/cache.py | 266 +++++++ .../requirementslib/models/dependencies.py | 650 ++++++++++++++++++ .../vendor/requirementslib/models/lockfile.py | 82 ++- .../vendor/requirementslib/models/markers.py | 15 +- .../vendor/requirementslib/models/pipfile.py | 206 ++---- .../requirementslib/models/requirements.py | 425 +++++++++--- .../requirementslib/models/resolvers.py | 239 +++++++ pipenv/vendor/requirementslib/models/utils.py | 350 +++++++++- pipenv/vendor/requirementslib/models/vcs.py | 48 ++ pipenv/vendor/requirementslib/utils.py | 137 ++-- 24 files changed, 2039 insertions(+), 1139 deletions(-) delete mode 100644 pipenv/vendor/requirementslib/LICENSE.APACHE delete mode 100644 pipenv/vendor/requirementslib/LICENSE.BSD delete mode 100644 pipenv/vendor/requirementslib/_compat.py delete mode 100644 pipenv/vendor/requirementslib/_vendor/__init__.py delete mode 100644 pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE delete mode 100644 pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE.APACHE delete mode 100644 pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE.BSD delete mode 100644 pipenv/vendor/requirementslib/_vendor/pipfile/__about__.py delete mode 100644 pipenv/vendor/requirementslib/_vendor/pipfile/__init__.py delete mode 100644 pipenv/vendor/requirementslib/_vendor/pipfile/api.py create mode 100644 pipenv/vendor/requirementslib/models/cache.py create mode 100644 pipenv/vendor/requirementslib/models/dependencies.py create mode 100644 pipenv/vendor/requirementslib/models/resolvers.py create mode 100644 pipenv/vendor/requirementslib/models/vcs.py diff --git a/pipenv/vendor/requirementslib/LICENSE b/pipenv/vendor/requirementslib/LICENSE index 6f62d44e..8c731e27 100644 --- a/pipenv/vendor/requirementslib/LICENSE +++ b/pipenv/vendor/requirementslib/LICENSE @@ -1,3 +1,21 @@ -This software is made available under the terms of *either* of the licenses -found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made -under the terms of *both* these licenses. +The MIT License (MIT) + +Copyright 2018 Dan Ryan. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pipenv/vendor/requirementslib/LICENSE.APACHE b/pipenv/vendor/requirementslib/LICENSE.APACHE deleted file mode 100644 index 4947287f..00000000 --- a/pipenv/vendor/requirementslib/LICENSE.APACHE +++ /dev/null @@ -1,177 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/pipenv/vendor/requirementslib/LICENSE.BSD b/pipenv/vendor/requirementslib/LICENSE.BSD deleted file mode 100644 index 698fc43e..00000000 --- a/pipenv/vendor/requirementslib/LICENSE.BSD +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) Kenneth Reitz and individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pipenv/vendor/requirementslib/__init__.py b/pipenv/vendor/requirementslib/__init__.py index eeb970c4..faba3b44 100644 --- a/pipenv/vendor/requirementslib/__init__.py +++ b/pipenv/vendor/requirementslib/__init__.py @@ -1,5 +1,5 @@ # -*- coding=utf-8 -*- -__version__ = "1.0.11" +__version__ = '1.1.2' from .exceptions import RequirementError diff --git a/pipenv/vendor/requirementslib/_compat.py b/pipenv/vendor/requirementslib/_compat.py deleted file mode 100644 index 52b35565..00000000 --- a/pipenv/vendor/requirementslib/_compat.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding=utf-8 -*- -import importlib -import six - -# Use these imports as compatibility imports -try: - from pathlib import Path -except ImportError: - from pathlib2 import Path - -try: - from urllib.parse import urlparse, unquote -except ImportError: - from urlparse import urlparse, unquote - -if six.PY2: - - class FileNotFoundError(IOError): - pass - - -else: - - class FileNotFoundError(FileNotFoundError): - pass - - -def do_import(module_path, subimport=None, old_path=None): - internal = "pip._internal.{0}".format(module_path) - old_path = old_path or module_path - pip9 = "pip.{0}".format(old_path) - try: - _tmp = importlib.import_module(internal) - except ImportError: - _tmp = importlib.import_module(pip9) - if subimport: - return getattr(_tmp, subimport, _tmp) - return _tmp - - -InstallRequirement = do_import("req.req_install", "InstallRequirement") -user_cache_dir = do_import("utils.appdirs", "user_cache_dir") -FAVORITE_HASH = do_import("utils.hashes", "FAVORITE_HASH") -is_file_url = do_import("download", "is_file_url") -url_to_path = do_import("download", "url_to_path") -path_to_url = do_import("download", "path_to_url") -is_archive_file = do_import("download", "is_archive_file") -_strip_extras = do_import("req.req_install", "_strip_extras") -Link = do_import("index", "Link") -Wheel = do_import("wheel", "Wheel") -is_installable_file = do_import("utils.misc", "is_installable_file", old_path="utils") -is_installable_dir = do_import("utils.misc", "is_installable_dir", old_path="utils") -make_abstract_dist = do_import( - "operations.prepare", "make_abstract_dist", old_path="req.req_set" -) -VcsSupport = do_import("vcs", "VcsSupport") diff --git a/pipenv/vendor/requirementslib/_vendor/__init__.py b/pipenv/vendor/requirementslib/_vendor/__init__.py deleted file mode 100644 index a6bcbda1..00000000 --- a/pipenv/vendor/requirementslib/_vendor/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding=utf-8 -*- diff --git a/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE b/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE deleted file mode 100644 index 6f62d44e..00000000 --- a/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -This software is made available under the terms of *either* of the licenses -found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made -under the terms of *both* these licenses. diff --git a/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE.APACHE b/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE.APACHE deleted file mode 100644 index 4947287f..00000000 --- a/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE.APACHE +++ /dev/null @@ -1,177 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE.BSD b/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE.BSD deleted file mode 100644 index 698fc43e..00000000 --- a/pipenv/vendor/requirementslib/_vendor/pipfile/LICENSE.BSD +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) Kenneth Reitz and individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pipenv/vendor/requirementslib/_vendor/pipfile/__about__.py b/pipenv/vendor/requirementslib/_vendor/pipfile/__about__.py deleted file mode 100644 index 3ba72191..00000000 --- a/pipenv/vendor/requirementslib/_vendor/pipfile/__about__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -__all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", -] - -__title__ = "pipfile" -__summary__ = "" -__uri__ = "https://github.com/pypa/pipfile" - -__version__ = "0.0.2" - -__author__ = "Kenneth Reitz and individual contributors" -__email__ = "me@kennethreitz.org" - -__license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2017 %s" % __author__ diff --git a/pipenv/vendor/requirementslib/_vendor/pipfile/__init__.py b/pipenv/vendor/requirementslib/_vendor/pipfile/__init__.py deleted file mode 100644 index fddd4f90..00000000 --- a/pipenv/vendor/requirementslib/_vendor/pipfile/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from __future__ import absolute_import, division, print_function - -from .__about__ import ( - __author__, __copyright__, __email__, __license__, __summary__, __title__, - __uri__, __version__ -) - -from .api import load, Pipfile diff --git a/pipenv/vendor/requirementslib/_vendor/pipfile/api.py b/pipenv/vendor/requirementslib/_vendor/pipfile/api.py deleted file mode 100644 index e8fa0277..00000000 --- a/pipenv/vendor/requirementslib/_vendor/pipfile/api.py +++ /dev/null @@ -1,230 +0,0 @@ -import toml - -import codecs -import json -import hashlib -import platform -import six -import sys -import os - - -DEFAULT_SOURCE = { - u'url': u'https://pypi.org/simple', - u'verify_ssl': True, - u'name': u'pypi', -} - - -def format_full_version(info): - version = '{0.major}.{0.minor}.{0.micro}'.format(info) - kind = info.releaselevel - if kind != 'final': - version += kind[0] + str(info.serial) - return version - - -def walk_up(bottom): - """mimic os.walk, but walk 'up' instead of down the directory tree. - From: https://gist.github.com/zdavkeos/1098474 - """ - - bottom = os.path.realpath(bottom) - - # get files in current dir - try: - names = os.listdir(bottom) - except Exception: - return - - dirs, nondirs = [], [] - for name in names: - if os.path.isdir(os.path.join(bottom, name)): - dirs.append(name) - else: - nondirs.append(name) - - yield bottom, dirs, nondirs - - new_path = os.path.realpath(os.path.join(bottom, '..')) - - # see if we are at the top - if new_path == bottom: - return - - for x in walk_up(new_path): - yield x - - -class PipfileParser(object): - def __init__(self, filename='Pipfile'): - self.filename = filename - self.sources = [] - self.groups = { - 'default': [], - 'develop': [] - } - self.group_stack = ['default'] - self.requirements = [] - - def __repr__(self): - return '= packaging.version.parse('18'): + with RequirementTracker() as req_tracker: + preparer = preparer(req_tracker=req_tracker) + yield resolver(preparer=preparer) + else: + preparer = preparer() + yield resolver(preparer=preparer) + + +def get_grouped_dependencies(constraints): + # We need to track what contributed a specifierset + # as well as which specifiers were required by the root node + # in order to resolve any conflicts when we are deciding which thing to backtrack on + # then we take the loose match (which _is_ flexible) and start moving backwards in + # versions by popping them off of a stack and checking for the conflicting package + for _, ireqs in full_groupby(constraints, key=key_from_ireq): + ireqs = list(ireqs) + editable_ireq = first(ireqs, key=lambda ireq: ireq.editable) + if editable_ireq: + yield editable_ireq # ignore all the other specs: the editable one is the one that counts + continue + ireqs = iter(ireqs) + # deepcopy the accumulator so as to not modify the self.our_constraints invariant + combined_ireq = copy.deepcopy(next(ireqs)) + for ireq in ireqs: + # NOTE we may be losing some info on dropped reqs here + try: + combined_ireq.req.specifier &= ireq.req.specifier + except TypeError: + if ireq.req.specifier._specs and not combined_ireq.req.specifier._specs: + combined_ireq.req.specifier._specs = ireq.req.specifier._specs + combined_ireq.constraint &= ireq.constraint + if not combined_ireq.markers: + combined_ireq.markers = ireq.markers + else: + _markers = combined_ireq.markers._markers + if not isinstance(_markers[0], (tuple, list)): + combined_ireq.markers._markers = [ + _markers, + "and", + ireq.markers._markers, + ] + # Return a sorted, de-duped tuple of extras + combined_ireq.extras = tuple( + sorted(set(tuple(combined_ireq.extras) + tuple(ireq.extras))) + ) + yield combined_ireq diff --git a/pipenv/vendor/requirementslib/models/lockfile.py b/pipenv/vendor/requirementslib/models/lockfile.py index c04ae021..92055d6e 100644 --- a/pipenv/vendor/requirementslib/models/lockfile.py +++ b/pipenv/vendor/requirementslib/models/lockfile.py @@ -1,24 +1,54 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -import attr + import json +import os + +import plette.lockfiles +import six + +from vistir.compat import Path +from vistir.contextmanagers import atomic_open_for_write + from .requirements import Requirement -from .utils import optional_instance_of -from .._compat import Path, FileNotFoundError -@attr.s -class Lockfile(object): - dev_requirements = attr.ib(default=attr.Factory(list)) - requirements = attr.ib(default=attr.Factory(list)) - path = attr.ib(default=None, validator=optional_instance_of(Path)) - pipfile_hash = attr.ib(default=None) +DEFAULT_NEWLINES = u"\n" + + +def preferred_newlines(f): + if isinstance(f.newlines, six.text_type): + return f.newlines + return DEFAULT_NEWLINES + + +class Lockfile(plette.lockfiles.Lockfile): + def __init__(self, *args, **kwargs): + path = kwargs.pop("path", None) + self.requirements = kwargs.pop("requirements", []) + self.dev_requirements = kwargs.pop("dev_requirements", []) + self.path = Path(path) if path else None + self.newlines = u"\n" + super(Lockfile, self).__init__(*args, **kwargs) + + @classmethod + def load(cls, path): + if not path: + path = os.curdir + path = Path(path).absolute() + if path.is_dir(): + path = path / "Pipfile.lock" + elif path.name == "Pipfile": + path = path.parent / "Pipfile.lock" + if not path.exists(): + raise OSError("Path does not exist: %s" % path) + return cls.create(path.parent, lockfile_name=path.name) @classmethod def create(cls, project_path, lockfile_name="Pipfile.lock"): """Create a new lockfile instance - :param project_path: Path to the project root + :param project_path: Path to project root :type project_path: str or :class:`~pathlib.Path` :returns: List[:class:`~requirementslib.Requirement`] objects """ @@ -28,20 +58,30 @@ class Lockfile(object): lockfile_path = project_path / lockfile_name requirements = [] dev_requirements = [] - if not lockfile_path.exists(): - raise FileNotFoundError("No such lockfile: %s" % lockfile_path) - with lockfile_path.open(encoding="utf-8") as f: - lockfile = json.loads(f.read()) + lockfile = super(Lockfile, cls).load(f) + lockfile.newlines = preferred_newlines(f) for k in lockfile["develop"].keys(): - dev_requirements.append(Requirement.from_pipfile(k, lockfile["develop"][k])) + dev_requirements.append(Requirement.from_pipfile(k, lockfile.develop[k]._data)) for k in lockfile["default"].keys(): - requirements.append(Requirement.from_pipfile(k, lockfile["default"][k])) - return cls( - path=lockfile_path, - requirements=requirements, - dev_requirements=dev_requirements, - ) + requirements.append(Requirement.from_pipfile(k, lockfile.default[k]._data)) + lockfile.requirements = requirements + lockfile.dev_requirements = dev_requirements + lockfile.path = lockfile_path + return lockfile + + @property + def dev_requirements_list(self): + return [r.as_pipfile() for r in self.dev_requirements] + + @property + def requirements_list(self): + return [r.as_pipfile() for r in self.requirements] + + def write(self): + open_kwargs = {"newline": self.newlines} + with atomic_open_for_write(self.path.as_posix(), **open_kwargs) as f: + super(Lockfile, self).dump(f, encoding="utf-8") def as_requirements(self, include_hashes=False, dev=False): """Returns a list of requirements in pip-style format""" diff --git a/pipenv/vendor/requirementslib/models/markers.py b/pipenv/vendor/requirementslib/models/markers.py index 70adc21f..534978ed 100644 --- a/pipenv/vendor/requirementslib/models/markers.py +++ b/pipenv/vendor/requirementslib/models/markers.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- import attr -import six -from packaging.markers import Marker, InvalidMarker -from .baserequirement import BaseRequirement -from .utils import validate_markers, filter_none + +from packaging.markers import InvalidMarker, Marker + from ..exceptions import RequirementError +from .baserequirement import BaseRequirement +from .utils import filter_none, validate_markers @attr.s @@ -84,10 +85,10 @@ class PipenvMarkers(BaseRequirement): markers = [] for marker in marker_strings: markers.append(marker) - marker = '' + combined_marker = None try: - marker = cls.make_marker(" and ".join(markers)) + combined_marker = cls.make_marker(" and ".join(markers)) except RequirementError: pass else: - return marker + return combined_marker diff --git a/pipenv/vendor/requirementslib/models/pipfile.py b/pipenv/vendor/requirementslib/models/pipfile.py index af67752a..f9522524 100644 --- a/pipenv/vendor/requirementslib/models/pipfile.py +++ b/pipenv/vendor/requirementslib/models/pipfile.py @@ -1,100 +1,13 @@ # -*- coding: utf-8 -*- -import attr -import contoml import os -import toml -from .._vendor import pipfile + +from vistir.compat import Path + from .requirements import Requirement -from .utils import optional_instance_of, filter_none -from .._compat import Path, FileNotFoundError -from ..exceptions import RequirementError +import plette.pipfiles -@attr.s -class Source(object): - #: URL to PyPI instance - url = attr.ib(default="pypi") - #: If False, skip SSL checks - verify_ssl = attr.ib(default=True, validator=optional_instance_of(bool)) - #: human name to refer to this source (can be referenced in packages or dev-packages) - name = attr.ib(default="") - - def get_dict(self): - return attr.asdict(self) - - @property - def expanded(self): - source_dict = attr.asdict(self).copy() - source_dict["url"] = os.path.expandvars(source_dict.get("url")) - return source_dict - - -@attr.s -class Section(object): - ALLOWED_NAMES = ("packages", "dev-packages") - #: Name of the pipfile section - name = attr.ib(default="packages") - #: A list of requirements that are contained by the section - requirements = attr.ib(default=list) - - def get_dict(self): - _dict = {} - for req in self.requirements: - _dict.update(req.as_pipfile()) - return {self.name: _dict} - - @property - def vcs_requirements(self): - return [req for req in self.requirements if req.is_vcs] - - @property - def editable_requirements(self): - return [req for req in self.requirements if req.editable] - - -@attr.s -class RequiresSection(object): - python_version = attr.ib(default=None) - python_full_version = attr.ib(default=None) - - def get_dict(self): - requires = attr.asdict(self, filter=filter_none) - if not requires: - return {} - return {"requires": requires} - - -@attr.s -class PipenvSection(object): - allow_prereleases = attr.ib(default=False) - - def get_dict(self): - if self.allow_prereleases: - return {"pipenv": attr.asdict(self)} - return {} - - -@attr.s -class Pipfile(object): - #: Path to the pipfile - path = attr.ib(default=None, converter=Path, validator=optional_instance_of(Path)) - #: Sources listed in the pipfile - sources = attr.ib(default=attr.Factory(list)) - #: Sections contained by the pipfile - sections = attr.ib(default=attr.Factory(list)) - #: Scripts found in the pipfile - scripts = attr.ib(default=attr.Factory(dict)) - #: This section stores information about what python version is required - requires = attr.ib(default=attr.Factory(RequiresSection)) - #: This section stores information about pipenv such as prerelease requirements - pipenv = attr.ib(default=attr.Factory(PipenvSection)) - #: This is the sha256 hash of the pipfile (without environment interpolation) - pipfile_hash = attr.ib() - - @pipfile_hash.default - def get_hash(self): - p = pipfile.load(self.path.as_posix(), inject_env=False) - return p.hash +class Pipfile(plette.pipfiles.Pipfile): @property def requires_python(self): @@ -102,50 +15,7 @@ class Pipfile(object): @property def allow_prereleases(self): - return self.pipenv.allow_prereleases - - def get_sources(self): - """Return a dictionary with a list of dictionaries of pipfile sources""" - _dict = {} - for src in self.sources: - _dict.update(src.get_dict()) - return {"source": _dict} if _dict else {} - - def get_sections(self): - """Return a dictionary with both pipfile sections and requirements""" - _dict = {} - for section in self.sections: - _dict.update(section.get_dict()) - return _dict - - def get_pipenv(self): - pipenv_dict = self.pipenv.get_dict() - if pipenv_dict: - return pipenv_dict - - def get_requires(self): - req_dict = self.requires.get_dict() - return req_dict if req_dict else {} - - def get_dict(self): - _dict = attr.asdict(self, recurse=False) - for k in ["path", "pipfile_hash", "sources", "sections", "requires", "pipenv"]: - if k in _dict: - _dict.pop(k) - return _dict - - def dump(self, to_dict=False): - """Dumps the pipfile to a toml string - """ - - _dict = self.get_sources() - _dict.update(self.get_sections()) - _dict.update(self.get_dict()) - _dict.update(self.get_pipenv()) - _dict.update(self.get_requires()) - if to_dict: - return _dict - return contoml.dumps(_dict) + return self.get("pipenv", {}).get("allow_prereleases", False) @classmethod def load(cls, path): @@ -156,34 +26,40 @@ class Pipfile(object): raise FileNotFoundError("%s is not a valid project path!" % path) elif not pipfile_path.exists() or not pipfile_path.is_file(): raise RequirementError("%s is not a valid Pipfile" % pipfile_path) - pipfile_dict = toml.load(pipfile_path.as_posix()) - sections = [cls.get_section(pipfile_dict, s) for s in Section.ALLOWED_NAMES] - pipenv = pipfile_dict.get("pipenv", {}) - requires = pipfile_dict.get("requires", {}) - creation_dict = { - "path": pipfile_path, - "sources": [Source(**src) for src in pipfile_dict.get("source", [])], - "sections": sections, - "scripts": pipfile_dict.get("scripts"), - } - if requires: - creation_dict["requires"] = RequiresSection(**requires) - if pipenv: - creation_dict["pipenv"] = PipenvSection(**pipenv) - return cls(**creation_dict) + with pipfile_path.open(encoding="utf-8") as fp: + pipfile = super(Pipfile, cls).load(fp) + pipfile.dev_requirements = [ + Requirement.from_pipfile(k, v) for k, v in pipfile.dev_packages.items() + ] + pipfile.requirements = [ + Requirement.from_pipfile(k, v) for k, v in pipfile.packages.items() + ] + pipfile.path = pipfile_path + return pipfile - @staticmethod - def get_section(pf_dict, section): - """Get section objects from a pipfile dictionary + # def resolve(self): + # It would be nice to still use this api someday + # option_sources = [s.expanded for s in self.sources] + # pip_args = [] + # if self.pipenv.allow_prereleases: + # pip_args.append('--pre') + # pip_options = get_pip_options(pip_args, sources=option_sources) + # finder = get_finder(sources=option_sources, pip_options=pip_options) + # resolver = DependencyResolver.create(finder=finder, allow_prereleases=self.pipenv.allow_prereleases) + # pkg_dict = {} + # for pkg in self.dev_packages.requirements + self.packages.requirements: + # pkg_dict[pkg.name] = pkg + # resolver.resolve(list(pkg_dict.values())) + # return resolver - :param pf_dict: A toml loaded pipfile dictionary - :type pf_dict: dict - :returns: Section objects - """ - sect = pf_dict.get(section) - requirements = [] - if section not in Section.ALLOWED_NAMES: - raise ValueError("Not a valid pipfile section name: %s" % section) - for name, pf_entry in sect.items(): - requirements.append(Requirement.from_pipfile(name, pf_entry)) - return Section(name=section, requirements=requirements) + @property + def dev_packages(self, as_requirements=True): + if as_requirements: + return self.dev_requirements + return self.dev_packages + + @property + def packages(self, as_requirements=True): + if as_requirements: + return self.requirements + return self.packages diff --git a/pipenv/vendor/requirementslib/models/requirements.py b/pipenv/vendor/requirementslib/models/requirements.py index ff3bba73..1baaa265 100644 --- a/pipenv/vendor/requirementslib/models/requirements.py +++ b/pipenv/vendor/requirementslib/models/requirements.py @@ -1,55 +1,46 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -import attr import collections import hashlib import os -import requirements + +import attr +import atexit from first import first +from packaging.markers import Marker +from packaging.specifiers import Specifier, SpecifierSet +from packaging.utils import canonicalize_name from six.moves.urllib import parse as urllib_parse +from six.moves.urllib.parse import unquote +from pip_shims.shims import ( + InstallRequirement, Link, Wheel, _strip_extras, parse_version, path_to_url, + url_to_path +) +from vistir.compat import FileNotFoundError, Path, TemporaryDirectory +from vistir.misc import dedup +from vistir.path import get_converted_relative_path, is_valid_url, is_file_url, mkdir_p + +from ..exceptions import RequirementError +from ..utils import VCS_LIST, is_vcs, is_installable_file from .baserequirement import BaseRequirement +from .dependencies import ( + AbstractDependency, find_all_matches, get_abstract_dependencies, + get_dependencies, get_finder +) from .markers import PipenvMarkers from .utils import ( - HASH_STRING, - extras_to_string, - get_version, - specs_to_string, - validate_specifiers, - validate_path, - validate_vcs, - build_vcs_link, - add_ssh_scheme_to_git_uri, - strip_ssh_from_git_uri, - split_vcs_method_from_uri, - filter_none, - optional_instance_of, - split_markers_from_line, - parse_extras, -) -from .._compat import ( - Link, - path_to_url, - url_to_path, - _strip_extras, - InstallRequirement, - Path, - urlparse, - unquote, - Wheel, - FileNotFoundError, -) -from ..exceptions import RequirementError -from ..utils import ( - VCS_LIST, - is_installable_file, - is_vcs, - is_valid_url, - pep423_name, - get_converted_relative_path, + HASH_STRING, add_ssh_scheme_to_git_uri, build_vcs_link, filter_none, + format_requirement, get_version, init_requirement, + is_pinned_requirement, make_install_requirement, optional_instance_of, + parse_extras, specs_to_string, split_markers_from_line, + split_vcs_method_from_uri, strip_ssh_from_git_uri, validate_path, + validate_specifiers, validate_vcs, extras_to_string ) +from .vcs import VCSRepository +from packaging.requirements import Requirement as PackagingRequirement @attr.s @@ -58,25 +49,31 @@ class NamedRequirement(BaseRequirement): version = attr.ib(validator=attr.validators.optional(validate_specifiers)) req = attr.ib() extras = attr.ib(default=attr.Factory(list)) + editable = attr.ib(default=False) @req.default def get_requirement(self): - from pkg_resources import RequirementParseError - try: - req = first(requirements.parse("{0}{1}".format(self.name, self.version))) - except RequirementParseError: - raise RequirementError( - "Error parsing requirement: %s%s" % (self.name, self.version) - ) + req = init_requirement("{0}{1}".format(canonicalize_name(self.name), self.version)) return req @classmethod def from_line(cls, line): - req = first(requirements.parse(line)) + req = init_requirement(line) specifiers = None if req.specifier: - specifiers = specs_to_string(req.specs) - return cls(name=req.name, version=specifiers, req=req) + specifiers = specs_to_string(req.specifier) + req.line = line + name = getattr(req, "name", None) + if not name: + name = getattr(req, "project_name", None) + req.name = name + if not name: + name = getattr(req, "key", line) + req.name = name + extras = None + if req.extras: + extras = list(req.extras) + return cls(name=name, version=specifiers, req=req, extras=extras) @classmethod def from_pipfile(cls, name, pipfile): @@ -85,13 +82,17 @@ class NamedRequirement(BaseRequirement): creation_args = {k: v for k, v in pipfile.items() if k in cls.attr_fields()} creation_args["name"] = name version = get_version(pipfile) + extras = creation_args.get("extras", None) creation_args["version"] = version - creation_args["req"] = first(requirements.parse("{0}{1}".format(name, version))) + req = init_requirement("{0}{1}".format(name, version)) + if extras: + req.extras += tuple(extras,) + creation_args["req"] = req return cls(**creation_args) @property def line_part(self): - return "{self.name}".format(self=self) + return "{0}".format(canonicalize_name(self.name)) @property def pipfile_part(self): @@ -243,17 +244,17 @@ class FileRequirement(BaseRequirement): and self.setup_path and self.setup_path.exists() ): - from distutils.core import run_setup + from setuptools.dist import distutils old_curdir = os.path.abspath(os.getcwd()) try: os.chdir(str(self.setup_path.parent)) - dist = run_setup(self.setup_path.as_posix(), stop_after="init") + dist = distutils.core.run_setup(self.setup_path.as_posix()) name = dist.get_name() except (FileNotFoundError, IOError) as e: dist = None except Exception as e: - from .._compat import InstallRequirement, make_abstract_dist + from pip_shims.shims import InstallRequirement, make_abstract_dist try: if not isinstance(Path, self.path): @@ -289,14 +290,18 @@ class FileRequirement(BaseRequirement): @req.default def get_requirement(self): - prefix = "-e " if self.editable else "" - line = "{0}{1}".format(prefix, self.link.url) - req = first(requirements.parse(line)) + req = init_requirement(canonicalize_name(self.name)) + req.editable = False + req.line = self.link.url_without_fragment if self.path and self.link and self.link.scheme.startswith("file"): req.local_file = True req.path = self.path - req.uri = None + req.url = None self._uri_scheme = "file" + else: + req.local_file = False + req.path = None + req.url = self.link.url_without_fragment if self.editable: req.editable = True req.link = self.link @@ -330,7 +335,7 @@ class FileRequirement(BaseRequirement): editable = line.startswith("-e ") line = line.split(" ", 1)[1] if editable else line setup_path = None - if not any([is_installable_file(line), is_valid_url(line)]): + if not any([is_installable_file(line), is_valid_url(line), is_file_url(line)]): raise RequirementError( "Supplied requirement is not installable: {0!r}".format(line) ) @@ -473,17 +478,6 @@ class VCSRequirement(FileRequirement): name = attr.ib() link = attr.ib() req = attr.ib() - _INCLUDE_FIELDS = ( - "editable", - "uri", - "path", - "vcs", - "ref", - "subdirectory", - "name", - "link", - "req", - ) def __attrs_post_init__(self): split = urllib_parse.urlsplit(self.uri) @@ -522,16 +516,66 @@ class VCSRequirement(FileRequirement): uri = "{0}+{1}".format(self.vcs, uri) return uri + def get_commit_hash(self, src_dir=None): + src_dir = os.environ.get('SRC_DIR', None) if not src_dir else src_dir + if not src_dir: + _src_dir = TemporaryDirectory() + atexit.register(_src_dir.cleanup) + src_dir = _src_dir.name + checkout_dir = Path(src_dir).joinpath(self.name).as_posix() + vcsrepo = VCSRepository( + url=self.link.url, + name=self.name, + ref=self.ref if self.ref else None, + checkout_directory=checkout_dir, + vcs_type=self.vcs + ) + vcsrepo.obtain() + return vcsrepo.get_commit_hash() + + def update_repo(self, src_dir=None, ref=None): + src_dir = os.environ.get('SRC_DIR', None) if not src_dir else src_dir + if not src_dir: + _src_dir = TemporaryDirectory() + atexit.register(_src_dir.cleanup) + src_dir = _src_dir.name + checkout_dir = Path(src_dir).joinpath(self.name).as_posix() + ref = self.ref if not ref else ref + vcsrepo = VCSRepository( + url=self.link.url, + name=self.name, + ref=ref if ref else None, + checkout_directory=checkout_dir, + vcs_type=self.vcs + ) + if not os.path.exists(checkout_dir): + vcsrepo.obtain() + else: + vcsrepo.update() + return vcsrepo.get_commit_hash() + @req.default def get_requirement(self): - prefix = "-e " if self.editable else "" - line = "{0}{1}".format(prefix, self.link.url) - req = first(requirements.parse(line)) + name = self.name or self.link.egg_fragment + url = self.uri or self.link.url_without_fragment + if not name: + raise ValueError( + "pipenv requires an #egg fragment for version controlled " + "dependencies. Please install remote dependency " + "in the form {0}#egg=.".format(url) + ) + req = init_requirement(canonicalize_name(self.name)) + req.editable = self.editable + req.url = self.uri + req.line = self.link.url + if self.ref: + req.revision = self.ref + if self.extras: + req.extras = self.extras + req.vcs = self.vcs if self.path and self.link and self.link.scheme.startswith("file"): req.local_file = True req.path = self.path - if self.editable: - req.editable = True req.link = self.link if ( self.uri != unquote(self.link.url_without_fragment) @@ -539,19 +583,7 @@ class VCSRequirement(FileRequirement): and "git+git@" in self.uri ): req.line = self.uri - req.uri = self.uri - if not req.name: - raise ValueError( - "pipenv requires an #egg fragment for version controlled " - "dependencies. Please install remote dependency " - "in the form {0}#egg=.".format(req.uri) - ) - if self.vcs and not req.vcs: - req.vcs = self.vcs - if self.ref and not req.revision: - req.revision = self.ref - if self.extras and not req.extras: - req.extras = self.extras + req.url = self.uri return req @classmethod @@ -564,6 +596,10 @@ class VCSRequirement(FileRequirement): if k in pipfile ] for key in pipfile_keys: + if key == "extras": + extras = pipfile.get(key, None) + if extras: + pipfile[key] = sorted(dedup([extra.lower() for extra in extras])) if key in VCS_LIST: creation_args["vcs"] = key composed_uri = add_ssh_scheme_to_git_uri( @@ -612,8 +648,12 @@ class VCSRequirement(FileRequirement): def line_part(self): """requirements.txt compatible line part sans-extras""" if self.req: - return self.req.line - base = "{0}".format(self.link) + base = self.req.line + if base and self.extras and not extras_to_string(self.extras) in base: + if self.subdirectory: + base = "{0}".format(self.get_link().url) + else: + base = "{0}{1}".format(base, extras_to_string(sorted(self.extras))) if self.editable: base = "-e {0}".format(base) return base @@ -650,8 +690,8 @@ class Requirement(object): editable = attr.ib(default=None) hashes = attr.ib(default=attr.Factory(list), converter=list) extras = attr.ib(default=attr.Factory(list)) + abstract_dep = attr.ib(default=None) _ireq = None - _INCLUDE_FIELDS = ("name", "markers", "index", "editable", "hashes", "extras") @name.default def get_name(self): @@ -678,14 +718,20 @@ class Requirement(object): @property def extras_as_pip(self): if self.extras: - return "[{0}]".format(",".join(self.extras)) + return "[{0}]".format(",".join(sorted([extra.lower() for extra in self.extras]))) return "" + @property + def commit_hash(self): + if not self.is_vcs: + return None + return self.req.get_commit_hash() + @specifiers.default def get_specifiers(self): if self.req and self.req.req.specifier: - return specs_to_string(self.req.req.specs) + return specs_to_string(self.req.req.specifier) return @property @@ -702,10 +748,15 @@ class Requirement(object): @property def normalized_name(self): - return pep423_name(self.name) + return canonicalize_name(self.name) + + def copy(self): + return attr.evolve(self) @classmethod def from_line(cls, line): + if isinstance(line, InstallRequirement): + line = format_requirement(line) hashes = None if "--hash=" in line: hashes = line.split(" --hash=") @@ -714,6 +765,7 @@ class Requirement(object): line = line.split(" ", 1)[1] if editable else line line, markers = split_markers_from_line(line) line, extras = _strip_extras(line) + specifiers = '' if extras: extras = parse_extras(extras) line = line.strip('"').strip("'").strip() @@ -721,9 +773,10 @@ class Requirement(object): vcs = None # Installable local files and installable non-vcs urls are handled # as files, generally speaking - if is_installable_file(line) or (is_valid_url(line) and not is_vcs(line)): + line_is_vcs = is_vcs(line) + if is_installable_file(line) or ((is_file_url(line) or is_valid_url(line)) and not line_is_vcs): r = FileRequirement.from_line(line_with_prefix) - elif is_vcs(line): + elif line_is_vcs: r = VCSRequirement.from_line(line_with_prefix, extras=extras) vcs = r.vcs elif line == "." and not is_installable_file(line): @@ -739,6 +792,7 @@ class Requirement(object): spec_idx = min((line.index(match) for match in spec_matches)) name = line[:spec_idx] version = line[spec_idx:] + specifiers = version if not extras: name, extras = _strip_extras(name) if extras: @@ -746,8 +800,19 @@ class Requirement(object): if version: name = "{0}{1}".format(name, version) r = NamedRequirement.from_line(line) + req_markers = None if markers: - r.req.markers = markers + req_markers = PackagingRequirement("fakepkg; {0}".format(markers)) + r.req.marker = getattr(req_markers, "marker", None) + r.req.local_file = getattr(r.req, "local_file", False) + name = getattr(r.req, "name", None) + if not name: + name = getattr(r.req, "project_name", None) + r.req.name = name + if not name: + name = getattr(r.req, "key", None) + if name: + r.req.name = name args = { "name": r.name, "vcs": vcs, @@ -756,15 +821,26 @@ class Requirement(object): "editable": editable, } if extras: + extras = sorted(dedup([extra.lower() for extra in extras])) args["extras"] = extras r.req.extras = extras r.extras = extras elif r.extras: - args["extras"] = r.extras + args["extras"] = sorted(dedup([extra.lower() for extra in r.extras])) if hashes: args["hashes"] = hashes return cls(**args) + @classmethod + def from_ireq(cls, ireq): + return cls.from_line(format_requirement(ireq)) + + @classmethod + def from_metadata(cls, name, version, extras, markers): + return cls.from_ireq(make_install_requirement( + name, version, extras=extras, markers=markers, + )) + @classmethod def from_pipfile(cls, name, pipfile): _pipfile = {} @@ -780,8 +856,14 @@ class Requirement(object): else: r = NamedRequirement.from_pipfile(name, pipfile) markers = PipenvMarkers.from_pipfile(name, _pipfile) + req_markers = None if markers: markers = str(markers) + req_markers = PackagingRequirement("fakepkg; {0}".format(markers)) + r.req.marker = getattr(req_markers, "marker", None) + r.req.specifier = SpecifierSet(_pipfile["version"]) + extras = _pipfile.get("extras") + r.req.extras = sorted(dedup([extra.lower() for extra in extras])) if extras else [] args = { "name": r.name, "vcs": vcs, @@ -793,9 +875,12 @@ class Requirement(object): } if any(key in _pipfile for key in ["hash", "hashes"]): args["hashes"] = _pipfile.get("hashes", [pipfile.get("hash")]) - return cls(**args) + cls_inst = cls(**args) + if cls_inst.is_named: + cls_inst.req.req.line = cls_inst.as_line() + return cls_inst - def as_line(self, sources=None): + def as_line(self, sources=None, include_hashes=True, include_extras=True): """Format this requirement as a line in requirements.txt. If `sources` provided, it should be an sequence of mappings, containing @@ -804,22 +889,52 @@ class Requirement(object): If `sources` is omitted or falsy, no index information will be included in the requirement line. """ - line = "{0}{1}{2}{3}{4}".format( + if self.is_vcs: + include_extras = False + parts = [ self.req.line_part, - self.extras_as_pip if not self.is_vcs else "", + self.extras_as_pip if include_extras else "", self.specifiers if self.specifiers else "", self.markers_as_pip, - self.hashes_as_pip, - ) + ] + if include_hashes: + parts.append(self.hashes_as_pip) if sources and not (self.requirement.local_file or self.vcs): from ..utils import prepare_pip_source_args if self.index: sources = [s for s in sources if s.get("name") == self.index] index_string = " ".join(prepare_pip_source_args(sources)) - line = "{0} {1}".format(line, index_string) + parts.extend([" ", index_string]) + line = "".join(parts) return line + def get_markers(self): + markers = self.markers + if markers: + fake_pkg = PackagingRequirement('fakepkg; {0}'.format(markers)) + markers = fake_pkg.markers + return markers + + def get_specifier(self): + return Specifier(self.specifiers) + + def get_version(self): + return parse_version(self.get_specifier().version) + + def get_requirement(self): + req_line = self.req.req.line + if req_line.startswith('-e '): + _, req_line = req_line.split(" ", 1) + req = init_requirement(self.name) + req.line = req_line + req.specifier = SpecifierSet(self.specifiers if self.specifiers else '') + if self.is_vcs or self.is_file_or_url: + req.url = self.req.link.url_without_fragment + req.marker = self.get_markers() + req.extras = set(self.extras) if self.extras else set() + return req + @property def constraint_line(self): return self.as_line() @@ -839,6 +954,8 @@ class Requirement(object): if k in good_keys } name = self.name + if 'markers' in req_dict and req_dict['markers']: + req_dict['markers'] = req_dict['markers'].replace('"', "'") base_dict = { k: v for k, v in self.req.pipfile_part[name].items() @@ -850,23 +967,103 @@ class Requirement(object): conflicts = [k for k in (conflicting_keys[1:],) if k in base_dict] for k in conflicts: base_dict.pop(k) - if "hashes" in base_dict and len(base_dict["hashes"]) == 1: - base_dict["hash"] = base_dict.pop("hashes")[0] + if "hashes" in base_dict: + _hashes = base_dict.pop("hashes") + hashes = [] + for _hash in _hashes: + try: + hashes.append(_hash.as_line()) + except AttributeError: + hashes.append(_hash) + base_dict["hashes"] = sorted(hashes) if len(base_dict.keys()) == 1 and "version" in base_dict: base_dict = base_dict.get("version") return {name: base_dict} + def as_ireq(self): + ireq_line = self.as_line(include_hashes=False) + if self.editable or self.req.editable: + if ireq_line.startswith("-e "): + ireq_line = ireq_line[len("-e "):] + ireq = InstallRequirement.from_editable(ireq_line) + else: + ireq = InstallRequirement.from_line(ireq_line) + if not getattr(ireq, "req", None): + ireq.req = self.req.req + else: + ireq.req.extras = self.req.req.extras + ireq.req.marker = self.req.req.marker + return ireq + @property def pipfile_entry(self): return self.as_pipfile().copy().popitem() @property def ireq(self): - if not self._ireq: - ireq_line = self.as_line() - if ireq_line.startswith("-e "): - ireq_line = ireq_line[len("-e ") :] - self._ireq = InstallRequirement.from_editable(ireq_line) - else: - self._ireq = InstallRequirement.from_line(ireq_line) - return self._ireq + return self.as_ireq() + + def get_dependencies(self, sources=None): + """Retrieve the dependencies of the current requirement. + + Retrieves dependencies of the current requirement. This only works on pinned + requirements. + + :param sources: Pipfile-formatted sources, defaults to None + :param sources: list[dict], optional + :return: A set of requirement strings of the dependencies of this requirement. + :rtype: set(str) + """ + if not sources: + sources = [{ + 'name': 'pypi', + 'url': 'https://pypi.org/simple', + 'verify_ssl': True, + }] + return get_dependencies(self.as_ireq(), sources=sources) + + def get_abstract_dependencies(self, sources=None): + """Retrieve the abstract dependencies of this requirement. + + Returns the abstract dependencies of the current requirement in order to resolve. + + :param sources: A list of sources (pipfile format), defaults to None + :param sources: list, optional + :return: A list of abstract (unpinned) dependencies + :rtype: list[ :class:`~requirementslib.models.dependency.AbstractDependency` ] + """ + + if not self.abstract_dep: + parent = getattr(self, 'parent', None) + self.abstract_dep = AbstractDependency.from_requirement(self, parent=parent) + if not sources: + sources = [{'url': 'https://pypi.org/simple', 'name': 'pypi', 'verify_ssl': True},] + if is_pinned_requirement(self.ireq): + deps = self.get_dependencies() + else: + ireq = sorted(self.find_all_matches(), key=lambda k: k.version) + deps = get_dependencies(ireq.pop(), sources=sources) + return get_abstract_dependencies(deps, sources=sources, parent=self.abstract_dep) + + def find_all_matches(self, sources=None, finder=None): + """Find all matching candidates for the current requirement. + + Consults a finder to find all matching candidates. + + :param sources: Pipfile-formatted sources, defaults to None + :param sources: list[dict], optional + :return: A list of Installation Candidates + :rtype: list[ :class:`~pip._internal.index.InstallationCandidate` ] + """ + if not finder: + finder = get_finder(sources=sources) + return find_all_matches(finder, self.as_ireq()) + + def merge_markers(self, markers): + if not isinstance(markers, Marker): + markers = Marker(markers) + _markers = set(Marker(self.ireq.markers)) if self.ireq.markers else set(markers) + _markers.add(markers) + new_markers = Marker(" or ".join([str(m) for m in sorted(_markers)])) + self.markers = str(new_markers) + self.req.req.marker = new_markers diff --git a/pipenv/vendor/requirementslib/models/resolvers.py b/pipenv/vendor/requirementslib/models/resolvers.py new file mode 100644 index 00000000..da6d0dda --- /dev/null +++ b/pipenv/vendor/requirementslib/models/resolvers.py @@ -0,0 +1,239 @@ +# -*- coding=utf-8 -*- +from contextlib import contextmanager + +import attr +import six + +from pip_shims.shims import VcsSupport, Wheel + +from ..utils import log +from .cache import HashCache +from .dependencies import AbstractDependency, find_all_matches, get_finder +from .utils import format_requirement, is_pinned_requirement, version_from_ireq + + +class ResolutionError(Exception): + pass + + +@attr.s +class DependencyResolver(object): + pinned_deps = attr.ib(default=attr.Factory(dict)) + #: A dictionary of abstract dependencies by name + dep_dict = attr.ib(default=attr.Factory(dict)) + #: A dictionary of sets of version numbers that are valid for a candidate currently + candidate_dict = attr.ib(default=attr.Factory(dict)) + #: A historical record of pins + pin_history = attr.ib(default=attr.Factory(dict)) + #: Whether to allow prerelease dependencies + allow_prereleases = attr.ib(default=False) + #: Stores hashes for each dependency + hashes = attr.ib(default=attr.Factory(dict)) + #: A hash cache + hash_cache = attr.ib(default=attr.Factory(HashCache)) + #: A finder for searching the index + finder = attr.ib(default=None) + #: Whether to include hashes even from incompatible wheels + include_incompatible_hashes = attr.ib(default=True) + #: A cache for storing available canddiates when using all wheels + _available_candidates_cache = attr.ib(default=attr.Factory(dict)) + + @classmethod + def create(cls, finder=None, allow_prereleases=False, get_all_hashes=True): + if not finder: + finder_args = [] + if allow_prereleases: + finder_args.append('--pre') + finder = get_finder(*finder_args) + creation_kwargs = { + 'allow_prereleases': allow_prereleases, + 'include_incompatible_hashes': get_all_hashes, + 'finder': finder, + 'hash_cache': HashCache(), + } + resolver = cls(**creation_kwargs) + return resolver + + @property + def dependencies(self): + return list(self.dep_dict.values()) + + @property + def resolution(self): + return list(self.pinned_deps.values()) + + def add_abstract_dep(self, dep): + """Add an abstract dependency by either creating a new entry or + merging with an old one. + + :param dep: An abstract dependency to add + :type dep: :class:`~requirementslib.models.dependency.AbstractDependency` + :raises ResolutionError: Raised when the given dependency is not compatible with + an existing abstract dependency. + """ + + if dep.name in self.dep_dict: + compatible_versions = self.dep_dict[dep.name].compatible_versions(dep) + if compatible_versions: + self.candidate_dict[dep.name] = compatible_versions + self.dep_dict[dep.name] = self.dep_dict[ + dep.name + ].compatible_abstract_dep(dep) + else: + raise ResolutionError + else: + self.candidate_dict[dep.name] = dep.version_set + self.dep_dict[dep.name] = dep + + def pin_deps(self): + """Pins the current abstract dependencies and adds them to the history dict. + + Adds any new dependencies to the abstract dependencies already present by + merging them together to form new, compatible abstract dependencies. + """ + + for name in list(self.dep_dict.keys()): + candidates = self.dep_dict[name].candidates[:] + abs_dep = self.dep_dict[name] + while candidates: + pin = candidates.pop() + # Move on from existing pins if the new pin isn't compatible + if name in self.pinned_deps: + if self.pinned_deps[name].editable: + continue + old_version = version_from_ireq(self.pinned_deps[name]) + if not pin.editable: + new_version = version_from_ireq(pin) + if (new_version != old_version and + new_version not in self.candidate_dict[name]): + continue + pin.parent = abs_dep.parent + pin_subdeps = self.dep_dict[name].get_deps(pin) + backup = self.dep_dict.copy(), self.candidate_dict.copy() + try: + for pin_dep in pin_subdeps: + self.add_abstract_dep(pin_dep) + except ResolutionError: + self.dep_dict, self.candidate_dict = backup + continue + else: + self.pinned_deps[name] = pin + break + + def resolve(self, root_nodes, max_rounds=20): + """Resolves dependencies using a backtracking resolver and multiple endpoints. + + Note: this resolver caches aggressively. + Runs for *max_rounds* or until any two pinning rounds yield the same outcome. + + :param root_nodes: A list of the root requirements. + :type root_nodes: list[:class:`~requirementslib.models.requirements.Requirement`] + :param max_rounds: The max number of resolution rounds, defaults to 20 + :param max_rounds: int, optional + :raises RuntimeError: Raised when max rounds is exceeded without a resolution. + """ + if self.dep_dict: + raise RuntimeError("Do not use the same resolver more than once") + + if not self.hash_cache: + self.hash_cache = HashCache() + + # Coerce input into AbstractDependency instances. + # We accept str, Requirement, and AbstractDependency as input. + for dep in root_nodes: + if isinstance(dep, six.string_types): + dep = AbstractDependency.from_string(dep) + elif not isinstance(dep, AbstractDependency): + dep = AbstractDependency.from_requirement(dep) + self.add_abstract_dep(dep) + + for round_ in range(max_rounds): + self.pin_deps() + self.pin_history[round_] = self.pinned_deps.copy() + + if round_ > 0: + previous_round = set(self.pin_history[round_ - 1].values()) + current_values = set(self.pin_history[round_].values()) + difference = current_values - previous_round + else: + difference = set(self.pin_history[round_].values()) + + log.debug("\n") + log.debug("{:=^30}".format(" Round {0} ".format(round_))) + log.debug("\n") + if difference: + log.debug("New Packages: ") + for d in difference: + log.debug("{:>30}".format(format_requirement(d))) + elif round_ >= 3: + log.debug("Stable Pins: ") + for d in current_values: + log.debug("{:>30}".format(format_requirement(d))) + return + else: + log.debug("No New Packages.") + # TODO: Raise a better error. + raise RuntimeError("cannot resolve after {} rounds".format(max_rounds)) + + def get_hashes(self): + for dep in self.pinned_deps.values(): + if dep.name not in self.hashes: + self.hashes[dep.name] = self.get_hashes_for_one(dep) + return self.hashes.copy() + + def get_hashes_for_one(self, ireq): + if not self.finder: + finder_args = [] + if self.allow_prereleases: + finder_args.append('--pre') + self.finder = get_finder(*finder_args) + + if ireq.editable: + return set() + + vcs = VcsSupport() + if ireq.link and ireq.link.scheme in vcs.all_schemes and 'ssh' in ireq.link.scheme: + return set() + + if not is_pinned_requirement(ireq): + raise TypeError( + "Expected pinned requirement, got {}".format(ireq)) + + matching_candidates = set() + with self.allow_all_wheels(): + matching_candidates = ( + find_all_matches(self.finder, ireq, pre=self.allow_prereleases) + ) + + return { + self.hash_cache.get_hash(candidate.location) + for candidate in matching_candidates + } + + @contextmanager + def allow_all_wheels(self): + """ + Monkey patches pip.Wheel to allow wheels from all platforms and Python versions. + + This also saves the candidate cache and set a new one, or else the results from the + previous non-patched calls will interfere. + """ + def _wheel_supported(self, tags=None): + # Ignore current platform. Support everything. + return True + + def _wheel_support_index_min(self, tags=None): + # All wheels are equal priority for sorting. + return 0 + + original_wheel_supported = Wheel.supported + original_support_index_min = Wheel.support_index_min + + Wheel.supported = _wheel_supported + Wheel.support_index_min = _wheel_support_index_min + + try: + yield + finally: + Wheel.supported = original_wheel_supported + Wheel.support_index_min = original_support_index_min diff --git a/pipenv/vendor/requirementslib/models/utils.py b/pipenv/vendor/requirementslib/models/utils.py index 44692399..6fd55b6f 100644 --- a/pipenv/vendor/requirementslib/models/utils.py +++ b/pipenv/vendor/requirementslib/models/utils.py @@ -1,10 +1,26 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import + import os +import sys + +from collections import defaultdict +from itertools import chain, groupby +from operator import attrgetter + import six + from attr import validators from first import first -from .._compat import Link +from packaging.markers import InvalidMarker, Marker, Op, Value, Variable +from packaging.specifiers import InvalidSpecifier, Specifier, SpecifierSet +from packaging.version import parse as parse_version +from packaging.requirements import Requirement as PackagingRequirement +from pkg_resources import Requirement + +from vistir.misc import dedup +from pip_shims.shims import InstallRequirement, Link + from ..utils import SCHEME_LIST, VCS_LIST, is_star @@ -21,6 +37,15 @@ def optional_instance_of(cls): return validators.optional(validators.instance_of(cls)) +def init_requirement(name): + req = Requirement.parse(name) + req.vcs = None + req.local_file = None + req.revision = None + req.path = None + return req + + def extras_to_string(extras): """Turn a list of extras into a string""" if isinstance(extras, six.string_types): @@ -29,16 +54,13 @@ def extras_to_string(extras): else: extras = [extras] - return "[{0}]".format(",".join(extras)) + return "[{0}]".format(",".join(sorted(extras))) def parse_extras(extras_str): """Turn a string of extras into a parsed extras list""" - import requirements - extras = first( - requirements.parse("fakepkg{0}".format(extras_to_string(extras_str))) - ).extras - return extras + extras = Requirement.parse("fakepkg{0}".format(extras_to_string(extras_str))).extras + return sorted(dedup([extra.lower() for extra in extras])) def specs_to_string(specs): @@ -46,7 +68,11 @@ def specs_to_string(specs): if specs: if isinstance(specs, six.string_types): return specs - return ",".join(["".join(spec) for spec in specs]) + try: + extras = ",".join(["".join(spec) for spec in specs]) + except TypeError: + extras = ",".join(["".join(spec._spec) for spec in specs]) + return extras return "" @@ -91,7 +117,7 @@ def strip_ssh_from_git_uri(uri): def add_ssh_scheme_to_git_uri(uri): - """Cleans VCS uris from pip format""" + """Cleans VCS uris from pipenv.patched.notpip format""" if isinstance(uri, six.string_types): # Add scheme for parsing purposes, this is also what pip does if uri.startswith("git+") and "://" not in uri: @@ -101,7 +127,6 @@ def add_ssh_scheme_to_git_uri(uri): def split_markers_from_line(line): """Split markers from a dependency""" - from packaging.markers import Marker, InvalidMarker if not any(line.startswith(uri_prefix) for uri_prefix in SCHEME_LIST): marker_sep = ";" else: @@ -133,7 +158,6 @@ def validate_path(instance, attr_, value): def validate_markers(instance, attr_, value): - from packaging.markers import Marker, InvalidMarker try: Marker("{0}{1}".format(attr_.name, value)) except InvalidMarker: @@ -141,11 +165,311 @@ def validate_markers(instance, attr_, value): def validate_specifiers(instance, attr_, value): - from packaging.specifiers import SpecifierSet, InvalidSpecifier - from packaging.markers import InvalidMarker if value == "": return True try: SpecifierSet(value) except (InvalidMarker, InvalidSpecifier): raise ValueError("Invalid Specifiers {0}".format(value)) + + +def key_from_ireq(ireq): + """Get a standardized key for an InstallRequirement.""" + if ireq.req is None and ireq.link is not None: + return str(ireq.link) + else: + return key_from_req(ireq.req) + + +def key_from_req(req): + """Get an all-lowercase version of the requirement's name.""" + if hasattr(req, 'key'): + # from pkg_resources, such as installed dists for pip-sync + key = req.key + else: + # from packaging, such as install requirements from requirements.txt + key = req.name + + key = key.replace('_', '-').lower() + return key + + +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): + """ + Generic formatter for pretty printing InstallRequirements to the terminal + in a less verbose way than using its `__str__` method. + """ + if ireq.editable: + line = '-e {}'.format(ireq.link) + else: + line = _requirement_to_str_lowercase_name(ireq.req) + + if str(ireq.req.marker) != str(ireq.markers): + if not ireq.req.marker: + line = '{}; {}'.format(line, ireq.markers) + else: + name, markers = line.split(";", 1) + markers = markers.strip() + line = '{}; ({}) and ({})'.format(name, markers, ireq.markers) + + return line + + +def format_specifier(ireq): + """ + Generic formatter for pretty printing the specifier part of + InstallRequirements to the terminal. + """ + # TODO: Ideally, this is carried over to the pip library itself + specs = ireq.specifier._specs if ireq.req is not None else [] + specs = sorted(specs, key=lambda x: x._spec[1]) + return ','.join(str(s) for s in specs) or '' + + +def is_pinned_requirement(ireq): + """ + Returns whether an InstallRequirement is a "pinned" requirement. + + An InstallRequirement is considered pinned if: + + - Is not editable + - It has exactly one specifier + - That specifier is "==" + - The version does not contain a wildcard + + Examples: + django==1.8 # pinned + django>1.8 # NOT pinned + django~=1.8 # NOT pinned + django==1.* # NOT pinned + """ + if ireq.editable: + return False + + specifier = getattr(ireq, "specifier", None) + if not specifier: + return False + if len(specifier._specs) != 1: + return False + + op, version = first(specifier._specs)._spec + return (op == '==' or op == '===') and not version.endswith('.*') + + +def as_tuple(ireq): + """ + Pulls out the (name: str, version:str, extras:(str)) tuple from the pinned InstallRequirement. + """ + if not is_pinned_requirement(ireq): + raise TypeError('Expected a pinned InstallRequirement, got {}'.format(ireq)) + + name = key_from_req(ireq.req) + version = first(ireq.specifier._specs)._spec[1] + extras = tuple(sorted(ireq.extras)) + return name, version, extras + + +def full_groupby(iterable, key=None): + """Like groupby(), but sorts the input on the group key first.""" + return groupby(sorted(iterable, key=key), key=key) + + +def flat_map(fn, collection): + """Map a function over a collection and flatten the result by one-level""" + return chain.from_iterable(map(fn, collection)) + + +def lookup_table(values, key=None, keyval=None, unique=False, use_lists=False): + """ + Builds a dict-based lookup table (index) elegantly. + + Supports building normal and unique lookup tables. For example: + + >>> assert lookup_table( + ... ['foo', 'bar', 'baz', 'qux', 'quux'], lambda s: s[0]) == { + ... 'b': {'bar', 'baz'}, + ... 'f': {'foo'}, + ... 'q': {'quux', 'qux'} + ... } + + For key functions that uniquely identify values, set unique=True: + + >>> assert lookup_table( + ... ['foo', 'bar', 'baz', 'qux', 'quux'], lambda s: s[0], + ... unique=True) == { + ... 'b': 'baz', + ... 'f': 'foo', + ... 'q': 'quux' + ... } + + The values of the resulting lookup table will be values, not sets. + + For extra power, you can even change the values while building up the LUT. + To do so, use the `keyval` function instead of the `key` arg: + + >>> assert lookup_table( + ... ['foo', 'bar', 'baz', 'qux', 'quux'], + ... keyval=lambda s: (s[0], s[1:])) == { + ... 'b': {'ar', 'az'}, + ... 'f': {'oo'}, + ... 'q': {'uux', 'ux'} + ... } + + """ + if keyval is None: + if key is None: + keyval = (lambda v: v) + else: + keyval = (lambda v: (key(v), v)) + + if unique: + return dict(keyval(v) for v in values) + + lut = {} + for value in values: + k, v = keyval(value) + try: + s = lut[k] + except KeyError: + if use_lists: + s = lut[k] = list() + else: + s = lut[k] = set() + if use_lists: + s.append(v) + else: + s.add(v) + return dict(lut) + + +def name_from_req(req): + """Get the name of the requirement""" + if hasattr(req, 'project_name'): + # from pkg_resources, such as installed dists for pip-sync + return req.project_name + else: + # from packaging, such as install requirements from requirements.txt + return req.name + + +def make_install_requirement(name, version, extras, markers, constraint=False): + """make_install_requirement Generates an :class:`~pip._internal.req.req_install.InstallRequirement`. + + Create an InstallRequirement from the supplied metadata. + + :param name: The requirement's name. + :type name: str + :param version: The requirement version (must be pinned). + :type version: str. + :param extras: The desired extras. + :type extras: list[str] + :param markers: The desired markers, without a preceding semicolon. + :type markers: str + :param constraint: Whether to flag the requirement as a constraint, defaults to False. + :param constraint: bool, optional + :return: A generated InstallRequirement + :rtype: :class:`~pip._internal.req.req_install.InstallRequirement` + """ + + # If no extras are specified, the extras string is blank + extras_string = "" + if extras: + # Sort extras for stability + extras_string = "[{}]".format(",".join(sorted(extras))) + + if not markers: + return InstallRequirement.from_line( + str('{}{}=={}'.format(name, extras_string, version)), + constraint=constraint) + else: + return InstallRequirement.from_line( + str('{}{}=={}; {}'.format(name, extras_string, version, str(markers))), + constraint=constraint) + + +def version_from_ireq(ireq): + """version_from_ireq Extract the version from a supplied :class:`~pip._internal.req.req_install.InstallRequirement` + + :param ireq: An InstallRequirement + :type ireq: :class:`~pip._internal.req.req_install.InstallRequirement` + :return: The version of the InstallRequirement. + :rtype: str + """ + + return first(ireq.specifier._specs).version + + +def clean_requires_python(candidates): + """Get a cleaned list of all the candidates with valid specifiers in the `requires_python` attributes.""" + all_candidates = [] + sys_version = '.'.join(map(str, sys.version_info[:3])) + py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', sys_version)) + for c in candidates: + from_location = attrgetter("location.requires_python") + requires_python = getattr(c, "requires_python", from_location(c)) + if requires_python: + # Old specifications had people setting this to single digits + # which is effectively the same as '>=digit,=2.6"')` + marker_key = Variable('python_version') + for spec in specifierset: + operator, val = spec._spec + cleaned_val = Value(val).serialize().replace('"', "") + spec_dict[Op(operator).serialize()].add(cleaned_val) + marker_str = ' and '.join([ + "{0}{1}'{2}'".format(marker_key.serialize(), op, ','.join(vals)) + for op, vals in spec_dict.items() + ]) + marker_to_add = PackagingRequirement('fakepkg; {0}'.format(marker_str)).marker + return marker_to_add diff --git a/pipenv/vendor/requirementslib/models/vcs.py b/pipenv/vendor/requirementslib/models/vcs.py new file mode 100644 index 00000000..a588f629 --- /dev/null +++ b/pipenv/vendor/requirementslib/models/vcs.py @@ -0,0 +1,48 @@ +# -*- coding=utf-8 -*- +import attr +from pip_shims import VcsSupport +import os + + +VCS_SUPPORT = VcsSupport() + + +@attr.s +class VCSRepository(object): + url = attr.ib() + name = attr.ib() + checkout_directory = attr.ib() + vcs_type = attr.ib() + commit_sha = attr.ib(default=None) + ref = attr.ib(default=None) + repo_instance = attr.ib() + + @repo_instance.default + def get_repo_instance(self): + backend = VCS_SUPPORT._registry.get(self.vcs_type) + return backend(url=self.url) + + def obtain(self): + if not os.path.exists(self.checkout_directory): + self.repo_instance.obtain(self.checkout_directory) + if self.ref: + self.checkout_ref(self.ref) + self.commit_sha = self.get_commit_hash(self.ref) + else: + self.ref = self.repo_instance.default_arg_rev + if not self.commit_sha: + self.commit_sha = self.get_commit_hash() + + def checkout_ref(self, ref): + target_rev = self.repo_instance.make_rev_options(ref) + if not self.repo_instance.is_commit_id_equal( + self.checkout_directory, self.get_commit_hash(ref) + ) and not self.repo_instance.is_commit_id_equal(self.checkout_directory, ref): + self.repo_instance.switch(self.checkout_directory, self.url, target_rev) + + def update(self, ref): + target_rev = self.repo_instance.make_rev_options(ref) + self.repo_instance.update(self.checkout_directory, target_rev) + + def get_commit_hash(self, ref=None): + return self.repo_instance.get_revision(self.checkout_directory) diff --git a/pipenv/vendor/requirementslib/utils.py b/pipenv/vendor/requirementslib/utils.py index 685a90b3..c41f9675 100644 --- a/pipenv/vendor/requirementslib/utils.py +++ b/pipenv/vendor/requirementslib/utils.py @@ -1,25 +1,28 @@ # -*- coding=utf-8 -*- from __future__ import absolute_import + import logging import os -import posixpath + import six -from itertools import product +from six.moves.urllib.parse import urlparse, urlsplit -try: - from urllib.parse import urlparse -except ImportError: - from urlparse import urlparse +from pip_shims import ( + Command, VcsSupport, cmdoptions, is_archive_file, is_installable_dir +) +from vistir.compat import Path +from vistir.path import is_valid_url, ensure_mkdir_p -try: - from pathlib import Path -except ImportError: - from pathlib2 import Path +VCS_ACCESS = VcsSupport() VCS_LIST = ("git", "svn", "hg", "bzr") +VCS_SCHEMES = [] SCHEME_LIST = ("http://", "https://", "ftp://", "ftps://", "file://") +if not VCS_SCHEMES: + VCS_SCHEMES = VcsSupport().all_schemes + def setup_logger(): logger = logging.getLogger("requirementslib") @@ -40,74 +43,14 @@ def is_vcs(pipfile_entry): return any(key for key in pipfile_entry.keys() if key in VCS_LIST) elif isinstance(pipfile_entry, six.string_types): - vcs_starts = product( - ("git+", "hg+", "svn+", "bzr+"), - ("file", "ssh", "https", "http", "svn", "sftp", ""), - ) - - return next( - ( - v - for v in ( - pipfile_entry.startswith("{0}{1}".format(vcs, scheme)) - for vcs, scheme in vcs_starts - ) - if v - ), - False, - ) - + if not is_valid_url(pipfile_entry) and pipfile_entry.startswith("git+"): + from .models.utils import add_ssh_scheme_to_git_uri + pipfile_entry = add_ssh_scheme_to_git_uri(pipfile_entry) + parsed_entry = urlsplit(pipfile_entry) + return parsed_entry.scheme in VCS_SCHEMES return False -def check_for_unc_path(path): - """ Checks to see if a pathlib `Path` object is a unc path or not""" - if ( - os.name == "nt" - and len(path.drive) > 2 - and not path.drive[0].isalpha() - and path.drive[1] != ":" - ): - return True - else: - return False - - -def get_converted_relative_path(path, relative_to=os.curdir): - """Convert `path` to be relative. - - Given a vague relative path, return the path relative to the given - location. - - This performs additional conversion to ensure the result is of POSIX form, - and starts with `./`, or is precisely `.`. - """ - - start_path = Path(relative_to) - try: - start = start_path.resolve() - except OSError: - start = start_path.absolute() - - # check if there is a drive letter or mount point - # if it is a mountpoint use the original absolute path - # instead of the unc path - if check_for_unc_path(start): - start = start_path.absolute() - - path = start.joinpath(path).relative_to(start) - - # check and see if the path that was passed into the function is a UNC path - # and raise value error if it is not. - if check_for_unc_path(path): - raise ValueError("The path argument does not currently accept UNC paths") - - relpath_s = posixpath.normpath(path.as_posix()) - if not (relpath_s == "." or relpath_s.startswith("./")): - relpath_s = posixpath.join(".", relpath_s) - return relpath_s - - def multi_split(s, split): """Splits on multiple given separators.""" for r in split: @@ -121,7 +64,6 @@ def is_star(val): def is_installable_file(path): """Determine if a path can potentially be installed""" - from ._compat import is_installable_dir, is_archive_file from packaging import specifiers if hasattr(path, "keys") and any( @@ -160,22 +102,6 @@ def is_installable_file(path): return False -def is_valid_url(url): - """Checks if a given string is an url""" - pieces = urlparse(url) - return all([pieces.scheme, any([pieces.netloc, pieces.path])]) - - -def pep423_name(name): - """Normalize package name to PEP 423 style standard.""" - name = name.lower() - if any(i not in name for i in (VCS_LIST + SCHEME_LIST)): - return name.replace("_", "-") - - else: - return name - - def prepare_pip_source_args(sources, pip_args=None): if pip_args is None: pip_args = [] @@ -197,3 +123,30 @@ def prepare_pip_source_args(sources, pip_args=None): ["--trusted-host", urlparse(source["url"]).hostname] ) return pip_args + + +class PipCommand(Command): + name = 'PipCommand' + + +def get_pip_command(): + # Use pip's parser for pip.conf management and defaults. + # General options (find_links, index_url, extra_index_url, trusted_host, + # and pre) are defered to pip. + import optparse + pip_command = PipCommand() + pip_command.parser.add_option(cmdoptions.no_binary()) + pip_command.parser.add_option(cmdoptions.only_binary()) + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + pip_command.parser, + ) + pip_command.parser.insert_option_group(0, index_opts) + pip_command.parser.add_option(optparse.Option('--pre', action='store_true', default=False)) + + return pip_command + + +@ensure_mkdir_p(mode=0o777) +def _ensure_dir(path): + return path