def trace_requirements(requirements): """given an iterable of pip InstallRequirements, return the set of required packages, given their transitive requirements. """ requirements = tuple(pretty_req(r) for r in requirements) working_set = fresh_working_set() # breadth-first traversal: from collections import deque queue = deque(requirements) seen = set() errors = [] result = [] while queue: req = queue.popleft() req_id = str( req ) # InstallRequirement doesn't implement hash/equality, so we compare using str() if req_id in seen: logger.debug('already analyzed: %s', req) continue else: seen.add(req_id) logger.debug('tracing: %s', req) if req_cycle(req): logger.warn('Circular dependency! %s', req) continue try: dist = working_set.find(req.req) except pkg_resources.VersionConflict as conflict: dist = conflict.args[0] errors.append('Error: version conflict: %s (%s) <-> %s' % (dist, timid_relpath(dist.location), req)) if dist is None: errors.append('Error: unmet dependency: %s' % req) continue result.append(dist_to_req(dist)) for dist_req in sorted(dist.requires(), key=lambda req: req.key): from pip.req import InstallRequirement dist_req = InstallRequirement(dist_req, req) logger.debug('adding sub-requirement %s', dist_req) queue.append(dist_req) if errors: raise InstallationError('\n'.join(errors)) return result
def trace_requirements(requirements): """given an iterable of pip InstallRequirements, return the set of required packages, given their transitive requirements. """ requirements = tuple(pretty_req(r) for r in requirements) working_set = fresh_working_set() # breadth-first traversal: from collections import deque queue = deque(requirements) seen = set() errors = [] result = [] while queue: req = queue.popleft() req_id = str(req) # InstallRequirement doesn't implement hash/equality, so we compare using str() if req_id in seen: logger.debug('already analyzed: %s', req) continue else: seen.add(req_id) logger.debug('tracing: %s', req) if req_cycle(req): logger.warn('Circular dependency! %s', req) continue try: dist = working_set.find(req.req) except pkg_resources.VersionConflict as conflict: dist = conflict.args[0] errors.append('Error: version conflict: %s (%s) <-> %s' % ( dist, timid_relpath(dist.location), req )) if dist is None: errors.append('Error: unmet dependency: %s' % req) continue result.append(dist_to_req(dist)) for dist_req in sorted(dist.requires(), key=lambda req: req.key): from pip.req import InstallRequirement dist_req = InstallRequirement(dist_req, req) logger.debug('adding sub-requirement %s', dist_req) queue.append(dist_req) if errors: raise InstallationError('\n'.join(errors)) return result
def trace_requirements(requirements): """given an iterable of pip InstallRequirements, return the set of required packages, given their transitive requirements. """ requirements = tuple(pretty_req(r) for r in requirements) working_set = fresh_working_set() # breadth-first traversal: from collections import deque queue = deque(requirements) queued = {_package_req_to_pkg_resources_req(req.req) for req in queue} errors = [] result = [] while queue: req = queue.popleft() logger.debug('tracing: %s', req) try: dist = working_set.find_normalized( _package_req_to_pkg_resources_req(req.req)) except pkg_resources.VersionConflict as conflict: dist = conflict.args[0] errors.append('Error: version conflict: {} ({}) <-> {}'.format( dist, timid_relpath(dist.location), req)) assert dist is not None, 'Should be unreachable in pip8+' result.append(dist_to_req(dist)) # TODO: pip does no validation of extras. should we? extras = [extra for extra in req.extras if extra in dist.extras] for sub_req in sorted(dist.requires(extras=extras), key=lambda req: req.key): sub_req = InstallRequirement(sub_req, req) if req_cycle(sub_req): logger.warning('Circular dependency! %s', sub_req) continue elif sub_req.req in queued: logger.debug('already queued: %s', sub_req) continue else: logger.debug('adding sub-requirement %s', sub_req) queue.append(sub_req) queued.add(sub_req.req) if errors: raise InstallationError('\n'.join(errors)) return result
def trace_requirements(requirements): """given an iterable of pip InstallRequirements, return the set of required packages, given their transitive requirements. """ requirements = tuple(pretty_req(r) for r in requirements) working_set = fresh_working_set() # breadth-first traversal: from collections import deque queue = deque(requirements) queued = {_package_req_to_pkg_resources_req(req.req) for req in queue} errors = [] result = [] while queue: req = queue.popleft() logger.debug('tracing: %s', req) try: dist = working_set.find_normalized(_package_req_to_pkg_resources_req(req.req)) except pkg_resources.VersionConflict as conflict: dist = conflict.args[0] errors.append('Error: version conflict: {} ({}) <-> {}'.format( dist, timid_relpath(dist.location), req )) assert dist is not None, 'Should be unreachable in pip8+' result.append(dist_to_req(dist)) # TODO: pip does no validation of extras. should we? extras = [extra for extra in req.extras if extra in dist.extras] for sub_req in sorted(dist.requires(extras=extras), key=lambda req: req.key): sub_req = InstallRequirement(sub_req, req) if req_cycle(sub_req): logger.warning('Circular dependency! %s', sub_req) continue elif sub_req.req in queued: logger.debug('already queued: %s', sub_req) continue else: logger.debug('adding sub-requirement %s', sub_req) queue.append(sub_req) queued.add(sub_req.req) if errors: raise InstallationError('\n'.join(errors)) return result