def update(self): ''' Searches for updates for every package in the WorkingSet. Then calls :meth:`~pyg.inst.Updater.upgrade`. ''' logger.info('Searching for updates') for dist in self.working_set: package = dist.project_name version = Version(dist.version) logger.verbose('Found: {0}=={1}', package, version) try: json = PyPIJson(package).retrieve() new_version = Version(json['info']['version']) except Exception as e: logger.error('Error: Failed to fetch data for {0} ({1})', package, e) continue if version >= new_version: continue txt = 'A new release is avaiable for {0}: {1!s} (old {2}), update'.format(package, new_version, dist.version) u = logger.ask(txt, bool=('upgrade version', 'keep working version'), dont_ask=self.yes) if u: self.upgrade(package, json, new_version) else: logger.info('{0} has not been upgraded', package) self._clean() logger.success('Updating finished successfully')
def install(self): try: r = Requirement(self.req) updater = Updater(skip=True) if self.upgrading: updater.remove_files(self.req) r.install() # Now let's install dependencies Installer._install_deps(r.reqset, r.name, updater) logger.success('{0} installed successfully', r.name) except (KeyboardInterrupt, Exception) as e: try: msg = e.args[0] except IndexError: msg = repr(e) if isinstance(e, KeyboardInterrupt): logger.warn('Process interrupted...') else: logger.warn('Error: An error occurred during the {0} of {1}: {2}', 'upgrading' if self.upgrading else 'installation', self.req, msg) if self.upgrading: logger.info('Restoring files...') updater.restore_files(self.req) else: logger.info('Removing broken files...') Uninstaller(self.req).uninstall() logger.error(msg, exc=InstallationError)
def uninstall(self): path_re = re.compile(r'\./{0}-[\d\w\.]+-py\d\.\d.egg'.format(self.name), re.I) path_re2 = re.compile(r'\.{0}'.format(self.name), re.I) to_del = self.find_files() if not to_del: logger.warn('{0}: did not find any files to delete', self.name) raise PygError logger.info('Uninstalling {0}', self.name) logger.indent += 8 for d in to_del: logger.info(d) logger.indent -= 8 do_it = logger.ask('Proceed', bool=('remove files', 'cancel'), dont_ask=args_manager['install']['yes']) if do_it: for d in to_del: try: logger.verbose('Deleting: {0}', d) shutil.rmtree(d) except OSError: ## It is not a directory try: os.remove(d) except OSError: logger.error('Error: cannot delete {0}', d) logger.verbose('Removing egg path from easy_install.pth...') with open(EASY_INSTALL) as f: lines = f.readlines() with open(EASY_INSTALL, 'w') as f: for line in lines: if path_re.match(line) or path_re2.match(line): continue f.write(line) logger.success('{0} uninstalled succesfully', self.name) else: logger.info('{0} has not been uninstalled', self.name)
def upgrade(self, package_name, json, version): ''' Upgrade a package to the most recent version. ''' logger.info('Removing {0} old version', package_name) self.remove_files(package_name) args_manager['install']['upgrade'] = True logger.info('Upgrading {0} to {1}', package_name, version) logger.indent += 8 for release in json['urls']: logger.info('Installing {0}...', release['filename']) logger.indent += 4 try: Installer.from_url(release['url']) break except Exception as e: try: msg = e.args[0] except IndexError: msg = repr(e) logger.error('Error: An error occurred while installing {0}: {1}', package_name, msg) logger.info('Trying another file...') logger.indent -= 4 else: logger.warn('Error: Did not find any installable release on PyPI for {0}', package_name) try: Requirement('{0}=={1}'.format(package_name, version))._install_from_links(args_manager['install']['packages_url']) except Exception as e: logger.fatal('Error: {0}', e, exc=InstallationError) logger.info('Restoring uninstalled files') self.restore_files(package_name) logger.indent = 0
def print_output(output, cmd): '''Print to sys.stderr the complete output of a failed command''' logger.info('Complete output from command `{0}`:', cmd) logger.indent += 8 for line in output.splitlines(): logger.error(line) logger.indent -= 8
def link(path): path = os.path.abspath(path) if not os.path.exists(path): logger.error('{0} does not exist', path) if not os.path.exists(PYG_LINKS): if not os.path.exists(os.path.dirname(PYG_LINKS)): os.makedirs(os.path.dirname(PYG_LINKS)) open(PYG_LINKS, 'w').close() path = os.path.abspath(path) logger.info('Linking {0} in {1}...', path, PYG_LINKS) if path in open(PYG_LINKS, 'r').read(): logger.warn('{0} is already linked, exiting now...', path) with open(PYG_LINKS, 'a') as f: f.write(path) f.write('\n')
def unpack(path, dest=None): ''' Unpack the specified archive into the same directory or a specified destination. ''' path = os.path.abspath(path) d, n = os.path.split(path) e = ext(n) if e in ('.egg', '.zip'): arch = ZipFile(path) elif e in ('.tar', '.tar.gz', '.tar.bz2'): mode = 'r' if e == '.tar' else 'r:' + e.split('.')[2] arch = tarfile.open(path, mode=mode) else: logger.error('Unknown extension: {0}', e, exc=TypeError) arch.extractall(dest or d)
def install_func(packname, req_file, editable, ignore, yes): check_and_exit() if editable: if len(packname) > 1: logger.error('Error: Unable to install multiple packages in editable mode') return package = packname[0] if os.path.exists(os.path.abspath(package)): package = 'dir+{0}#egg={1}'.format(os.path.abspath(package), os.path.basename(package)) return vcs(package).develop() if req_file: logger.info('Installing from requirements file') for rq in req_file: Installer.from_req_file(os.path.abspath(rq)) return if packname: for package in packname: _install_package_from_name(package, ignore)
def _download_and_install(self, url, filename, packname, e, hash=None): fobj = download(url, 'Downloading {0}'.format(self.name)) if hash is not None: logger.info('Checking md5 sum') if md5(fobj.getvalue()).hexdigest() != hash: logger.fatal('Error: {0} appears to be corrupted', self.name) return if e in ('.tar.gz', '.tar.bz2', '.zip'): installer = Archive(fobj, e, packname, self.reqset) elif e == '.egg': installer = Egg(fobj, filename, self.reqset, packname) elif is_windows() and e in WINDOWS_EXT: installer = Binary(fobj, e, packname) else: logger.error('Error: unknown filetype: {0}', e, exc=InstallationError) ## There is no need to catch exceptions now, this will be done by `pyg.inst.Installer.install` installer.install() self.success = True
def _install_deps(rs, name=None, updater=None): if not rs: return if args_manager['install']['no_deps']: logger.info('Skipping dependencies for {0}', name) logger.indent = 8 for req in rs: logger.info(req) logger.indent = 0 return logger.info('Installing dependencies...') dep_error = False newly_installed = [] for req in rs: if is_installed(req) and not args_manager['install']['upgrade_all']: logger.indent = 8 logger.info('{0} is already installed, use -A, --upgrade-all to upgrade dependencies', req) continue logger.indent = 0 logger.info('Installing {0} (from {1})', req, rs.comes_from) logger.indent = 8 try: Installer(req).install() newly_installed.append(req) except AlreadyInstalled: continue except InstallationError: dep_error = True logger.error('Error: {0} has not been installed correctly', req) continue logger.indent = 0 if dep_error: if updater: for req in newly_installed: updater.restore_files(req) updater.remove_files(rs.comes_from.name) updater.restore_files(rs.comes_from.name) logger.error("{0}'s dependencies installation failed", rs.comes_from.name, exc=InstallationError) else: logger.success('Finished installing dependencies for {0}', rs.comes_from.name)
def _find_develop(self, dir, req): try: logger.info('Looking for a local package...') try: dist = Develop(req.name) except (ValueError, AttributeError): logger.error('Cannot find a local distribution for {0}', req.name, exc=PygError) else: # XXX: Add a `location` attribute in pkgtools' next release location = os.path.abspath(dist._arg_name) path = os.path.dirname(location) if not req.match(Version(dist.version)): logger.error('Found {0}, but it does not match the requirement', path, exc=PygError) setup_py = os.path.join(path, 'setup.py') if not os.path.exists(setup_py): logger.error('Cannot find setup.py for {0}', req.name, exc=PygError) logger.info('Found a matching package in {0}', path) with TempDir() as tempdir: code, output = call_setup(path, ['sdist', '-d', tempdir]) if code != 0: logger.fatal('setup.py failed to create the source distribution') print_output(output, 'setup.py sdist') raise PygError arch = glob.glob(os.path.join(tempdir, '*{0}*'.format(req.name)))[0] shutil.move(arch, dir) arch_name = os.path.join(dir, os.path.basename(arch)) unpack(arch_name) return arch_name except (PygError, IndexError, ValueError): return False
def _install_from_links(self, package_index): ## Monkey-patch for pyg.inst.Updater: ## it does not know the real index url! logger.info('Looking for links on {0}', package_index) try: links = get_links(self, package_index) if not links: raise InstallationError('Error: did not find any files') except Exception as e: raise InstallationError(str(e)) logger.indent = 8 for url in links: filename = urlparse.urlparse(url).path.split('/')[-1] logger.info('Found: {0}', filename) try: self._download_and_install(url, filename, self.name) except Exception as e: logger.error('Error: {0}', e) continue break logger.indent = 0 if not self.success: raise InstallationError('Fatal: cannot install {0}'.format(self.name))