Ejemplo n.º 1
0
def _upload(project,
            archive_filename,
            uploaded_basename,
            site=None,
            username=None,
            token=None,
            log_level=None):
    assert not project.problems

    client = _Client(site=site,
                     username=username,
                     token=token,
                     log_level=log_level)
    try:
        json = client.upload(project.publication_info(), archive_filename,
                             uploaded_basename)
        return _UploadedStatus(json)
    except Unauthorized as e:
        return SimpleStatus(
            success=False,
            description='Please log in with the "anaconda login" command.',
            errors=["Not logged in."])
    except BinstarError as e:
        return SimpleStatus(success=False,
                            description="Upload failed.",
                            errors=[str(e)])
Ejemplo n.º 2
0
def set_variables(project, vars_and_values, env_spec_name=None):
    """Set variables' values in kapsel-local.yml.

    Returns a ``Status`` instance which evaluates to True on
    success and has an ``errors`` property (with a list of error
    strings) on failure.

    Args:
        project (Project): the project
        vars_and_values (list of tuple): key-value pairs
        env_spec_name (str): name of env spec to use

    Returns:
        ``Status`` instance
    """
    (env_prefix, status) = _prepare_env_prefix(project, env_spec_name)
    if env_prefix is None:
        return status

    local_state = LocalStateFile.load_for_directory(project.directory_path)
    var_reqs = dict()
    for req in project.find_requirements(klass=EnvVarRequirement):
        var_reqs[req.env_var] = req
    present_vars = set(var_reqs.keys())
    errors = []
    local_state_count = 0
    keyring_count = 0
    for varname, value in vars_and_values:
        if varname in present_vars:
            if var_reqs[varname].encrypted:
                keyring.set(env_prefix, varname, value)
                keyring_count = keyring_count + 1
            else:
                local_state.set_value(['variables', varname], value)
                local_state_count = local_state_count + 1
        else:
            errors.append("Variable %s does not exist in the project." %
                          varname)

    if errors:
        return SimpleStatus(success=False,
                            description="Could not set variables.",
                            errors=errors)
    else:
        if local_state_count > 0:
            local_state.save()
        if keyring_count == 0:
            description = ("Values saved in %s." % local_state.filename)
        elif local_state_count == 0:
            description = ("Values saved in the system keychain.")
        else:
            description = (
                "%d values saved in %s, %d values saved in the system keychain."
                % (local_state_count, local_state.filename, keyring_count))
        return SimpleStatus(success=True, description=description)
Ejemplo n.º 3
0
def _remove_env_path(env_path):
    """Also used by project_ops.py to delete environment files."""
    if os.path.exists(env_path):
        try:
            shutil.rmtree(env_path)
            return SimpleStatus(success=True, description=("Deleted environment files in %s." % env_path))
        except Exception as e:
            problem = "Failed to remove environment files in {}: {}.".format(env_path, str(e))
            return SimpleStatus(success=False, description=problem)
    else:
        return SimpleStatus(success=True,
                            description=("Nothing to clean up for environment '%s'." % os.path.basename(env_path)))
Ejemplo n.º 4
0
def remove_service(project, prepare_result, variable_name):
    """Remove a service to kapsel.yml.

    Returns a ``Status`` instance which evaluates to True on
    success and has an ``errors`` property (with a list of error
    strings) on failure.

    Args:
        project (Project): the project
        prepare_result (PrepareResult): result of a previous prepare
        variable_name (str): environment variable name for the service requirement

    Returns:
        ``Status`` instance
    """
    failed = project.problems_status()
    if failed is not None:
        return failed

    requirements = [
        req for req in project.find_requirements(klass=ServiceRequirement)
        if req.service_type == variable_name or req.env_var == variable_name
    ]
    if not requirements:
        return SimpleStatus(
            success=False,
            description="Service '{}' not found in the project file.".format(
                variable_name))

    if len(requirements) > 1:
        return SimpleStatus(
            success=False,
            description=(
                "Conflicting results, found {} matches, use list-services"
                " to identify which service you want to remove").format(
                    len(requirements)))

    env_var = requirements[0].env_var

    status = prepare.unprepare(project, prepare_result, whitelist=[env_var])
    if not status:
        return status

    project.project_file.unset_value(['services', env_var])
    project.project_file.use_changes_without_saving()
    assert project.problems == []

    project.project_file.save()
    return SimpleStatus(
        success=True,
        description="Removed service '{}' from the project file.".format(
            variable_name))
