def resolve_reqs(self, download_dir, ireq, wheel_cache, setup_requires={}, dist=None): results = None setup_requires = {} dist = None ireq.isolated = False ireq._wheel_cache = wheel_cache try: from pipenv.patched.notpip._internal.operations.prepare import RequirementPreparer except ImportError: # Pip 9 and below reqset = RequirementSet( self.build_dir, self.source_dir, download_dir=download_dir, wheel_download_dir=self._wheel_download_dir, session=self.session, ignore_installed=True, ignore_compatibility=False, wheel_cache=wheel_cache) results = reqset._prepare_file(self.finder, ireq, ignore_requires_python=True) else: # pip >= 10 preparer_kwargs = { 'build_dir': self.build_dir, 'src_dir': self.source_dir, 'download_dir': download_dir, 'wheel_download_dir': self._wheel_download_dir, 'progress_bar': 'off', 'build_isolation': False } resolver_kwargs = { 'finder': self.finder, 'session': self.session, 'upgrade_strategy': "to-satisfy-only", 'force_reinstall': True, 'ignore_dependencies': False, 'ignore_requires_python': True, 'ignore_installed': True, 'isolated': False, 'wheel_cache': wheel_cache, 'use_user_site': False, 'ignore_compatibility': False } resolver = None preparer = None with RequirementTracker() as req_tracker: # Pip 18 uses a requirement tracker to prevent fork bombs if req_tracker: preparer_kwargs['req_tracker'] = req_tracker preparer = RequirementPreparer(**preparer_kwargs) resolver_kwargs['preparer'] = preparer reqset = RequirementSet() ireq.is_direct = True # reqset.add_requirement(ireq) resolver = PipResolver(**resolver_kwargs) resolver.require_hashes = False results = resolver._resolve_one(reqset, ireq) cleanup_fn = getattr(reqset, "cleanup_files", None) if cleanup_fn is not None: try: cleanup_fn() except OSError: pass if ireq.editable and (not ireq.source_dir or not os.path.exists(ireq.source_dir)): if ireq.editable: self._source_dir = TemporaryDirectory(fs_str("source")) ireq.ensure_has_source_dir(self.source_dir) if ireq.editable and (ireq.source_dir and os.path.exists(ireq.source_dir)): # Collect setup_requires info from local eggs. # Do this after we call the preparer on these reqs to make sure their # egg info has been created from pipenv.utils import chdir with chdir(ireq.setup_py_dir): try: from setuptools.dist import distutils dist = distutils.core.run_setup(ireq.setup_py) except InstallationError: ireq.run_egg_info() except (TypeError, ValueError, AttributeError): pass if not dist: try: dist = ireq.get_dist() except (ImportError, ValueError, TypeError, AttributeError): pass if ireq.editable and dist: setup_requires = getattr(dist, "extras_require", None) if not setup_requires: setup_requires = { "setup_requires": getattr(dist, "setup_requires", None) } if not getattr(ireq, 'req', None): try: ireq.req = dist.as_requirement() if dist else None except (ValueError, TypeError) as e: pass # Convert setup_requires dict into a somewhat usable form. if setup_requires: for section in setup_requires: python_version = section not_python = not (section.startswith('[') and ':' in section) # This is for cleaning up :extras: formatted markers # by adding them to the results of the resolver # since any such extra would have been returned as a result anyway for value in setup_requires[section]: # This is a marker. if value.startswith('[') and ':' in value: python_version = value[1:-1] not_python = False # Strip out other extras. if value.startswith('[') and ':' not in value: not_python = True if ':' not in value: try: if not not_python: results.add( InstallRequirement.from_line( "{0}{1}".format( value, python_version).replace( ':', ';'))) # Anything could go wrong here -- can't be too careful. except Exception: pass # this section properly creates 'python_version' markers for cross-python # virtualenv creation and for multi-python compatibility. requires_python = reqset.requires_python if hasattr( reqset, 'requires_python') else resolver.requires_python if requires_python: marker_str = '' # This corrects a logic error from the previous code which said that if # we Encountered any 'requires_python' attributes, basically only create a # single result no matter how many we resolved. This should fix # a majority of the remaining non-deterministic resolution issues. if any( requires_python.startswith(op) for op in Specifier._operators.keys()): # We are checking first if we have leading specifier operator # if not, we can assume we should be doing a == comparison specifierset = SpecifierSet(requires_python) # for multiple specifiers, the correct way to represent that in # a specifierset is `Requirement('fakepkg; python_version<"3.0,>=2.6"')` from passa.internals.specifiers import cleanup_pyspecs marker_str = str( Marker(" and ".join( dedup([ "python_version {0[0]} '{0[1]}'".format(spec) for spec in cleanup_pyspecs(specifierset) ])))) # The best way to add markers to a requirement is to make a separate requirement # with only markers on it, and then to transfer the object istelf marker_to_add = Requirement( 'fakepkg; {0}'.format(marker_str)).marker if ireq in results: results.remove(ireq) print(marker_to_add) ireq.req.marker = marker_to_add results = set(results) if results else set() return results, ireq
def get_legacy_dependencies(self, ireq): """ Given a pinned or an editable InstallRequirement, returns a set of dependencies (also InstallRequirements, but not necessarily pinned). They indicate the secondary dependencies for the given requirement. """ if not (ireq.editable or is_pinned_requirement(ireq)): raise TypeError( 'Expected pinned or editable InstallRequirement, got {}'. format(ireq)) if ireq not in self._dependencies_cache: if ireq.editable and (ireq.source_dir and os.path.exists(ireq.source_dir)): # No download_dir for locally available editable requirements. # If a download_dir is passed, pip will unnecessarely # archive the entire source directory download_dir = None elif ireq.link and not ireq.link.is_artifact: # No download_dir for VCS sources. This also works around pip # using git-checkout-index, which gets rid of the .git dir. download_dir = None else: download_dir = self._download_dir if not os.path.isdir(download_dir): os.makedirs(download_dir) if not os.path.isdir(self._wheel_download_dir): os.makedirs(self._wheel_download_dir) # Collect setup_requires info from local eggs. # Do this after we call the preparer on these reqs to make sure their # egg info has been created setup_requires = {} dist = None if ireq.editable: try: dist = ireq.get_dist() except InstallationError: ireq.run_egg_info() dist = ireq.get_dist() except (TypeError, ValueError, AttributeError): pass else: if dist.has_metadata('requires.txt'): setup_requires = self.finder.get_extras_links( dist.get_metadata_lines('requires.txt')) try: # Pip 9 and below reqset = RequirementSet( self.build_dir, self.source_dir, download_dir=download_dir, wheel_download_dir=self._wheel_download_dir, session=self.session, ignore_installed=True, ignore_compatibility=False, wheel_cache=self.wheel_cache, ) result = reqset._prepare_file(self.finder, ireq, ignore_requires_python=True) except TypeError: # Pip >= 10 (new resolver!) preparer = RequirementPreparer( build_dir=self.build_dir, src_dir=self.source_dir, download_dir=download_dir, wheel_download_dir=self._wheel_download_dir, progress_bar='off', build_isolation=False) reqset = RequirementSet() ireq.is_direct = True reqset.add_requirement(ireq) self.resolver = PipResolver(preparer=preparer, finder=self.finder, session=self.session, upgrade_strategy="to-satisfy-only", force_reinstall=True, ignore_dependencies=False, ignore_requires_python=True, ignore_installed=True, isolated=False, wheel_cache=self.wheel_cache, use_user_site=False, ignore_compatibility=False) self.resolver.resolve(reqset) result = set(reqset.requirements.values()) # HACK: Sometimes the InstallRequirement doesn't properly get # these values set on it during the resolution process. It's # difficult to pin down what is going wrong. This fixes things. if not getattr(ireq, 'version', None): try: dist = ireq.get_dist() if not dist else None ireq.version = ireq.get_dist().version except (ValueError, OSError, TypeError, AttributeError) as e: pass if not getattr(ireq, 'project_name', None): try: ireq.project_name = dist.project_name if dist else None except (ValueError, TypeError) as e: pass if not getattr(ireq, 'req', None): try: ireq.req = dist.as_requirement() if dist else None except (ValueError, TypeError) as e: pass # Convert setup_requires dict into a somewhat usable form. if setup_requires: for section in setup_requires: python_version = section not_python = not (section.startswith('[') and ':' in section) # This is for cleaning up :extras: formatted markers # by adding them to the results of the resolver # since any such extra would have been returned as a result anyway for value in setup_requires[section]: # This is a marker. if value.startswith('[') and ':' in value: python_version = value[1:-1] not_python = False # Strip out other extras. if value.startswith('[') and ':' not in value: not_python = True if ':' not in value: try: if not not_python: result = result + [ InstallRequirement.from_line( "{0}{1}".format( value, python_version).replace( ':', ';')) ] # Anything could go wrong here -- can't be too careful. except Exception: pass # this section properly creates 'python_version' markers for cross-python # virtualenv creation and for multi-python compatibility. requires_python = reqset.requires_python if hasattr( reqset, 'requires_python') else self.resolver.requires_python if requires_python: marker_str = '' # This corrects a logic error from the previous code which said that if # we Encountered any 'requires_python' attributes, basically only create a # single result no matter how many we resolved. This should fix # a majority of the remaining non-deterministic resolution issues. if any( requires_python.startswith(op) for op in Specifier._operators.keys()): # We are checking first if we have leading specifier operator # if not, we can assume we should be doing a == comparison specifierset = list(SpecifierSet(requires_python)) # for multiple specifiers, the correct way to represent that in # a specifierset is `Requirement('fakepkg; python_version<"3.0,>=2.6"')` marker_key = Variable('python_version') markers = [] for spec in specifierset: operator, val = spec._spec operator = Op(operator) val = Value(val) markers.append(''.join([ marker_key.serialize(), operator.serialize(), val.serialize() ])) marker_str = ' and '.join(markers) # The best way to add markers to a requirement is to make a separate requirement # with only markers on it, and then to transfer the object istelf marker_to_add = Requirement( 'fakepkg; {0}'.format(marker_str)).marker result.remove(ireq) ireq.req.marker = marker_to_add result.add(ireq) self._dependencies_cache[ireq] = result reqset.cleanup_files() return set(self._dependencies_cache[ireq])
def get_legacy_dependencies(self, ireq): """ Given a pinned or an editable InstallRequirement, returns a set of dependencies (also InstallRequirements, but not necessarily pinned). They indicate the secondary dependencies for the given requirement. """ if not (ireq.editable or is_pinned_requirement(ireq)): raise TypeError( 'Expected pinned or editable InstallRequirement, got {}'. format(ireq)) if ireq not in self._dependencies_cache: if ireq.editable and (ireq.source_dir and os.path.exists(ireq.source_dir)): # No download_dir for locally available editable requirements. # If a download_dir is passed, pip will unnecessarely # archive the entire source directory download_dir = None elif ireq.link and not ireq.link.is_artifact: # No download_dir for VCS sources. This also works around pip # using git-checkout-index, which gets rid of the .git dir. download_dir = None else: download_dir = self._download_dir if not os.path.isdir(download_dir): os.makedirs(download_dir) if not os.path.isdir(self._wheel_download_dir): os.makedirs(self._wheel_download_dir) try: # Pip < 9 and below reqset = RequirementSet( self.build_dir, self.source_dir, download_dir=download_dir, wheel_download_dir=self._wheel_download_dir, session=self.session, ignore_installed=True, ignore_compatibility=False, wheel_cache=self.wheel_cache, ) result = reqset._prepare_file(self.finder, ireq, ignore_requires_python=True) except TypeError: # Pip >= 10 (new resolver!) preparer = RequirementPreparer( build_dir=self.build_dir, src_dir=self.source_dir, download_dir=download_dir, wheel_download_dir=self._wheel_download_dir, progress_bar='off', build_isolation=False) reqset = RequirementSet() ireq.is_direct = True reqset.add_requirement(ireq) self.resolver = PipResolver(preparer=preparer, finder=self.finder, session=self.session, upgrade_strategy="to-satisfy-only", force_reinstall=False, ignore_dependencies=False, ignore_requires_python=True, ignore_installed=True, isolated=False, wheel_cache=self.wheel_cache, use_user_site=False, ignore_compatibility=False) self.resolver.resolve(reqset) result = reqset.requirements.values() # Collect setup_requires info from local eggs. # Do this after we call the preparer on these reqs to make sure their # egg info has been created setup_requires = {} if ireq.editable: try: dist = ireq.get_dist() if dist.has_metadata('requires.txt'): setup_requires = self.finder.get_extras_links( dist.get_metadata_lines('requires.txt')) # HACK: Sometimes the InstallRequirement doesn't properly get # these values set on it during the resolution process. It's # difficult to pin down what is going wrong. This fixes things. ireq.version = dist.version ireq.project_name = dist.project_name ireq.req = dist.as_requirement() except (TypeError, ValueError): pass # Convert setup_requires dict into a somewhat usable form. if setup_requires: for section in setup_requires: python_version = section not_python = not (section.startswith('[') and ':' in section) for value in setup_requires[section]: # This is a marker. if value.startswith('[') and ':' in value: python_version = value[1:-1] not_python = False # Strip out other extras. if value.startswith('[') and ':' not in value: not_python = True if ':' not in value: try: if not not_python: result = result + [ InstallRequirement.from_line( "{0}{1}".format( value, python_version).replace( ':', ';')) ] # Anything could go wrong here — can't be too careful. except Exception: pass requires_python = reqset.requires_python if hasattr( reqset, 'requires_python') else self.resolver.requires_python if requires_python: marker = 'python_version=="{0}"'.format( requires_python.replace(' ', '')) new_req = InstallRequirement.from_line('{0}; {1}'.format( str(ireq.req), marker)) result = [new_req] self._dependencies_cache[ireq] = result reqset.cleanup_files() return set(self._dependencies_cache[ireq])