def display(self): """Log a pretty display of the depgraph with marks if any""" LOG.info('DepGraph with %d nodes:-', len(self.nodes)) LOG.info('Nodes:') LOG.info(wrapped(', '.join(self.nodes.keys()), prefix='\t')) LOG.info('Edges:') for n1, v in self.edges.items(): n1_once = n1 for n2, rl in v.items(): LOG.info(wrapped('{0:18} <- {1:18} [{2}]'.format( n1_once, n2, ', '.join([str(r) for r in rl])), prefix='\t')) n1_once = ' ' * len(n1_once) LOG.info('Marks:') marks = self.get_marks() for pkg in marks['install']: LOG.info('\t[+] %s', pkg.full_name) for pkg in marks['remove']: LOG.info('\t[-] %s', pkg.full_name) for p1, p2 in marks['change']: LOG.info('\t[c] %s -> %s', p1.full_name, p2.full_name) if p1.version_key > p2.version_key: # show reasonf for downgrade because = 'because ' for name0, rl in self.edges[p1.name].items(): for r in rl: LOG.info(wrapped('\t {0}{1} requires {2}'.format( because, name0, r))) because = ' ' * len(because) # reset
def display(self): """Log a pretty display of the depgraph with marks if any""" LOG.info('DepGraph with %d nodes:-', len(self.nodes)) LOG.info('Nodes:') LOG.info(wrapped(', '.join(self.nodes.keys()), prefix='\t')) LOG.info('Edges:') for n1, v in self.edges.items(): n1_once = n1 for n2, rl in v.items(): LOG.info( wrapped('{0:18} <- {1:18} [{2}]'.format( n1_once, n2, ', '.join([str(r) for r in rl])), prefix='\t')) n1_once = ' ' * len(n1_once) LOG.info('Marks:') marks = self.get_marks() for pkg in marks['install']: LOG.info('\t[+] %s', pkg.full_name) for pkg in marks['remove']: LOG.info('\t[-] %s', pkg.full_name) for p1, p2 in marks['change']: LOG.info('\t[c] %s -> %s', p1.full_name, p2.full_name) if p1.version_key > p2.version_key: # show reasonf for downgrade because = 'because ' for name0, rl in self.edges[p1.name].items(): for r in rl: LOG.info( wrapped('\t {0}{1} requires {2}'.format( because, name0, r))) because = ' ' * len(because) # reset
def download_package(self, package): assert type(package) is RepoPackage sh.mkdirs(DOWNLOAD_CACHE) auth = licensing.get_be_license_auth() send_license = package.requires_be_license license_installed = auth is not None # A license is required for this package, but no license is installed if not license_installed and send_license: msg = '\n'.join([ wrapped('If you have purchased ActivePython Business Edition, ' 'please login to your account at:'), ' https://account.activestate.com/', wrapped('and download and run the license installer for your ' 'platform.'), '', wrapped('Please visit <%s> to learn more about the ' 'ActivePython Business Edition offering.' % \ licensing.BE_HOME_PAGE)]) raise error.PackageAccessError( package, 'requires Business Edition subscription', msg) try: # At this point, the user is already known to have a BE license file_location, _ = net.download_file( package.download_url, DOWNLOAD_CACHE, dict( auth=auth, use_cache=True, # XXX: this introduces network delay # (If-None-Match) despite having the file # in download cache # TODO: abstract client.store...autosync save_properties=True, start_info='{{status}}: [{0}] {1} {2}'.format( six.moves.urlparse(package.download_url).netloc, package.name, package.printable_version)), interactive=self.pypmenv.options['interactive']) except six.moves.HTTPError as e: reason = str(e) LOG.debug("HTTPError while accessing URL: %s -- reason: %s", package.download_url, reason) if send_license and e.code in (401, 402, 403): msg = wrapped( 'Your ActivePython Business Edition subscription seems to ' 'have expired. Please visit your account at ' 'https://account.activestate.com/ to renew your subscription.' ) else: msg = '' raise error.PackageAccessError(package, reason, msg) return file_location
def download_package(self, package): assert type(package) is RepoPackage sh.mkdirs(DOWNLOAD_CACHE) LOG.info('Get: [%s] %s %s', six.moves.urlparse(package.download_url).netloc, package.name, package.printable_version) auth = licensing.get_be_license_auth() send_license = package.requires_be_license license_installed = auth is not None # A license is required for this package, but no license is installed if not license_installed and send_license: msg = '\n'.join([ wrapped('If you have purchased ActivePython Business Edition, ' 'please login to your account at:'), ' https://account.activestate.com/', wrapped('and download and run the license installer for your ' 'platform.'), '', wrapped('Please visit <%s> to learn more about the ' 'ActivePython Business Edition offering.' % \ licensing.BE_HOME_PAGE)]) raise error.PackageAccessError( package, 'requires Business Edition subscription', msg) try: # At this point, the user is already known to have a BE license file_location = net.download_file( package.download_url, DOWNLOAD_CACHE, dict(auth=auth), interactive=self.pypmenv.options['interactive']) except six.moves.HTTPError as e: reason = str(e) LOG.debug("HTTPError while accessing URL: %s -- reason: %s", package.download_url, reason) if send_license and e.code in (401, 402, 403): msg = wrapped( 'Your ActivePython Business Edition subscription seems to ' 'have expired. Please visit your account at ' 'https://account.activestate.com/ to renew your subscription.' ) else: msg = '' raise error.PackageAccessError(package, reason, msg) return file_location
def __init__(self, requirement, required_by=None): self.requirement = requirement self.required_by = required_by msg = 'no such package found for requirement "%s"' % requirement if required_by: msg += '; required by "%s"' % required_by msg += "; try PyPM Index: http://code.activestate.com/pypm/search:%s/" % requirement super(PackageNotFound, self).__init__(wrapped(msg))
def mark_show(verb, list, item_print_func): if not list: return messages.append( 'The following packages will be {verb} {0}:\n{1}'.format( self.pypmenv.pyenv.printable_location, wrapped(' '.join(item_print_func(item) for item in list), prefix=' ', break_on_hyphens=False), verb=verb))
def _pep370_fix_path_unix(): """If ~/.local/bin is not in $PATH, automatically add them Do this only with the user's consent. And do not run this check more than once (i.e., run only when PyPM is *first run*). """ if sys.platform.startswith('win'): return # MSI does this on Windows # Proceed only when the terminal is interactive and was never run before isatty = (sys.stdin.isatty() and sys.stdout.isatty()) firstrun_file = P.join(application.locations.user_cache_dir, '.firstrun-pep370') if (not isatty) or P.exists(firstrun_file): return import site from datetime import datetime pathenv = [ P.abspath(x.strip()) for x in os.environ.get('PATH', '').split(':') ] binpath = P.abspath(P.join(site.USER_BASE, 'bin')) profile = P.expanduser('~/.profile' if sys.platform == 'darwin' else '~/.bashrc') profile_lines = [ '# PEP 370 PATH added by PyPM on %s' % datetime.now(), 'export PATH=%s:$PATH' % binpath, ] already_in_profile = P.exists(profile) and profile_lines[1] in [ l.strip() for l in open(profile).readlines() ] # Proceed only if ~/.local/bin is neither in $PATH, nor added to profile if binpath in pathenv or already_in_profile: return # Add to profile on the user's consent msg = ('Packages will install their script files to "%s" ' '(as per PEP 370). This directory is not yet in your $PATH. ' 'Would you like PyPM to add it by appending to "%s"?') % ( concise_path(binpath), concise_path(profile)) if textui.askyesno(wrapped(msg, '*** '), default=True): if P.exists(profile): sh.cp(profile, profile + '.bak') # take a backup first with open(profile, 'a') as f: f.write('\n%s\n' % '\n'.join(profile_lines)) print('You may now reopen your shell for the changes to take effect.') sh.mkdirs(P.dirname(firstrun_file)) with open(firstrun_file, 'w') as f: pass # prevent future runs
def _pep370_fix_path_unix(): """If ~/.local/bin is not in $PATH, automatically add them Do this only with the user's consent. And do not run this check more than once (i.e., run only when PyPM is *first run*). """ if sys.platform.startswith('win'): return # MSI does this on Windows # Proceed only when the terminal is interactive and was never run before isatty = (sys.stdin.isatty() and sys.stdout.isatty()) firstrun_file = P.join(application.locations.user_cache_dir, '.firstrun-pep370') if (not isatty) or P.exists(firstrun_file): return import site from datetime import datetime pathenv = [P.abspath(x.strip()) for x in os.environ.get('PATH', '').split(':')] binpath = P.abspath(P.join(site.USER_BASE, 'bin')) profile = P.expanduser('~/.profile' if sys.platform == 'darwin' else '~/.bashrc') profile_lines = [ '# PEP 370 PATH added by PyPM on %s' % datetime.now(), 'export PATH=%s:$PATH' % binpath, ] already_in_profile = P.exists(profile) and profile_lines[1] in [ l.strip() for l in open(profile).readlines() ] # Proceed only if ~/.local/bin is neither in $PATH, nor added to profile if binpath in pathenv or already_in_profile: return # Add to profile on the user's consent msg = ( 'Packages install script files to "%s" ' '(as per PEP 370). This directory is not yet in your $PATH. ' 'Would you like PyPM to add it to $PATH by modifying (with backup) "%s"?' ) % (binpath, profile) if textui.askyesno(wrapped(msg, '*** '), default=True): if P.exists(profile): sh.cp(profile, profile+'.bak') # take a backup first with open(profile, 'a') as f: f.write('\n%s\n' % '\n'.join(profile_lines)) print ('You may now reopen your shell for the changes to take effect.') sh.mkdirs(P.dirname(firstrun_file)) with open(firstrun_file, 'w') as f: pass # prevent future runs
def __init__(self, name, version): msg = 'package "{0}{1}" is not installed'.format( name, '-'+version if version else '') super(NoPackageInstalled, self).__init__(wrapped(msg))
def __init__(self, requirement, required_by=None): msg = 'no such package "%s"; see pypm.activestate.com' % requirement if required_by: msg += '; required by "%s"' % required_by super(PackageNotFound, self).__init__(wrapped(msg))
def do_show(self, subcmd, opts, name, version=None): """${cmd_name}: Display detailed information about a package If the package is already installed, show the location of the site-packages directory under which it is installed. ${cmd_usage} ${cmd_option_list} """ with self.bootstrapped(): self._autosync() pkg = self.pypmenv.repo_store.find_package(name, version) dependencies = pkg.install_requires[''] extra_dependencies = [] for extra in pkg.install_requires: if extra == '': continue extra_dependencies.append('`pypm install {0}[{1}]` ->\n {2}'.format( pkg.name, extra, ',\n '.join(pkg.install_requires[extra]))) # Show package metadata LOG.info('Name: %s', pkg.name) LOG.info('Latest version: %s', pkg.printable_version) if pkg.author or pkg.author_email: LOG.info('Author: %s %s', pkg.author, pkg.author_email and '<%s>' % pkg.author_email or '') LOG.info('Summary: %s', pkg.summary) if pkg.home_page: LOG.info('Home Page: %s', pkg.home_page) if pkg.license: LOG.info('License: %s', pkg.license) # Show package dependencies if dependencies: LOG.info('Dependencies:') for dep in dependencies: LOG.info(' %s', dep) if extra_dependencies: LOG.info('Optional dependencies:') for ed in extra_dependencies: LOG.info(' %s', ed) # Optionally, show packages depending on it if opts.rdepends: LOG.info('Depended by:') rdependencies = {} for rpkg in self.pypmenv.repo_store._query(): for req in rpkg.install_requires['']: req = Requirement.parse(req) if pkg.name == req_name(req): if pkg.version in req: prev_rpkg = rdependencies.get(rpkg.name, None) if (not prev_rpkg) or ( rpkg.version_key > prev_rpkg.version_key): rdependencies[rpkg.name] = rpkg if rdependencies: LOG.info(wrapped( ', '.join(sorted( [p.full_name for p in rdependencies.values()])), prefix=' ', break_on_hyphens=False)) # Show list of (older) versions available in the repository if not version: pkglist = self.pypmenv.repo_store.find_package_releases(name) LOG.info('Available versions: %s', ', '.join( [p.printable_version for p in pkglist])) # Status: is this package installed or not? Does it require BE? try: ipkg = self.pypmenv.installed_store.find_only_package(name) except error.NoPackageInstalled: LOG.info('Status: Not installed') if pkg.requires_be_license and not licensing.user_has_be_license(): LOG.info( 'NOTE: This package requires a valid Business Edition ' 'license. Please visit %s for more details.', licensing.BE_HOME_PAGE) else: # Is this package installed because another package? depgraph = installer.PyPMDepGraph(self.pypmenv) required_by = depgraph.edges[ipkg.name].items() if required_by: LOG.info('Required by:') for rpkg, rl in required_by: LOG.info(' %s [%s]', rpkg, req2str(*rl)) LOG.info('Status: Already installed (%s) at %s', ipkg.printable_version, self.pypmenv.pyenv.printable_location) # Is a newer version available for upgrade? if ipkg.version_key < pkg.version_key: # TODO: Need --force LOG.info('Status: ' 'A newer version (%s) is available. ' 'Type "pypm install %s" to upgrade', pkg.printable_version, pkg.name) # Show postinstall notes for this package for note in pkg.get_notes(postinstall=True): LOG.info('***NOTE***: %s', note['content'].strip()) if opts.open_home_page: import webbrowser u = 'http://code.activestate.com/pypm/{0}/'.format(pkg.name) webbrowser.open(pkg.home_page or u) return pkg
def _extract_to_install_scheme(self, bpkgfile, name): pyenv = self.pypmenv.pyenv # Install scheme used by the build environment (i.e., pyenv used by # pypm-builder on our build machines). as_build_scheme = { 'win': { 'purelib': 'lib/site-packages', 'stdlib': 'lib', 'scripts': 'scripts', }, 'unix': { 'purelib': 'lib/python{0}/site-packages'.format(pyenv.pyver), 'stdlib': 'lib/python{0}'.format(pyenv.pyver), 'scripts': 'bin', }, } plat = PLATNAME.startswith('win') and 'win' or 'unix' # Scheme used by pyenv pyenv_scheme = { 'purelib': self._pyenv_scheme_path('purelib'), 'stdlib': self._pyenv_scheme_path('stdlib'), 'scripts': self._pyenv_scheme_path('scripts'), } files_to_overwrite = [] force_overwrite = self.pypmenv.options['force'] # Hack #1: Don't check for distribute and pip, as virtualenvs usually # already have a copy of them installed. if name in ('distribute', 'setuptools', 'pip'): force_overwrite = True with bpkgfile.extract_over2(pyenv.base_dir) as tf: for tinfo in tf.getmembers(): # Replace AS build virtualenv scheme with the user's scheme # Eg: lib/site-packages/XYZ -> %APPDATA%/Python/Python26/XYZ for name, prefix in as_build_scheme[plat].items(): if tinfo.name.lower().startswith(prefix): old = tinfo.name new = pyenv_scheme[name] + old[len(prefix):] if new != old: LOG.debug('fs:extract: transforming "%s" to "%s"', old, new) tinfo.name = new # Check for overwrites if os.path.lexists( tinfo.name) and not os.path.isdir(tinfo.name): # Hack #2: allow overwriting of *.pth files (setuptools # hackishness) eg: [...]/site-packages/setuptools.pth if not tinfo.name.endswith('.pth'): files_to_overwrite.append(tinfo.name) if files_to_overwrite: LOG.debug( 'install requires overwriting of %d files:\n%s', len(files_to_overwrite), '\n'.join([ os.path.join(pyenv.base_dir, f) for f in files_to_overwrite ])) if force_overwrite: LOG.warn('overwriting %d files' % len(files_to_overwrite)) else: errmsg = [ 'cannot overwrite "%s"' % concise_path( os.path.join(pyenv.base_dir, files_to_overwrite[0])) ] if len(files_to_overwrite) > 1: errmsg.append(' (and %d other files)' % (len(files_to_overwrite) - 1, )) errmsg.append( '; run pypm as "pypm --force ..." to overwrite anyway') if len(files_to_overwrite) > 1: errmsg.append( '; run "pypm log" to see the full list of files to be overwritten' ) raise IOError(wrapped(''.join(errmsg))) return tf.getnames()
def _extract_to_install_scheme(self, bpkgfile, name): pyenv = self.pypmenv.pyenv # Install scheme used by the build environment (i.e., pyenv used by # pypm-builder on our build machines). as_build_scheme = { 'win': { 'purelib': 'lib/site-packages', 'stdlib': 'lib', 'scripts': 'scripts', }, 'unix': { 'purelib': 'lib/python{0}/site-packages'.format(pyenv.pyver), 'stdlib': 'lib/python{0}'.format(pyenv.pyver), 'scripts': 'bin', }, } plat = PLATNAME.startswith('win') and 'win' or 'unix' # Scheme used by pyenv pyenv_scheme = { 'purelib': self._pyenv_scheme_path('purelib'), 'stdlib': self._pyenv_scheme_path('stdlib'), 'scripts': self._pyenv_scheme_path('scripts'), } files_to_overwrite = [] force_overwrite = self.pypmenv.options['force'] # Hack #1: Don't check for distribute and pip, as virtualenvs usually # already have a copy of them installed. if name in ('distribute', 'setuptools', 'pip'): force_overwrite = True with bpkgfile.extract_over2(pyenv.base_dir) as tf: for tinfo in tf.getmembers(): # Replace AS build virtualenv scheme with the user's scheme # Eg: lib/site-packages/XYZ -> %APPDATA%/Python/Python26/XYZ for name, prefix in as_build_scheme[plat].items(): if tinfo.name.lower().startswith(prefix): old = tinfo.name new = pyenv_scheme[name] + old[len(prefix):] if new != old: LOG.debug('fs:extract: transforming "%s" to "%s"', old, new) tinfo.name = new # Check for overwrites if os.path.exists(tinfo.name) and not os.path.isdir(tinfo.name): # Hack #2: allow overwriting of *.pth files (setuptools # hackishness) eg: [...]/site-packages/setuptools.pth if not tinfo.name.endswith('.pth'): files_to_overwrite.append(tinfo.name) if files_to_overwrite: LOG.debug( 'install requires overwriting of %d files:\n%s', len(files_to_overwrite), '\n'.join([os.path.join(pyenv.base_dir, f) for f in files_to_overwrite])) if force_overwrite: LOG.warn('overwriting %d files' % len(files_to_overwrite)) else: errmsg = ['cannot overwrite "%s"' % concise_path(os.path.join( pyenv.base_dir, files_to_overwrite[0]))] if len(files_to_overwrite) > 1: errmsg.append(' (and %d other files)' % (len(files_to_overwrite)-1,)) errmsg.append('; run pypm as "pypm --force ..." to overwrite anyway') if len(files_to_overwrite) > 1: errmsg.append('; run "pypm log" to see the full list of files to be overwritten') raise IOError(wrapped(''.join(errmsg))) return tf.getnames()