Ejemplo n.º 5
0
def clean(project, prepare_result):
    """Blow away auto-provided state for the project.

    This should not remove any potential "user data" such as
    kapsel-local.yml.

    This includes a call to ``conda_kapsel.prepare.unprepare``
    but also removes the entire services/ and envs/ directories
    even if they contain leftovers that we didn't prepare in the
    most recent prepare() call.

    Args:
        project (Project): the project instance
        prepare_result (PrepareResult): result of a previous prepare

    Returns:
        a ``Status`` instance

    """
    status = prepare.unprepare(project, prepare_result)
    logs = status.logs
    errors = status.errors
    if status:
        logs = logs + [status.status_description]
    else:
        errors = errors + [status.status_description]

    # we also nuke any "debris" from non-current choices, like old
    # environments or services
    def cleanup_dir(dirname):
        if os.path.isdir(dirname):
            logs.append("Removing %s." % dirname)
            try:
                shutil.rmtree(dirname)
            except Exception as e:
                errors.append("Error removing %s: %s." % (dirname, str(e)))

    cleanup_dir(os.path.join(project.directory_path, "services"))
    cleanup_dir(os.path.join(project.directory_path, "envs"))

    if status and len(errors) == 0:
        return SimpleStatus(success=True,
                            description="Cleaned.",
                            logs=logs,
                            errors=errors)
    else:
        return SimpleStatus(success=False,
                            description="Failed to clean everything up.",
                            logs=logs,
                            errors=errors)
Ejemplo n.º 6
0
 def unprovide(self, requirement, environ, local_state_file, overrides, requirement_status=None):
     """Override superclass to delete the downloaded file."""
     project_dir = environ['PROJECT_DIR']
     filename = os.path.abspath(os.path.join(project_dir, requirement.filename))
     try:
         if os.path.isdir(filename):
             shutil.rmtree(filename)
         elif os.path.isfile(filename):
             os.remove(filename)
         else:
             return SimpleStatus(success=True,
                                 description=("No need to remove %s which wasn't downloaded." % filename))
         return SimpleStatus(success=True, description=("Removed downloaded file %s." % filename))
     except Exception as e:
         return SimpleStatus(success=False, description=("Failed to remove %s: %s." % (filename, str(e))))
Ejemplo n.º 7
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)

        status = SimpleStatus(success=True, description='Service added.')
        status.requirement = RedisRequirement(PluginRegistry(), env_var='REDIS_URL', options=dict(type='redis'))

        _monkeypatch_add_service(monkeypatch, status)

        code = _parse_args_and_run_subcommand(['conda-kapsel', 'add-service', 'redis'])
        assert code == 0

        out, err = capsys.readouterr()
        assert (
            'Service added.\n' + 'Added service redis to the project file, its address will be in REDIS_URL.\n') == out
        assert '' == err
Ejemplo n.º 8
0
def remove_download(project, prepare_result, env_var):
    """Remove file or directory referenced by ``env_var`` from file system and the project.

    The returned ``Status`` will be an instance of ``SimpleStatus``. A False
    status will have an ``errors`` property with a list of error
    strings.

    Args:
        project (Project): the project
        prepare_result (PrepareResult): result of a previous prepare
        env_var (str): env var to store the local filename

    Returns:
        ``Status`` instance
    """
    failed = project.problems_status()
    if failed is not None:
        return failed
    # Modify the project file _in memory only_, do not save
    requirement = project.find_requirements(env_var, klass=DownloadRequirement)
    if not requirement:
        return SimpleStatus(
            success=False,
            description="Download requirement: {} not found.".format(env_var))
    assert len(requirement) == 1  # duplicate env vars aren't allowed
    requirement = requirement[0]

    status = prepare.unprepare(project, prepare_result, whitelist=[env_var])
    if status:
        project.project_file.unset_value(['downloads', env_var])
        project.project_file.use_changes_without_saving()
        assert project.problems == []
        project.project_file.save()

    return status
