def provide(self, requirement, context): """Override superclass to start a download.. If it locates a downloaded file with matching checksum, it sets the requirement's env var to that filename. """ super_result = super(DownloadProvider, self).provide(requirement, context) if context.mode == PROVIDE_MODE_CHECK: return super_result # we do the download in both prod and dev mode frontend = _new_error_recorder(context.frontend) if requirement.env_var not in context.environ or context.status.analysis.config['source'] == 'download': filename = self._provide_download(requirement, context, frontend) if filename is not None: context.environ[requirement.env_var] = filename return super_result.copy_with_additions(errors=frontend.pop_errors())
def provide(self, requirement, context): """Override superclass to start a project-scoped redis-server. If it locates or starts a redis-server, it sets the requirement's env var to that server's URL. """ assert 'PATH' in context.environ url = None source = context.status.analysis.config['source'] super_result = super(RedisProvider, self).provide(requirement, context) url = context.environ.get(requirement.env_var, None) frontend = _new_error_recorder(context.frontend) # we jump through a little hoop to avoid a "can't connect to system redis" # message if we're going to end up successfully starting a local one. system_failed = False if url is None and (source == 'find_system' or source == 'find_all'): url = self._provide_system(requirement, context, frontend) if url is None: system_failed = True if url is None and (source == 'find_project' or source == 'find_all'): # we will only start a local Redis in "dev" mode, not prod or check mode if context.mode == PROVIDE_MODE_DEVELOPMENT: url = self._provide_project(requirement, context, frontend) if url is None: if system_failed: frontend.error("Could not connect to system default Redis.") else: context.environ[requirement.env_var] = url return super_result.copy_with_additions(errors=frontend.pop_errors())
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())
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))