Exemple #1
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        params = _monkeypatch_update(
            monkeypatch, SimpleStatus(success=True, description='Updated.'))

        code = _parse_args_and_run_subcommand(['anaconda-project', 'update'])
        assert code == 0

        out, err = capsys.readouterr()
        assert ('Updated.\n') == out
        assert '' == err

        assert 1 == len(params['args'])
        assert dict(env_spec_name=None) == params['kwargs']
Exemple #2
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(['anaconda-project', '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
    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(
            ['anaconda-project', '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
Exemple #4
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(['anaconda-project', 'add-packages', '--env-spec', 'foo', '--channel',
                                               'c1', '--channel=c2', 'a', 'b'])
        assert code == 0

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

        assert 1 == len(params['args'])
        assert dict(env_spec_name='foo', packages=['a', 'b'], channels=['c1', 'c2']) == params['kwargs']
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 = logged_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))
Exemple #6
0
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)

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

        _monkeypatch_add_service(monkeypatch, status)

        code = _parse_args_and_run_subcommand(['anaconda-project', '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
Exemple #7
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(['anaconda-project', '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']
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        params = _monkeypatch_remove_platforms(
            monkeypatch, SimpleStatus(success=True,
                                      description='Installed ok.'))

        code = _parse_args_and_run_subcommand(
            ['anaconda-project', 'remove-platforms', 'bar'])
        assert code == 0

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

        assert 1 == len(params['args'])
        assert dict(env_spec_name=None, platforms=['bar']) == params['kwargs']
def add_variables(project, vars_to_add, defaults=None):
    """Add variables in anaconda-project.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.")
    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='bootstrap-env',
            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)
Exemple #11
0
def _download(project,
              project_dir=None,
              parent_dir=None,
              site=None,
              username=None,
              token=None,
              log_level=None):
    client = _Client(site=site,
                     username=username,
                     token=token,
                     log_level=log_level)
    try:
        fn = client.download(project, project_dir, parent_dir)
        return _DownloadedStatus(fn)
    except BinstarError as e:
        return SimpleStatus(success=False,
                            description="{} was not found.".format(project),
                            errors=[str(e)])
    def check(dirname):
        _monkeypatch_pwd(monkeypatch, dirname)
        params = _monkeypatch_remove_packages(
            monkeypatch, SimpleStatus(success=True,
                                      description='Installed ok.'))

        code = _parse_args_and_run_subcommand([
            'anaconda-project', 'remove-packages', '--env-spec', 'foo', 'bar'
        ])
        assert code == 0

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

        assert 1 == len(params['args'])
        assert dict(env_spec_name='foo', packages=['bar'],
                    pip=False) == params['kwargs']
def add_command(project,
                name,
                command_type,
                command,
                env_spec_name=None,
                supports_http_options=None):
    """Add a command to anaconda-project.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
       supports_http_options (bool): None for leave it alone, otherwise true or false

    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

    if supports_http_options is not None:
        assert isinstance(supports_http_options, bool)
        command_dict['supports_http_options'] = supports_http_options

    if command_type == 'notebook':
        notebook_file = os.path.join(project.directory_path, command)
        errors = []
        # TODO missing notebook should be an error caught before here
        if os.path.isfile(notebook_file):
            extras = notebook_analyzer.extras(notebook_file, errors)
        else:
            extras = {}
        if len(errors) > 0:
            failed = SimpleStatus(success=False,
                                  description="Unable to add the command.",
                                  errors=errors)
            return failed
        command_dict.update(extras)

    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.")
def remove_packages(project, env_spec_name, packages):
    """Attempt to remove packages from an environment in anaconda-project.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 anaconda-project.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 anaconda-project.yml, and then see if we can
    # still prepare the project.

    # TODO this should handle env spec inheritance in the same way
    # it handles the global packages list, that is, removing a package
    # from a spec should also remove it from its ancestors (and possibly
    # add it to other children of those ancestors).
    # Not doing it at the time of writing this comment because it might
    # be nicer to rewrite this whole thing when we add version pinning
    # anyway.

    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
Exemple #15
0
def unprepare(project, prepare_result, whitelist=None):
    """Attempt to clean up project-scoped resources allocated by prepare().

    This will retain any user configuration choices about how to
    provide requirements, but it stops project-scoped services.
    Global system services or other services potentially shared
    among projects will not be stopped.

    To stop a single service, use ``whitelist=["SERVICE_VARIABLE"]``.

    Args:
        project (Project): the project
        prepare_result (PrepareResult): result from the previous prepare
        whitelist (iterable of str or type): ONLY call shutdown commands for the listed env vars' requirements

    Returns:
        a ``Status`` instance
    """
    if project.problems:
        errors = []
        for problem in project.problems:
            errors.append(problem)
        return SimpleStatus(success=False,
                            description="Unable to load the project.",
                            errors=errors)

    local_state_file = LocalStateFile.load_for_directory(
        project.directory_path)

    # note: if the prepare_result was a failure before statuses
    # were even checked, then statuses could be empty
    failed_statuses = []
    failed_requirements = []
    success_statuses = []
    for status in prepare_result.statuses:
        requirement = status.requirement
        if not _in_provide_whitelist(whitelist, requirement):
            continue

        provider = status.provider
        unprovide_status = provider.unprovide(requirement,
                                              prepare_result.environ,
                                              local_state_file,
                                              prepare_result.overrides, status)
        if not unprovide_status:
            failed_requirements.append(requirement)
            failed_statuses.append(unprovide_status)
        else:
            success_statuses.append(unprovide_status)

    if not failed_statuses:
        if len(success_statuses) > 1:
            logs = [status.status_description for status in success_statuses]
            return SimpleStatus(success=True,
                                description="Success.",
                                logs=logs)
        elif len(success_statuses) > 0:
            return success_statuses[0]
        else:
            return SimpleStatus(success=True,
                                description="Nothing to clean up.")
    elif len(failed_statuses) == 1:
        return failed_statuses[0]
    else:
        all_errors = [
            error for status in failed_statuses for error in status.errors
        ]
        all_names = sorted([
            req.env_var for req in failed_requirements
            if isinstance(req, EnvVarRequirement)
        ])
        return SimpleStatus(success=False,
                            description=("Failed to clean up %s." %
                                         ", ".join(all_names)),
                            errors=all_errors)
def add_service(project, service_type, variable_name=None):
    """Add a service to anaconda-project.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)
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
Exemple #18
0
 def mock_upload(*args, **kwargs):
     params['args'] = args
     params['kwargs'] = kwargs
     return SimpleStatus(success=True, description="Yay", logs=['Hello'])
 def mock_unarchive(filename, project_dir, parent_dir=None):
     return SimpleStatus(success=False,
                         description="DESC",
                         logs=['a', 'b'],
                         errors=['c', 'd'])
def _unarchive_project(archive_filename,
                       project_dir,
                       frontend,
                       parent_dir=None):
    """Unpack an archive of files in the project.

    This takes care of several details, for example it deals with
    hostile archives containing files outside of the dest
    directory, and it handles both tar and zip.

    It does not load or validate the unpacked project.

    project_dir can be None to auto-choose one.

    If parent_dir is non-None, place the project_dir in it. This is most useful
    if project_dir is None.

    Args:
        archive_filename (str): the tar or zip archive file
        project_dir (str): the directory that will contain the project config file
        parent_dir (str): place project directory in here

    Returns:
        a ``Status``, if failed has ``errors``, on success has a ``project_dir`` property
    """
    if project_dir is not None and os.path.isabs(
            project_dir) and parent_dir is not None:
        raise ValueError(
            "If supplying parent_dir to unarchive, project_dir must be relative or None"
        )

    frontend = _new_error_recorder(frontend)

    list_files = None
    extract_files = None
    if archive_filename.endswith(".zip"):
        list_files = _list_files_zip
        extract_files = _extract_files_zip
    elif any([
            archive_filename.endswith(suffix)
            for suffix in [".tar", ".tar.gz", ".tar.bz2"]
    ]):
        list_files = _list_files_tar
        extract_files = _extract_files_tar
    else:
        frontend.error(
            "Unsupported archive filename %s, must be a .zip, .tar.gz, or .tar.bz2"
            % (archive_filename))
        return SimpleStatus(success=False,
                            description=("Could not unpack archive %s" %
                                         archive_filename),
                            errors=frontend.pop_errors())

    try:
        result = _get_source_and_dest_files(archive_filename, list_files,
                                            project_dir, parent_dir, frontend)
        if result is None:
            return SimpleStatus(success=False,
                                description=("Could not unpack archive %s" %
                                             archive_filename),
                                errors=frontend.pop_errors())
        (canonical_project_dir, src_and_dest) = result

        if len(src_and_dest) == 0:
            frontend.error(
                "Archive does not contain a project directory or is empty.")
            return SimpleStatus(success=False,
                                description=("Could not unpack archive %s" %
                                             archive_filename),
                                errors=frontend.pop_errors())

        assert not os.path.exists(canonical_project_dir)
        os.makedirs(canonical_project_dir)

        try:
            extract_files(archive_filename, src_and_dest, frontend)
        except Exception as e:
            try:
                shutil.rmtree(canonical_project_dir)
            except (IOError, OSError):
                pass
            raise e

        return _UnarchiveStatus(
            success=True,
            description=("Project archive unpacked to %s." %
                         canonical_project_dir),
            project_dir=canonical_project_dir)
    except (IOError, OSError, zipfile.error, tarfile.TarError) as e:
        frontend.error(str(e))
        return SimpleStatus(success=False,
                            description="Failed to read project archive.",
                            errors=frontend.pop_errors())
Exemple #21
0
 def mock_unset_variables(project, env_spec_name, _vars):
     params.append(env_spec_name)
     params.append(_vars)
     return SimpleStatus(success=True, description="BOO")
Exemple #22
0
 def mock_upload(*args, **kwargs):
     params['args'] = args
     params['kwargs'] = kwargs
     args[0].frontend.info('Hello')
     return SimpleStatus(success=True, description="Yay")
def _archive_project(project, filename):
    """Make an archive of the non-ignored files in the project.

    Args:
        project (``Project``): the project
        filename (str): name for the new zip or tar.gz archive file

    Returns:
        a ``Status``, if failed has ``errors``
    """
    failed = project.problems_status()
    if failed is not None:
        for error in failed.errors:
            project.frontend.error(error)
        return failed

    frontend = _new_error_recorder(project.frontend)

    if not os.path.exists(project.project_file.filename):
        frontend.error("%s does not exist." % project.project_file.basename)
        return SimpleStatus(success=False,
                            description="Can't create an archive.",
                            errors=frontend.pop_errors())

    # this would most likely happen in a GUI editor, if it reloaded
    # the project from memory but hadn't saved yet.
    if project.project_file.has_unsaved_changes:
        frontend.error("%s has been modified but not saved." %
                       project.project_file.basename)
        return SimpleStatus(success=False,
                            description="Can't create an archive.",
                            errors=frontend.pop_errors())

    infos = _enumerate_archive_files(
        project.directory_path,
        frontend,
        requirements=project.union_of_requirements_for_all_envs)
    if infos is None:
        return SimpleStatus(success=False,
                            description="Failed to list files in the project.",
                            errors=frontend.pop_errors())

    # don't put the destination zip into itself, since it's fairly natural to
    # create a archive right in the project directory
    relative_dest_file = subdirectory_relative_to_directory(
        filename, project.directory_path)
    if not os.path.isabs(relative_dest_file):
        infos = [
            info for info in infos if info.relative_path != relative_dest_file
        ]

    tmp_filename = filename + ".tmp-" + str(uuid.uuid4())
    try:
        if filename.lower().endswith(".zip"):
            _write_zip(project.name, infos, tmp_filename, frontend)
        elif filename.lower().endswith(".tar.gz"):
            _write_tar(project.name,
                       infos,
                       tmp_filename,
                       compression="gz",
                       frontend=frontend)
        elif filename.lower().endswith(".tar.bz2"):
            _write_tar(project.name,
                       infos,
                       tmp_filename,
                       compression="bz2",
                       frontend=frontend)
        elif filename.lower().endswith(".tar"):
            _write_tar(project.name,
                       infos,
                       tmp_filename,
                       compression=None,
                       frontend=frontend)
        else:
            frontend.error("Unsupported archive filename %s." % (filename))
            return SimpleStatus(
                success=False,
                description=
                "Project archive filename must be a .zip, .tar.gz, or .tar.bz2.",
                errors=frontend.pop_errors())
        rename_over_existing(tmp_filename, filename)
    except IOError as e:
        frontend.error(str(e))
        return SimpleStatus(
            success=False,
            description=("Failed to write project archive %s." % (filename)),
            errors=frontend.pop_errors())
    finally:
        try:
            os.remove(tmp_filename)
        except (IOError, OSError):
            pass

    unlocked = []
    for env_spec in project.env_specs.values():
        if env_spec.lock_set.disabled:
            unlocked.append(env_spec.name)

    if len(unlocked) > 0:
        frontend.info(
            "Warning: env specs are not locked, which means they may not "
            "work consistently for others or when deployed.")
        frontend.info(
            "  Consider using the 'anaconda-project lock' command to lock the project."
        )
        if len(unlocked) != len(project.env_specs):
            frontend.info("  Unlocked env specs are: " +
                          (", ".join(sorted(unlocked))))

    return SimpleStatus(success=True,
                        description=("Created project archive %s" % filename))
 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))
Exemple #25
0
 def mock_set_variables(project, _vars):
     params.append(_vars)
     return SimpleStatus(success=True, description="BOO")
Exemple #26
0
 def mock_dockerize(*args, **kwargs):
     params['args'] = args
     params['kwargs'] = kwargs
     return SimpleStatus(success=False, description="Boo")
def update_command(project,
                   name,
                   command_type=None,
                   command=None,
                   new_name=None):
    """Update attributes of a command in anaconda-project.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_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.")
Exemple #28
0
def _archive_project(project, filename):
    """Make an archive of the non-ignored files in the project.

    Args:
        project (``Project``): the project
        filename (str): name for the new zip or tar.gz archive file

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

    if not os.path.exists(project.project_file.filename):
        return SimpleStatus(success=False,
                            description="Can't create an archive.",
                            errors=[("%s does not exist." %
                                     project.project_file.basename)])

    # this would most likely happen in a GUI editor, if it reloaded
    # the project from memory but hadn't saved yet.
    if project.project_file.has_unsaved_changes:
        return SimpleStatus(success=False,
                            description="Can't create an archive.",
                            errors=[("%s has been modified but not saved." %
                                     project.project_file.basename)])

    errors = []
    infos = _enumerate_archive_files(project.directory_path,
                                     errors,
                                     requirements=project.requirements)
    if infos is None:
        return SimpleStatus(success=False,
                            description="Failed to list files in the project.",
                            errors=errors)

    # don't put the destination zip into itself, since it's fairly natural to
    # create a archive right in the project directory
    relative_dest_file = subdirectory_relative_to_directory(
        filename, project.directory_path)
    if not os.path.isabs(relative_dest_file):
        infos = [
            info for info in infos if info.relative_path != relative_dest_file
        ]

    logs = []
    tmp_filename = filename + ".tmp-" + str(uuid.uuid4())
    try:
        if filename.lower().endswith(".zip"):
            _write_zip(project.name, infos, tmp_filename, logs)
        elif filename.lower().endswith(".tar.gz"):
            _write_tar(project.name,
                       infos,
                       tmp_filename,
                       compression="gz",
                       logs=logs)
        elif filename.lower().endswith(".tar.bz2"):
            _write_tar(project.name,
                       infos,
                       tmp_filename,
                       compression="bz2",
                       logs=logs)
        elif filename.lower().endswith(".tar"):
            _write_tar(project.name,
                       infos,
                       tmp_filename,
                       compression=None,
                       logs=logs)
        else:
            return SimpleStatus(
                success=False,
                description=
                "Project archive filename must be a .zip, .tar.gz, or .tar.bz2.",
                errors=["Unsupported archive filename %s." % (filename)])
        rename_over_existing(tmp_filename, filename)
    except IOError as e:
        return SimpleStatus(
            success=False,
            description=("Failed to write project archive %s." % (filename)),
            errors=[str(e)])
    finally:
        try:
            os.remove(tmp_filename)
        except (IOError, OSError):
            pass

    return SimpleStatus(success=True,
                        description=("Created project archive %s" % filename),
                        logs=logs)
 def mock_set_variables(project, env_spec_name, vars_and_values,
                        prepare_result):
     return SimpleStatus(success=False,
                         description="Set variables FAIL")
Exemple #30
0
 def mock_download(*args, **kwargs):
     params['args'] = args
     params['kwargs'] = kwargs
     return SimpleStatus(success=True, description="Yay")