Ejemplo n.º 9
0
def remove_variables(project, vars_to_remove, env_spec_name=None):
    """Remove variables from kapsel.yml and unset their values in local project state.

    Returns a ``Status`` instance which evaluates to True on
    success and has an ``errors`` property (with a list of error
    strings) on failure.

    Args:
        project (Project): the project
        vars_to_remove (list of str): variable names
        env_spec_name (str): name of env spec to use

    Returns:
        ``Status`` instance
    """
    (env_prefix, status) = _prepare_env_prefix(project, env_spec_name)
    if env_prefix is None:
        return status

    local_state = LocalStateFile.load_for_directory(project.directory_path)
    for varname in vars_to_remove:
        _unset_variable(project, env_prefix, varname, local_state)
        project.project_file.unset_value(['variables', varname])
        project.project_file.save()
        local_state.save()

    return SimpleStatus(success=True,
                        description="Variables removed from the project file.")
Ejemplo n.º 10
0
def unset_variables(project, vars_to_unset, env_spec_name=None):
    """Unset variables' values in kapsel-local.yml.

    Returns a ``Status`` instance which evaluates to True on
    success and has an ``errors`` property (with a list of error
    strings) on failure.

    Args:
        project (Project): the project
        vars_to_unset (list of str): variable names
        env_spec_name (str): name of env spec to use

    Returns:
        ``Status`` instance
    """
    (env_prefix, status) = _prepare_env_prefix(project, env_spec_name)
    if env_prefix is None:
        return status

    local_state = LocalStateFile.load_for_directory(project.directory_path)
    for varname in vars_to_unset:
        _unset_variable(project, env_prefix, varname, local_state)
    local_state.save()

    return SimpleStatus(success=True, description=("Variables were unset."))
Ejemplo n.º 11
0
def set_properties(project, name=None, icon=None, description=None):
    """Set simple properties on a project.

    This doesn't support properties which require prepare()
    actions to check their effects; see other calls such as
    ``add_packages()`` for those.

    This will fail if project.problems is non-empty.

    Args:
        project (``Project``): the project instance
        name (str): Name of the project or None to leave unmodified
        icon (str): Icon for the project or None to leave unmodified
        description (str): description for the project or None to leave unmodified

    Returns:
        a ``Status`` instance indicating success or failure
    """
    failed = project.problems_status()
    if failed is not None:
        return failed

    if name is not None:
        project.project_file.set_value('name', name)

    if icon is not None:
        project.project_file.set_value('icon', icon)

    if description is not None:
        project.project_file.set_value('description', description)

    project.project_file.use_changes_without_saving()

    if len(project.problems) == 0:
        # write out the kapsel.yml if it looks like we're safe.
        project.project_file.save()
        return SimpleStatus(success=True,
                            description="Project properties updated.")
    else:
        # revert to previous state (after extracting project.problems)
        status = SimpleStatus(success=False,
                              description="Failed to set project properties.",
                              errors=list(project.problems))
        project.project_file.load()
        return status
