def test_invalid_specs(): invalids = ['=', 'foo 1.0', '>'] for invalid in invalids: assert conda_api.parse_spec(invalid) is None with pytest.raises(TypeError) as excinfo: conda_api.parse_spec(42) assert 'Expected a string' in str(excinfo.value)
def _conda_combine_key(spec): parsed = conda_api.parse_spec(spec) if parsed is None: # this is broken but we complain about it in project.py, carry on here return spec else: return parsed.name
def _find_conda_deviations(self, prefix, env_spec): try: installed = conda_api.installed(prefix) except conda_api.CondaError as e: raise CondaManagerError("Conda failed while listing installed packages in %s: %s" % (prefix, str(e))) missing = set() wrong_version = set() for spec_string in env_spec.conda_packages_for_create: spec = conda_api.parse_spec(spec_string) name = spec.name if name not in installed: missing.add(name) else: def version_match(wanted, installed): if wanted == installed: return True else: return installed.startswith(wanted + ".") # The only constraint we are smart enough to understand is # the one we put in the lock file, which is plain =. # We can't do version comparisons, which is a bug. # We won't notice if non-= constraints are unmet. (_, installed_version, installed_build) = installed[name] if spec.exact_version is not None and not version_match(spec.exact_version, installed_version): wrong_version.add(name) elif spec.exact_build_string is not None and not version_match(spec.exact_build_string, installed_build): wrong_version.add(name) return (sorted(list(missing)), sorted(list(wrong_version)))
def test_resolve_dependencies_with_actual_conda(): manager = DefaultCondaManager(frontend=NullFrontend()) lock_set = manager.resolve_dependencies(['bokeh'], channels=(), platforms=(conda_api.current_platform(), )) specs = lock_set.package_specs_for_current_platform pprint(specs) names = [conda_api.parse_spec(spec).name for spec in specs] assert 'bokeh' in names assert len(specs) > 5 # 5 is an arbitrary number of deps that surely bokeh has
def __init__(self, name, conda_packages, channels, pip_packages=(), description=None, inherit_from_names=(), inherit_from=()): """Construct a package set with the given name and packages. Args: name (str): name of the package set conda_packages (list): list of package specs to pass to conda install channels (list): list of channel names pip_packages (list): list of pip package specs to pass to pip description (str or None): one-sentence-ish summary of what this env is inherit_from_name (str or None): name of what we inherit from inherit_from (EnvSpec or None): pull in packages and channels from """ assert inherit_from_names is not None assert inherit_from is not None self._name = name self._conda_packages = tuple(conda_packages) self._channels = tuple(channels) self._pip_packages = tuple(pip_packages) self._description = description self._channels_and_packages_hash = None self._inherit_from_names = inherit_from_names self._inherit_from = inherit_from # inherit_from must be a subset of inherit_from_names # except that we can have an anonymous base env spec for # the global packages/channels sections; if there was an # error that kept us from creating one of the specs we # name as a parent, then self._inherit_from would be a # subset rather than equal. for name in tuple([spec.name for spec in self._inherit_from]): assert name is None or name in self._inherit_from_names conda_specs_by_name = dict() for spec in self.conda_packages: # we quietly skip invalid specs here and let them fail # somewhere we can more easily report an error message. parsed = conda_api.parse_spec(spec) if parsed is not None: conda_specs_by_name[parsed.name] = spec self._conda_specs_by_name = conda_specs_by_name pip_specs_by_name = dict() for spec in self.pip_packages: # we quietly skip invalid specs here and let them fail # somewhere we can more easily report an error message. parsed = pip_api.parse_spec(spec) if parsed is not None: pip_specs_by_name[parsed.name] = spec self._pip_specs_by_name = pip_specs_by_name
def replace_spec(old): name = parse_spec(old).name for (replaced_name, new_spec) in updated_specs: if replaced_name == name: return new_spec return old
def _update_env_spec(project, name, packages, channels, create): failed = project.problems_status() if failed is not None: return failed if packages is None: packages = [] if channels is None: channels = [] if not create and (name is not None): if name not in project.env_specs: problem = "Environment spec {} doesn't exist.".format(name) return SimpleStatus(success=False, description=problem) if name is None: env_dict = project.project_file.root else: env_dict = project.project_file.get_value(['env_specs', name]) if env_dict is None: env_dict = dict() project.project_file.set_value(['env_specs', name], env_dict) # packages may be a "CommentedSeq" and we don't want to lose the comments, # so don't convert this thing to a regular list. old_packages = env_dict.get('packages', []) old_packages_set = set(parse_spec(dep).name for dep in old_packages) bad_specs = [] updated_specs = [] new_specs = [] for dep in packages: if dep in old_packages: # no-op adding the EXACT same thing (don't move it around) continue parsed = parse_spec(dep) if parsed is None: bad_specs.append(dep) else: if parsed.name in old_packages_set: updated_specs.append((parsed.name, dep)) else: new_specs.append(dep) if len(bad_specs) > 0: bad_specs_string = ", ".join(bad_specs) return SimpleStatus(success=False, description="Could not add packages.", errors=[("Bad package specifications: %s." % bad_specs_string)]) # remove everything that we are changing the spec for def replace_spec(old): name = parse_spec(old).name for (replaced_name, new_spec) in updated_specs: if replaced_name == name: return new_spec return old _map_inplace(replace_spec, old_packages) # add all the new ones for added in new_specs: old_packages.append(added) env_dict['packages'] = old_packages # channels may be a "CommentedSeq" and we don't want to lose the comments, # so don't convert this thing to a regular list. new_channels = env_dict.get('channels', []) old_channels_set = set(new_channels) for channel in channels: if channel not in old_channels_set: new_channels.append(channel) env_dict['channels'] = new_channels status = _commit_requirement_if_it_works(project, CondaEnvRequirement, env_spec_name=name) return status
def __init__(self, name, conda_packages, channels, pip_packages=(), description=None, inherit_from_names=(), inherit_from=(), platforms=(), lock_set=None): """Construct a package set with the given name and packages. Args: name (str): name of the package set conda_packages (list): list of package specs to pass to conda install channels (list): list of channel names pip_packages (list): list of pip package specs to pass to pip description (str or None): one-sentence-ish summary of what this env is inherit_from_name (str or None): name of what we inherit from inherit_from (EnvSpec or None): pull in packages and channels from lock_set (CondaLockSet): locked packages or None """ assert inherit_from_names is not None assert inherit_from is not None self._name = name self._path = None self._readonly = None self._conda_packages = tuple(conda_packages) self._channels = tuple(channels) self._pip_packages = tuple(pip_packages) self._description = description self._logical_hash = None self._locked_hash = None self._import_hash = None self._inherit_from_names = inherit_from_names self._inherit_from = inherit_from self._lock_set = lock_set self._platforms = tuple(conda_api.sort_platform_list(platforms)) # inherit_from must be a subset of inherit_from_names # except that we can have an anonymous base env spec for # the global packages/channels sections; if there was an # error that kept us from creating one of the specs we # name as a parent, then self._inherit_from would be a # subset rather than equal. for name in tuple([spec.name for spec in self._inherit_from]): assert name is None or name in self._inherit_from_names conda_specs_by_name = dict() for spec in self.conda_packages_for_create: # we quietly skip invalid specs here and let them fail # somewhere we can more easily report an error message. parsed = conda_api.parse_spec(spec) if parsed is not None: conda_specs_by_name[parsed.name] = spec self._conda_specs_for_create_by_name = conda_specs_by_name name_set = set() conda_constrained_packages = [] for spec in self.conda_packages: parsed = conda_api.parse_spec(spec) if parsed is not None: name_set.add(parsed.name) if parsed.conda_constraint is not None or parsed.pip_constraint is not None: conda_constrained_packages.append(spec) self._conda_logical_specs_name_set = name_set self.conda_constrained_packages = sorted(conda_constrained_packages) pip_specs_by_name = dict() for spec in self.pip_packages_for_create: # we quietly skip invalid specs here and let them fail # somewhere we can more easily report an error message. parsed = pip_api.parse_spec(spec) if parsed is not None: pip_specs_by_name[parsed.name] = spec self._pip_specs_for_create_by_name = pip_specs_by_name name_set = set() for spec in self.pip_packages: parsed = pip_api.parse_spec(spec) if parsed is not None: name_set.add(parsed.name) self._pip_logical_specs_name_set = name_set self._conda = conda_manager.new_conda_manager()
def test_invalid_specs(): invalids = ['=', 'foo 1.0', '>'] for invalid in invalids: assert conda_api.parse_spec(invalid) is None