def check(requirements_paths=[], metadata=[], projects=[]): """Return True if all of the specified dependencies have been ported to Python 3. The requirements_paths argument takes a sequence of file paths to requirements files. The 'metadata' argument takes a sequence of strings representing metadata. The 'projects' argument takes a sequence of project names. Any project that is not listed on PyPI will be considered ported. """ dependencies = main.projects_from_requirements(requirements_paths) dependencies.extend(main.projects_from_metadata(metadata)) dependencies.extend(projects) dependencies = set(name.lower() for name in dependencies) pypy_projects = pypi.all_pypy_projects() all_projects = pypi.all_projects() for dependency in dependencies: if dependency in all_projects and dependency not in pypy_projects: if not pypi.is_pure_python(dependency): return False return True
def blocking_dependencies(projects, pypy_projects): """Starting from 'projects', find all projects which are blocking PyPy usage. Any project in 'pypy_projects' is considered ported and thus will not have its dependencies searched. Version requirements are also ignored as it is assumed that if a project is updating to support PyPy then they will be willing to update to the latest version of their dependencies. The only dependencies checked are those required to run the project. """ log = logging.getLogger('ciu') check = [] evaluated = set() for project in projects: log.info('Checking top-level project: {0} ...'.format(project)) try: dist = distlib.locators.locate(project) except AttributeError: # This is a work around. //bitbucket.org/pypa/distlib/issue/59/ . log.warning('{0} found but had to be skipped.'.format(project)) continue if not dist: log.warning('{0} not found'.format(project)) continue project = dist.name.lower() # PyPI can be forgiving about name formats. if project not in pypy_projects: check.append(project) reasons = LowerDict((project, None) for project in check if not pypi.is_pure_python(project)) thread_pool_executor = concurrent.futures.ThreadPoolExecutor( max_workers=ciu.CPU_COUNT) with thread_pool_executor as executor: while len(check) > 0: new_check = [] for parent, deps in zip(check, executor.map(dependencies, check)): if deps is None: # Can't find any results for a project, so ignore it so as # to not accidentally consider indefinitely that a project # can't port. del reasons[parent] continue log.info('Dependencies of {0}: {1}'.format(project, deps)) for dep in deps: log.info('Checking dependency: {0} ...'.format(dep)) if dep in evaluated: log.info('{0} already checked'.format(dep)) continue else: evaluated.add(dep) if dep in pypy_projects: continue if not pypi.is_pure_python(dep): reasons[dep] = parent new_check.append(dep) check = new_check return reasons_to_paths(reasons)
def test_pure_python_with_no_wheels(self): self.assertFalse(pypi.is_pure_python('ipaddr'))
def test_is_not_pure_python(self): self.assertFalse(pypi.is_pure_python('cryptography'))
def test_is_pure_python(self): self.assertTrue(pypi.is_pure_python('urllib3'))