Ejemplo n.º 12
0
def remove_env_spec(project, name):
    """Remove the environment spec from project directory and remove from kapsel.yml.

    Returns a ``Status`` subtype (it won't be a
    ``RequirementStatus`` as with some other functions, just a
    plain status).

    Args:
        project (Project): the project
        name (str): environment spec name

    Returns:
        ``Status`` instance
    """
    assert name is not None

    failed = project.problems_status()
    if failed is not None:
        return failed

    if name not in project.env_specs:
        problem = "Environment spec {} doesn't exist.".format(name)
        return SimpleStatus(success=False, description=problem)

    if len(project.env_specs) == 1:
        problem = "At least one environment spec is required; '{}' is the only one left.".format(
            name)
        return SimpleStatus(success=False, description=problem)

    env_path = project.env_specs[name].path(project.directory_path)

    # For remove_service and remove_download, we use unprepare()
    # to do the cleanup; for the environment, it's awkward to do
    # that because the env we want to remove may not be the one
    # that was prepared. So instead we share some code with the
    # CondaEnvProvider but don't try to go through the unprepare
    # machinery.
    status = _remove_env_path(env_path)
    if status:
        project.project_file.unset_value(['env_specs', name])
        project.project_file.use_changes_without_saving()
        assert project.problems == []
        project.project_file.save()

    return status
Ejemplo n.º 13
0
def add_command(project, name, command_type, command, env_spec_name=None):
    """Add a command to kapsel.yml.

    Returns a ``Status`` subtype (it won't be a
    ``RequirementStatus`` as with some other functions, just a
    plain status).

    Args:
       project (Project): the project
       name (str): name of the command
       command_type (str): choice of `bokeh_app`, `notebook`, `unix` or `windows` command
       command (str): the command line or filename itself
       env_spec_name (str): env spec to use with this command

    Returns:
       a ``Status`` instance
    """
    if command_type not in ALL_COMMAND_TYPES:
        raise ValueError("Invalid command type " + command_type +
                         " choose from " + repr(ALL_COMMAND_TYPES))

    name = name.strip()

    failed = project.problems_status()
    if failed is not None:
        return failed

    command_dict = project.project_file.get_value(['commands', name])
    if command_dict is None:
        command_dict = dict()
        project.project_file.set_value(['commands', name], command_dict)

    command_dict[command_type] = command

    if env_spec_name is None:
        if 'env_spec' not in command_dict:
            # make it explicit for clarity
            command_dict['env_spec'] = project.default_env_spec_name
        # if env_spec is set, leave it alone; this way people can
        # modify commands via command line without specifying the
        # env_spec every time.
    else:
        command_dict['env_spec'] = env_spec_name

    project.project_file.use_changes_without_saving()

    failed = project.problems_status(description="Unable to add the command.")
    if failed is not None:
        # reset, maybe someone added conflicting command line types or something
        project.project_file.load()
        return failed
    else:
        project.project_file.save()
        return SimpleStatus(success=True,
                            description="Command added to project file.")
Ejemplo n.º 14
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)

        status = SimpleStatus(success=True, description='Service added.')
        status.requirement = RedisRequirement(PluginRegistry(),
                                              env_var='REDIS_URL',
                                              options=dict(type='redis'))

        _monkeypatch_add_service(monkeypatch, status)

        code = _parse_args_and_run_subcommand(
            ['conda-kapsel', 'add-service', 'redis'])
        assert code == 0

        out, err = capsys.readouterr()
        assert (
            'Service added.\n' +
            'Added service redis to the project file, its address will be in REDIS_URL.\n'
        ) == out
        assert '' == err
Ejemplo n.º 15
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        _monkeypatch_add_service(
            monkeypatch,
            SimpleStatus(success=False, description='Service add FAIL.'))

        code = _parse_args_and_run_subcommand(
            ['conda-kapsel', 'add-service', 'redis'])
        assert code == 1

        out, err = capsys.readouterr()
        assert '' == out
        assert 'Service add FAIL.\n' == err
Ejemplo n.º 16
0
def remove_command(project, name):
    """Remove a command from kapsel.yml.

    Returns a ``Status`` subtype (it won't be a
    ``RequirementStatus`` as with some other functions, just a
    plain status).

    Args:
       project (Project): the project
       name (string): name of the command to be removed

    Returns:
       a ``Status`` instance
    """
    failed = project.problems_status()
    if failed is not None:
        return failed

    if name not in project.commands:
        return SimpleStatus(
            success=False,
            description="Command: '{}' not found in project file.".format(
                name))

    command = project.commands[name]
    if command.auto_generated:
        return SimpleStatus(
            success=False,
            description="Cannot remove auto-generated command: '{}'.".format(
                name))

    project.project_file.unset_value(['commands', name])
    project.project_file.use_changes_without_saving()
    assert project.problems == []
    project.project_file.save()

    return SimpleStatus(
        success=True,
        description="Command: '{}' removed from project file.".format(name))
Ejemplo n.º 17
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        _monkeypatch_add_env_spec(
            monkeypatch,
            SimpleStatus(success=True, description='Environment looks good.'))

        code = _parse_args_and_run_subcommand(
            ['conda-kapsel', 'add-env-spec', '--name', 'foo'])
        assert code == 0

        out, err = capsys.readouterr()
        assert ('Environment looks good.\n' +
                'Added environment foo to the project file.\n') == out
        assert '' == err
Ejemplo n.º 18
0
 def problems_status(self, description=None):
     """Get a ``Status`` describing project problems, or ``None`` if no problems."""
     if len(self.problems) > 0:
         errors = []
         for problem in self.problems:
             errors.append(problem)
         if description is None:
             description = "Unable to load the project."
         return SimpleStatus(success=False,
                             description=description,
                             logs=[],
                             errors=errors)
     else:
         return None
Ejemplo n.º 19
0
def shutdown_service_run_state(local_state_file, service_name):
    """Run any shutdown commands from the local state file for the given service.

    Also remove the shutdown commands from the file.

    Args:
        local_state_file (LocalStateFile): local state
        service_name (str): the name of the service, usually a
            variable name, should be specific enough to uniquely
            identify the provider

    Returns:
        a `Status` instance potentially containing errors
    """
    run_states = local_state_file.get_all_service_run_states()
    if service_name not in run_states:
        return SimpleStatus(success=True, description=("Nothing to do to shut down %s." % service_name))

    errors = []
    state = run_states[service_name]
    if 'shutdown_commands' in state:
        commands = state['shutdown_commands']
        for command in commands:
            code = subprocess.call(command)
            if code != 0:
                errors.append("Shutting down %s, command %s failed with code %d." % (service_name, repr(command), code))
    # clear out the run state once we try to shut it down
    local_state_file.set_service_run_state(service_name, dict())
    local_state_file.save()

    if errors:
        return SimpleStatus(success=False,
                            description=("Shutdown commands failed for %s." % service_name),
                            errors=errors)
    else:
        return SimpleStatus(success=True, description=("Successfully shut down %s." % service_name))
Ejemplo n.º 20
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        _monkeypatch_add_env_spec(
            monkeypatch,
            SimpleStatus(success=False,
                         description='Environment variable MYDATA is not set.',
                         logs=['This is a log message.'],
                         errors=['This is an error message.']))

        code = _parse_args_and_run_subcommand(
            ['conda-kapsel', 'add-env-spec', '--name', 'foo'])
        assert code == 1

        out, err = capsys.readouterr()
        assert '' == out
        assert 'This is a log message.\nThis is an error message.\nEnvironment variable MYDATA is not set.\n' == err
Ejemplo n.º 21
0
def add_variables(project, vars_to_add, defaults=None):
    """Add variables in kapsel.yml, optionally setting their defaults.

    Returns a ``Status`` instance which evaluates to True on
    success and has an ``errors`` property (with a list of error
    strings) on failure.

    Args:
        project (Project): the project
        vars_to_add (list of str): variable names
        defaults (dict): dictionary from keys to defaults, can be empty

    Returns:
        ``Status`` instance
    """
    failed = project.problems_status()
    if failed is not None:
        return failed

    if defaults is None:
        defaults = dict()

    present_vars = {
        req.env_var
        for req in project.requirements if isinstance(req, EnvVarRequirement)
    }
    for varname in vars_to_add:
        if varname in defaults:
            # we need to update the default even if var already exists
            new_default = defaults.get(varname)
            variable_value = project.project_file.get_value(
                ['variables', varname])
            if variable_value is None or not isinstance(variable_value, dict):
                variable_value = new_default
            else:
                variable_value['default'] = new_default
            project.project_file.set_value(['variables', varname],
                                           variable_value)
        elif varname not in present_vars:
            # we are only adding the var if nonexistent and should leave
            # the default alone if it's already set
            project.project_file.set_value(['variables', varname], None)
    project.project_file.save()

    return SimpleStatus(success=True,
                        description="Variables added to the project file.")
Ejemplo n.º 22
0
    def unprovide(self, requirement, environ, local_state_file, overrides, requirement_status=None):
        """Override superclass to delete project-scoped envs directory."""
        config = self.read_config(requirement,
                                  environ,
                                  local_state_file,
                                  # future: pass in this default_env_spec_name
                                  default_env_spec_name='default',
                                  overrides=overrides)

        env_path = config.get('value', None)
        assert env_path is not None
        project_dir = environ['PROJECT_DIR']
        if not env_path.startswith(project_dir):
            return SimpleStatus(success=True,
                                description=("Current environment is not in %s, no need to delete it." % project_dir))

        return _remove_env_path(env_path)
Ejemplo n.º 23
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        params = _monkeypatch_remove_packages(
            monkeypatch, SimpleStatus(success=True,
                                      description='Installed ok.'))

        code = _parse_args_and_run_subcommand(
            ['conda-kapsel', 'remove-packages', 'bar'])
        assert code == 0

        out, err = capsys.readouterr()
        assert ('Installed ok.\n' +
                'Removed packages from project file: bar.\n') == out
        assert '' == err

        assert 1 == len(params['args'])
        assert dict(env_spec_name=None, packages=['bar']) == params['kwargs']
Ejemplo n.º 24
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        params = _monkeypatch_add_env_spec(
            monkeypatch,
            SimpleStatus(success=True, description='Environment looks good.'))

        code = _parse_args_and_run_subcommand([
            'conda-kapsel', 'add-env-spec', '--name', 'foo', '--channel', 'c1',
            '--channel=c2', 'a', 'b'
        ])
        assert code == 0

        out, err = capsys.readouterr()
        assert ('Environment looks good.\n' +
                'Added environment foo to the project file.\n') == out
        assert '' == err

        assert 1 == len(params['args'])
        assert dict(name='foo', packages=['a', 'b'],
                    channels=['c1', 'c2']) == params['kwargs']
Ejemplo n.º 25
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        params = _monkeypatch_add_packages(
            monkeypatch, SimpleStatus(success=True,
                                      description='Installed ok.'))

        code = _parse_args_and_run_subcommand([
            'conda-kapsel', 'add-packages', '--channel', 'c1', '--channel=c2',
            'a', 'b'
        ])
        assert code == 0

        out, err = capsys.readouterr()
        assert ('Installed ok.\n' +
                'Added packages to project file: a, b.\n') == out
        assert '' == err

        assert 1 == len(params['args'])
        assert dict(env_spec_name=None,
                    packages=['a', 'b'],
                    channels=['c1', 'c2']) == params['kwargs']
Ejemplo n.º 26
0
 def unprovide(self, requirement, environ, local_state_file, overrides, requirement_status=None):
     """Override superclass to return success always."""
     return SimpleStatus(success=True, description=("Nothing to clean up for %s." % requirement.env_var))
Ejemplo n.º 27
0
def add_service(project, service_type, variable_name=None):
    """Add a service to kapsel.yml.

    The returned ``Status`` should be a ``RequirementStatus`` for
    the service requirement if it evaluates to True (on success),
    but may be another subtype of ``Status`` on failure. A False
    status will have an ``errors`` property with a list of error
    strings.

    Args:
        project (Project): the project
        service_type (str): which kind of service
        variable_name (str): environment variable name (None for default)

    Returns:
        ``Status`` instance
    """
    failed = project.problems_status()
    if failed is not None:
        return failed

    known_types = project.plugin_registry.list_service_types()
    found = None
    for known in known_types:
        if known.name == service_type:
            found = known
            break

    if found is None:
        return SimpleStatus(
            success=False,
            description="Unable to add service.",
            logs=[],
            errors=[
                "Unknown service type '%s', we know about: %s" %
                (service_type, ", ".join(map(lambda s: s.name, known_types)))
            ])

    if variable_name is None:
        variable_name = found.default_variable

    assert len(known_types
               ) == 1  # when this fails, see change needed in the loop below

    requirement_already_exists = False
    existing_requirements = project.find_requirements(env_var=variable_name)
    if len(existing_requirements) > 0:
        requirement = existing_requirements[0]
        if isinstance(requirement, ServiceRequirement):
            assert requirement.service_type == service_type
            # when the above assertion fails, add the second known type besides
            # redis in test_project_ops.py::test_add_service_already_exists_with_different_type
            # and then uncomment the below code.
            # if requirement.service_type != service_type:
            #    return SimpleStatus(success=False, description="Unable to add service.", logs=[],
            #                            errors=["Service %s already exists but with type '%s'" %
            #                              (variable_name, requirement.service_type)])
            # else:
            requirement_already_exists = True
        else:
            return SimpleStatus(
                success=False,
                description="Unable to add service.",
                logs=[],
                errors=["Variable %s is already in use." % variable_name])

    if not requirement_already_exists:
        project.project_file.set_value(['services', variable_name],
                                       service_type)

    return _commit_requirement_if_it_works(project, variable_name)
Ejemplo n.º 28
0
 def mock_upload(*args, **kwargs):
     params['args'] = args
     params['kwargs'] = kwargs
     return SimpleStatus(success=True, description="Yay", logs=['Hello'])
Ejemplo n.º 29
0
def update_command(project,
                   name,
                   command_type=None,
                   command=None,
                   new_name=None):
    """Update attributes of a command in kapsel.yml.

    Returns a ``Status`` subtype (it won't be a
    ``RequirementStatus`` as with some other functions, just a
    plain status).

    Args:
       project (Project): the project
       name (str): name of the command
       command_type (str or None): choice of `bokeh_app`, `notebook`, `unix` or `windows` command
       command (str or None): the command line or filename itself; command_type must also be specified

    Returns:
       a ``Status`` instance
    """
    # right now update_command can be called "pointlessly" (with
    # no new command), this is because in theory it might let you
    # update other properties too, when/if commands have more
    # properties.
    if command_type is None and new_name is None:
        return SimpleStatus(success=True,
                            description=("Nothing to change about command %s" %
                                         name))

    if command_type not in (list(ALL_COMMAND_TYPES) + [None]):
        raise ValueError("Invalid command type " + command_type +
                         " choose from " + repr(ALL_COMMAND_TYPES))

    if command is None and command_type is not None:
        raise ValueError(
            "If specifying the command_type, must also specify the command")

    failed = project.problems_status()
    if failed is not None:
        return failed

    if name not in project.commands:
        return SimpleStatus(success=False,
                            description="Failed to update command.",
                            errors=[("No command '%s' found." % name)])

    command_object = project.commands[name]
    if command_object.auto_generated:
        return SimpleStatus(
            success=False,
            description="Failed to update command.",
            errors=[("Autogenerated command '%s' can't be modified." % name)])

    command_dict = project.project_file.get_value(['commands', name])
    assert command_dict is not None

    if new_name:
        project.project_file.unset_value(['commands', name])
        project.project_file.set_value(['commands', new_name], command_dict)

    existing_types = set(command_dict.keys())
    conflicting_types = existing_types - set([command_type])
    # 'unix' and 'windows' don't conflict with one another
    if command_type == 'unix':
        conflicting_types = conflicting_types - set(['windows'])
    elif command_type == 'windows':
        conflicting_types = conflicting_types - set(['unix'])

    if command_type is not None:
        for conflicting in conflicting_types:
            del command_dict[conflicting]

        command_dict[command_type] = command

    project.project_file.use_changes_without_saving()

    failed = project.problems_status(description="Unable to add the command.")
    if failed is not None:
        # reset, maybe someone added a nonexistent bokeh app or something
        project.project_file.load()
        return failed
    else:
        project.project_file.save()
        return SimpleStatus(success=True,
                            description="Command updated in project file.")
Ejemplo n.º 30
0
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
Ejemplo n.º 31
0
def remove_packages(project, env_spec_name, packages):
    """Attempt to remove packages from an environment in kapsel.yml.

    If the env_spec_name is None rather than an env name,
    packages are removed from the global packages section
    (from all environments).

    The returned ``Status`` should be a ``RequirementStatus`` for
    the environment requirement if it evaluates to True (on success),
    but may be another subtype of ``Status`` on failure. A False
    status will have an ``errors`` property with a list of error
    strings.

    Args:
        project (Project): the project
        env_spec_name (str): environment spec name or None for all environment specs
        packages (list of str): packages to remove

    Returns:
        ``Status`` instance
    """
    # This is sort of one big ugly. What we SHOULD be able to do
    # is simply remove the package from kapsel.yml then re-run
    # prepare, and if the packages aren't pulled in as deps of
    # something else, they get removed. This would work if our
    # approach was to always force the env to exactly the env
    # we'd have created from scratch, given our env config.
    # But that isn't our approach right now.
    #
    # So what we do right now is remove the package from the env,
    # and then remove it from kapsel.yml, and then see if we can
    # still prepare the project.

    failed = project.problems_status()
    if failed is not None:
        return failed

    assert packages is not None
    assert len(packages) > 0

    if env_spec_name is None:
        envs = project.env_specs.values()
        unaffected_envs = []
    else:
        env = project.env_specs.get(env_spec_name, None)
        if env is None:
            problem = "Environment spec {} doesn't exist.".format(
                env_spec_name)
            return SimpleStatus(success=False, description=problem)
        else:
            envs = [env]
            unaffected_envs = list(project.env_specs.values())
            unaffected_envs.remove(env)
            assert len(unaffected_envs) == (len(project.env_specs) - 1)

    assert len(envs) > 0

    conda = conda_manager.new_conda_manager()

    for env in envs:
        prefix = env.path(project.directory_path)
        try:
            if os.path.isdir(prefix):
                conda.remove_packages(prefix, packages)
        except conda_manager.CondaManagerError:
            pass  # ignore errors; not all the envs will exist or have the package installed perhaps

    def envs_to_their_dicts(envs):
        env_dicts = []
        for env in envs:
            env_dict = project.project_file.get_value(['env_specs', env.name])
            if env_dict is not None:  # it can be None for the default environment (which doesn't have to be listed)
                env_dicts.append(env_dict)
        return env_dicts

    env_dicts = envs_to_their_dicts(envs)
    env_dicts.append(project.project_file.root)

    unaffected_env_dicts = envs_to_their_dicts(unaffected_envs)

    assert len(env_dicts) > 0

    previous_global_deps = set(project.project_file.root.get('packages', []))

    for env_dict in env_dicts:
        # 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', [])
        removed_set = set(packages)
        _filter_inplace(lambda dep: dep not in removed_set, old_packages)
        env_dict['packages'] = old_packages

    # if we removed any deps from global, add them to the
    # individual envs that were not supposed to be affected.
    new_global_deps = set(project.project_file.root.get('packages', []))
    removed_from_global = (previous_global_deps - new_global_deps)
    for env_dict in unaffected_env_dicts:
        # old_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.extend(list(removed_from_global))
        env_dict['packages'] = old_packages

    status = _commit_requirement_if_it_works(project,
                                             CondaEnvRequirement,
                                             env_spec_name=env_spec_name)

    return status