class Yolk(object): """ Main class for yolk """ def __init__(self): #PyPI project name with proper case self.project_name = "" #PyPI project version self.version = "" #List of all versions not hidden on PyPI self.all_versions = [] self.pkg_spec = [] self.options = None self.logger = logging.getLogger("yolk") #Squelch output from setuptools #Add future offenders to this list. shut_up = ['distutils.log'] sys.stdout = StdOut(sys.stdout, shut_up) sys.stderr = StdOut(sys.stderr, shut_up) self.pypi = None def get_plugin(self, method): """ Return plugin object if CLI option is activated and method exists @param method: name of plugin's method we're calling @type method: string @returns: list of plugins with `method` """ all_plugins = [] for entry_point in pkg_resources.iter_entry_points('yolk.plugins'): plugin_obj = entry_point.load() plugin = plugin_obj() plugin.configure(self.options, None) if plugin.enabled: if not hasattr(plugin, method): self.logger.warn("Error: plugin has no method: %s" % method) plugin = None else: all_plugins.append(plugin) return all_plugins def set_log_level(self): """ Set log level according to command-line options @returns: logger object """ if self.options.debug: self.logger.setLevel(logging.DEBUG) elif self.options.quiet: self.logger.setLevel(logging.ERROR) else: self.logger.setLevel(logging.INFO) self.logger.addHandler(logging.StreamHandler()) return self.logger def run(self): """ Perform actions based on CLI options @returns: status code """ opt_parser = setup_opt_parser() (self.options, remaining_args) = opt_parser.parse_args() logger = self.set_log_level() pkg_spec = validate_pypi_opts(opt_parser) if not pkg_spec: pkg_spec = remaining_args self.pkg_spec = pkg_spec if not self.options.pypi_search and (len(sys.argv) == 1 or\ len(remaining_args) > 2): opt_parser.print_help() return 2 #Options that depend on querying installed packages, not PyPI. #We find the proper case for package names if they are installed, #otherwise PyPI returns the correct case. if self.options.show_deps or self.options.show_all or \ self.options.show_active or self.options.show_non_active or \ (self.options.show_updates and pkg_spec): want_installed = True else: want_installed = False #show_updates may or may not have a pkg_spec if not want_installed or self.options.show_updates: self.pypi = CheeseShop(self.options.debug) #XXX: We should return 2 here if we couldn't create xmlrpc server if pkg_spec: (self.project_name, self.version, self.all_versions) = \ self.parse_pkg_ver(want_installed) if want_installed and not self.project_name: logger.error("%s is not installed." % pkg_spec[0]) return 1 #I could prefix all these with 'cmd_' and the methods also #and then iterate over the `options` dictionary keys... commands = ['show_deps', 'query_metadata_pypi', 'fetch', 'versions_available', 'show_updates', 'browse_website', 'show_download_links', 'pypi_search', 'show_pypi_changelog', 'show_pypi_releases', 'yolk_version', 'show_all', 'show_active', 'show_non_active', 'show_entry_map', 'show_entry_points'] #Run first command it finds, and only the first command, then return #XXX: Check if more than one command was set in options and give error? for action in commands: if getattr(self.options, action): return getattr(self, action)() opt_parser.print_help() def show_active(self): """ Show installed active packages """ return self.show_distributions("active") def show_non_active(self): """ Show installed non-active packages """ return self.show_distributions("nonactive") def show_all(self): """ Show all installed packages """ return self.show_distributions("all") def show_updates(self): """ Check installed packages for available updates on PyPI @param project_name: optional package name to check; checks every installed pacakge if none specified @type project_name: string @returns: None """ dists = Distributions() if self.project_name: #Check for a single package pkg_list = [self.project_name] else: #Check for every installed package pkg_list = get_pkglist() found = None for pkg in pkg_list: for (dist, active) in dists.get_distributions("all", pkg, dists.get_highest_installed(pkg)): (project_name, versions) = \ self.pypi.query_versions_pypi(dist.project_name) if versions: #PyPI returns them in chronological order, #but who knows if its guaranteed in the API? #Make sure we grab the highest version: newest = get_highest_version(versions) if newest != dist.version: #We may have newer than what PyPI knows about if pkg_resources.parse_version(dist.version) < \ pkg_resources.parse_version(newest): found = True print " %s %s (%s)" % (project_name, dist.version, newest) if not found and self.project_name: self.logger.info("You have the latest version installed.") elif not found: self.logger.info("No newer packages found at The Cheese Shop") return 0 def show_distributions(self, show): """ Show list of installed activated OR non-activated packages @param show: type of pkgs to show (all, active or nonactive) @type show: string @returns: None or 2 if error """ show_metadata = self.options.metadata #Search for any plugins with active CLI options with add_column() method plugins = self.get_plugin("add_column") #Some locations show false positive for 'development' packages: ignores = ["/UNIONFS", "/KNOPPIX.IMG"] #Check if we're in a workingenv #See http://cheeseshop.python.org/pypi/workingenv.py workingenv = os.environ.get('WORKING_ENV') if workingenv: ignores.append(workingenv) dists = Distributions() results = None for (dist, active) in dists.get_distributions(show, self.project_name, self.version): metadata = get_metadata(dist) for prefix in ignores: if dist.location.startswith(prefix): dist.location = dist.location.replace(prefix, "") #Case-insensitve search because of Windows if dist.location.lower().startswith(get_python_lib().lower()): develop = "" else: develop = dist.location if metadata: add_column_text = "" for my_plugin in plugins: #See if package is 'owned' by a package manager such as #portage, apt, rpm etc. #add_column_text += my_plugin.add_column(filename) + " " add_column_text += my_plugin.add_column(dist) + " " self.print_metadata(metadata, develop, active, add_column_text) else: print str(dist) + " has no metadata" results = True if not results and self.project_name: if self.version: pkg_spec = "%s==%s" % (self.project_name, self.version) else: pkg_spec = "%s" % self.project_name if show == "all": self.logger.error("There are no versions of %s installed." \ % pkg_spec) else: self.logger.error("There are no %s versions of %s installed." \ % \ (show, pkg_spec)) return 2 elif show == "all" and results and self.options.fields: print "Versions with '*' are non-active." print "Versions with '!' are deployed in development mode." def print_metadata(self, metadata, develop, active, installed_by): """ Print out formatted metadata @param metadata: package's metadata @type metadata: pkg_resources Distribution obj @param develop: path to pkg if its deployed in development mode @type develop: string @param active: show if package is activated or not @type active: boolean @param installed_by: Shows if pkg was installed by a package manager other than setuptools @type installed_by: string @returns: None """ show_metadata = self.options.metadata if self.options.fields: fields = self.options.fields.split(',') fields = map(str.strip, fields) else: fields = [] version = metadata['Version'] #When showing all packages, note which are not active: if active: if fields: active_status = "" else: active_status = "active" else: if fields: active_status = "*" else: active_status = "non-active" if develop: if fields: development_status = "! (%s)" % develop else: development_status = "development (%s)" % develop else: development_status = installed_by status = "%s %s" % (active_status, development_status) if fields: print '%s (%s)%s %s' % (metadata['Name'], version, active_status, development_status) else: # Need intelligent justification print metadata['Name'].ljust(15) + " - " + version.ljust(12) + \ " - " + status if fields: #Only show specific fields, using case-insensitive search fields = map(str.lower, fields) for field in metadata.keys(): if field.lower() in fields: print ' %s: %s' % (field, metadata[field]) print elif show_metadata: #Print all available metadata fields for field in metadata.keys(): if field != 'Name' and field != 'Summary': print ' %s: %s' % (field, metadata[field]) def show_deps(self): """ Show dependencies for package(s) @returns: 0 - sucess 1 - No dependency info supplied """ pkgs = pkg_resources.Environment() for pkg in pkgs[self.project_name]: if not self.version: print pkg.project_name, pkg.version i = len(pkg._dep_map.values()[0]) if i: while i: if not self.version or self.version and \ pkg.version == self.version: if self.version and i == len(pkg._dep_map.values()[0]): print pkg.project_name, pkg.version print " " + str(pkg._dep_map.values()[0][i - 1]) i -= 1 else: self.logger.info(\ "No dependency information was supplied with the package.") return 1 return 0 def show_pypi_changelog(self): """ Show detailed PyPI ChangeLog for the last `hours` @returns: 0 = sucess or 1 if failed to retrieve from XML-RPC server """ hours = self.options.show_pypi_changelog if not hours.isdigit(): self.logger.error("Error: You must supply an integer.") return 1 try: changelog = self.pypi.changelog(int(hours)) except XMLRPCFault, err_msg: self.logger.error(err_msg) self.logger.error("ERROR: Couldn't retrieve changelog.") return 1 last_pkg = '' for entry in changelog: pkg = entry[0] if pkg != last_pkg: print "%s %s\n\t%s" % (entry[0], entry[1], entry[3]) last_pkg = pkg else: print "\t%s" % entry[3] return 0
class Yolk(object): """ Main class for yolk """ def __init__(self): #PyPI project name with proper case self.project_name = "" #PyPI project version self.version = "" #List of all versions not hidden on PyPI self.all_versions = [] self.pkg_spec = [] self.options = None self.logger = logging.getLogger("yolk") #Squelch output from setuptools #Add future offenders to this list. shut_up = ['distutils.log'] sys.stdout = StdOut(sys.stdout, shut_up) sys.stderr = StdOut(sys.stderr, shut_up) self.pypi = None def get_plugin(self, method): """ Return plugin object if CLI option is activated and method exists @param method: name of plugin's method we're calling @type method: string @returns: list of plugins with `method` """ all_plugins = [] for entry_point in pkg_resources.iter_entry_points('yolk.plugins'): plugin_obj = entry_point.load() plugin = plugin_obj() plugin.configure(self.options, None) if plugin.enabled: if not hasattr(plugin, method): self.logger.warn("Error: plugin has no method: %s" % method) plugin = None else: all_plugins.append(plugin) return all_plugins def set_log_level(self): """ Set log level according to command-line options @returns: logger object """ if self.options.debug: self.logger.setLevel(logging.DEBUG) elif self.options.quiet: self.logger.setLevel(logging.ERROR) else: self.logger.setLevel(logging.INFO) self.logger.addHandler(logging.StreamHandler()) return self.logger def run(self): """ Perform actions based on CLI options @returns: status code """ opt_parser = setup_opt_parser() (self.options, remaining_args) = opt_parser.parse_args() logger = self.set_log_level() pkg_spec = validate_pypi_opts(opt_parser) if not pkg_spec: pkg_spec = remaining_args self.pkg_spec = pkg_spec if not self.options.pypi_search and (len(sys.argv) == 1 or\ len(remaining_args) > 2): opt_parser.print_help() return 2 #Options that depend on querying installed packages, not PyPI. #We find the proper case for package names if they are installed, #otherwise PyPI returns the correct case. if self.options.show_deps or self.options.show_all or \ self.options.show_active or self.options.show_non_active or \ (self.options.show_updates and pkg_spec): want_installed = True else: want_installed = False #show_updates may or may not have a pkg_spec if not want_installed or self.options.show_updates: self.pypi = CheeseShop(self.options.debug) #XXX: We should return 2 here if we couldn't create xmlrpc server if pkg_spec: (self.project_name, self.version, self.all_versions) = \ self.parse_pkg_ver(want_installed) if want_installed and not self.project_name: logger.error("%s is not installed." % pkg_spec[0]) return 1 #I could prefix all these with 'cmd_' and the methods also #and then iterate over the `options` dictionary keys... commands = [ 'show_deps', 'query_metadata_pypi', 'fetch', 'versions_available', 'show_updates', 'browse_website', 'show_download_links', 'pypi_search', 'show_pypi_changelog', 'show_pypi_releases', 'yolk_version', 'show_all', 'show_active', 'show_non_active', 'show_entry_map', 'show_entry_points' ] #Run first command it finds, and only the first command, then return #XXX: Check if more than one command was set in options and give error? for action in commands: if getattr(self.options, action): return getattr(self, action)() opt_parser.print_help() def show_active(self): """ Show installed active packages """ return self.show_distributions("active") def show_non_active(self): """ Show installed non-active packages """ return self.show_distributions("nonactive") def show_all(self): """ Show all installed packages """ return self.show_distributions("all") def show_updates(self): """ Check installed packages for available updates on PyPI @param project_name: optional package name to check; checks every installed pacakge if none specified @type project_name: string @returns: None """ dists = Distributions() if self.project_name: #Check for a single package pkg_list = [self.project_name] else: #Check for every installed package pkg_list = get_pkglist() found = None for pkg in pkg_list: for (dist, active) in dists.get_distributions( "all", pkg, dists.get_highest_installed(pkg)): (project_name, versions) = \ self.pypi.query_versions_pypi(dist.project_name) if versions: #PyPI returns them in chronological order, #but who knows if its guaranteed in the API? #Make sure we grab the highest version: newest = get_highest_version(versions) if newest != dist.version: #We may have newer than what PyPI knows about if pkg_resources.parse_version(dist.version) < \ pkg_resources.parse_version(newest): found = True print " %s %s (%s)" % (project_name, dist.version, newest) if not found and self.project_name: self.logger.info("You have the latest version installed.") elif not found: self.logger.info("No newer packages found at The Cheese Shop") return 0 def show_distributions(self, show): """ Show list of installed activated OR non-activated packages @param show: type of pkgs to show (all, active or nonactive) @type show: string @returns: None or 2 if error """ show_metadata = self.options.metadata #Search for any plugins with active CLI options with add_column() method plugins = self.get_plugin("add_column") #Some locations show false positive for 'development' packages: ignores = ["/UNIONFS", "/KNOPPIX.IMG"] #Check if we're in a workingenv #See http://cheeseshop.python.org/pypi/workingenv.py workingenv = os.environ.get('WORKING_ENV') if workingenv: ignores.append(workingenv) dists = Distributions() results = None for (dist, active) in dists.get_distributions(show, self.project_name, self.version): metadata = get_metadata(dist) for prefix in ignores: if dist.location.startswith(prefix): dist.location = dist.location.replace(prefix, "") #Case-insensitve search because of Windows if dist.location.lower().startswith(get_python_lib().lower()): develop = "" else: develop = dist.location if metadata: add_column_text = "" for my_plugin in plugins: #See if package is 'owned' by a package manager such as #portage, apt, rpm etc. #add_column_text += my_plugin.add_column(filename) + " " add_column_text += my_plugin.add_column(dist) + " " self.print_metadata(metadata, develop, active, add_column_text) else: print str(dist) + " has no metadata" results = True if not results and self.project_name: if self.version: pkg_spec = "%s==%s" % (self.project_name, self.version) else: pkg_spec = "%s" % self.project_name if show == "all": self.logger.error("There are no versions of %s installed." \ % pkg_spec) else: self.logger.error("There are no %s versions of %s installed." \ % \ (show, pkg_spec)) return 2 elif show == "all" and results and self.options.fields: print "Versions with '*' are non-active." print "Versions with '!' are deployed in development mode." def print_metadata(self, metadata, develop, active, installed_by): """ Print out formatted metadata @param metadata: package's metadata @type metadata: pkg_resources Distribution obj @param develop: path to pkg if its deployed in development mode @type develop: string @param active: show if package is activated or not @type active: boolean @param installed_by: Shows if pkg was installed by a package manager other than setuptools @type installed_by: string @returns: None """ show_metadata = self.options.metadata if self.options.fields: fields = self.options.fields.split(',') fields = map(str.strip, fields) else: fields = [] version = metadata['Version'] #When showing all packages, note which are not active: if active: if fields: active_status = "" else: active_status = "active" else: if fields: active_status = "*" else: active_status = "non-active" if develop: if fields: development_status = "! (%s)" % develop else: development_status = "development (%s)" % develop else: development_status = installed_by status = "%s %s" % (active_status, development_status) if fields: print '%s (%s)%s %s' % (metadata['Name'], version, active_status, development_status) else: # Need intelligent justification print metadata['Name'].ljust(15) + " - " + version.ljust(12) + \ " - " + status if fields: #Only show specific fields, using case-insensitive search fields = map(str.lower, fields) for field in metadata.keys(): if field.lower() in fields: print ' %s: %s' % (field, metadata[field]) print elif show_metadata: #Print all available metadata fields for field in metadata.keys(): if field != 'Name' and field != 'Summary': print ' %s: %s' % (field, metadata[field]) def show_deps(self): """ Show dependencies for package(s) @returns: 0 - sucess 1 - No dependency info supplied """ pkgs = pkg_resources.Environment() for pkg in pkgs[self.project_name]: if not self.version: print pkg.project_name, pkg.version i = len(pkg._dep_map.values()[0]) if i: while i: if not self.version or self.version and \ pkg.version == self.version: if self.version and i == len(pkg._dep_map.values()[0]): print pkg.project_name, pkg.version print " " + str(pkg._dep_map.values()[0][i - 1]) i -= 1 else: self.logger.info(\ "No dependency information was supplied with the package.") return 1 return 0 def show_pypi_changelog(self): """ Show detailed PyPI ChangeLog for the last `hours` @returns: 0 = sucess or 1 if failed to retrieve from XML-RPC server """ hours = self.options.show_pypi_changelog if not hours.isdigit(): self.logger.error("Error: You must supply an integer.") return 1 try: changelog = self.pypi.changelog(int(hours)) except XMLRPCFault, err_msg: self.logger.error(err_msg) self.logger.error("ERROR: Couldn't retrieve changelog.") return 1 last_pkg = '' for entry in changelog: pkg = entry[0] if pkg != last_pkg: print "%s %s\n\t%s" % (entry[0], entry[1], entry[3]) last_pkg = pkg else: print "\t%s" % entry[3] return 0
class Yolk(object): """Main class for yolk.""" def __init__(self): # PyPI project name with proper case self.project_name = '' # PyPI project version self.version = '' # List of all versions not hidden on PyPI self.all_versions = [] self.pkg_spec = None self.options = None # Squelch output from setuptools # Add future offenders to this list. shut_up = ['distutils.log'] sys.stdout = StdOut(sys.stdout, shut_up) sys.stderr = StdOut(sys.stderr, shut_up) self.pypi = None def get_plugin(self, method): """Return plugin object if CLI option is activated and method exists. @param method: name of plugin's method we're calling @type method: string @returns: list of plugins with `method` """ all_plugins = [] for entry_point in pkg_resources.iter_entry_points('yolk.plugins'): plugin_obj = entry_point.load() plugin = plugin_obj() plugin.configure(self.options, None) if plugin.enabled: if not hasattr(plugin, method): plugin = None else: all_plugins.append(plugin) return all_plugins def run(self): """Perform actions based on CLI options. @returns: status code """ parser = setup_parser() try: import argcomplete argcomplete.autocomplete(parser) except ImportError: pass self.options = parser.parse_args() pkg_spec = validate_pypi_opts(parser) if not pkg_spec: pkg_spec = self.options.pkg_spec self.pkg_spec = pkg_spec if self.options.fields: self.options.fields = [ s.strip().lower() for s in self.options.fields.split(',') ] else: self.options.fields = [] if (not self.options.pypi_search and len(sys.argv) == 1): parser.print_help() return 2 # Options that depend on querying installed packages, not PyPI. # We find the proper case for package names if they are installed, # otherwise PyPI returns the correct case. if (self.options.show_deps or self.options.show_all or self.options.show_active or self.options.show_non_active or (self.options.show_updates and pkg_spec) or self.options.upgrade): want_installed = True else: want_installed = False # show_updates may or may not have a pkg_spec if (not want_installed or self.options.show_updates or self.options.upgrade): self.pypi = CheeseShop(self.options.debug) # XXX: We should return 2 here if we couldn't create xmlrpc server if pkg_spec: (self.project_name, self.version, self.all_versions) = self.parse_pkg_ver(want_installed) if want_installed and not self.project_name: print(u'{} is not installed'.format(pkg_spec), file=sys.stderr) return 1 # I could prefix all these with 'cmd_' and the methods also # and then iterate over the `options` dictionary keys... commands = [ 'show_deps', 'query_metadata_pypi', 'fetch', 'versions_available', 'show_updates', 'upgrade', 'browse_website', 'show_download_links', 'pypi_search', 'show_pypi_changelog', 'show_pypi_releases', 'yolk_version', 'show_all', 'show_active', 'show_non_active', 'show_entry_map', 'show_entry_points' ] # Run first command it finds, and only the first command, then return # XXX: Check if more than one command was set in options and give # error? for action in commands: if getattr(self.options, action): return getattr(self, action)() parser.print_help() def show_active(self): """Show installed active packages.""" return self.show_distributions('active') def show_non_active(self): """Show installed non-active packages.""" return self.show_distributions('nonactive') def show_all(self): """Show all installed packages.""" return self.show_distributions('all') def show_updates(self): """Check installed packages for available updates on PyPI. @param project_name: optional package name to check; checks every installed package if none specified @type project_name: string @returns: None """ if self.project_name: pkg_list = [self.project_name] else: pkg_list = get_pkglist() for (project_name, version, newest) in _updates(pkg_list, self.pypi, user_installs_only=self.options.user): print(u'{} {} ({})'.format(project_name, version, newest)) return 0 def upgrade(self): """Check installed packages for available updates on PyPI and upgrade. @param project_name: optional package name to check; checks every installed package if none specified @type project_name: string @returns: None """ if self.project_name: pkg_list = [self.project_name] else: pkg_list = get_pkglist() names = [ values[0] for values in _updates( pkg_list, self.pypi, user_installs_only=self.options.user) ] if names: subprocess.call( [sys.executable, '-m', 'pip', 'install', '--upgrade'] + (['--user'] if self.options.user else []) + names) return 0 def show_distributions(self, show): """Show list of installed activated OR non-activated packages. @param show: type of pkgs to show (all, active or nonactive) @type show: string @returns: None or 2 if error """ # Search for any plugins with active CLI options with add_column() # method. plugins = self.get_plugin('add_column') # Some locations show false positive for 'development' packages: ignores = ['/UNIONFS', '/KNOPPIX.IMG'] # See http://cheeseshop.python.org/pypi/workingenv.py for details. workingenv = os.environ.get('WORKING_ENV') if workingenv: ignores.append(workingenv) results = None for (dist, active) in yolklib.get_distributions(show, self.project_name, self.version): metadata = get_metadata(dist) for prefix in ignores: if dist.location.startswith(prefix): dist.location = dist.location.replace(prefix, '') # Case-insensitive search because of Windows. if dist.location.lower().startswith(get_python_lib().lower()): develop = '' else: develop = dist.location if metadata: add_column_text = '' for my_plugin in plugins: # See if package is 'owned' by a package manager such as # portage, apt, rpm etc. add_column_text += my_plugin.add_column(dist) + ' ' self.print_metadata(metadata, develop, active, add_column_text) else: print(str(dist) + ' has no metadata') results = True if not results and self.project_name: if self.version: pkg_spec = '{}=={}'.format(self.project_name, self.version) else: pkg_spec = self.project_name if show == 'all': print( u'There are no versions of {} installed'.format(pkg_spec), file=sys.stderr) else: print(u'There are no {} versions of {} installed'.format( show, pkg_spec), file=sys.stderr) return 2 elif show == 'all' and results and self.options.fields: print("Versions with '*' are non-active.") print("Versions with '!' are deployed in development mode.") def print_metadata(self, metadata, develop, active, installed_by): """Print out formatted metadata. @param metadata: package's metadata @type metadata: pkg_resources Distribution obj @param develop: path to pkg if its deployed in development mode @type develop: string @param active: show if package is activated or not @type active: boolean @param installed_by: Shows if pkg was installed by a package manager other than setuptools @type installed_by: string @returns: None """ show_metadata = self.options.metadata version = metadata['Version'] # When showing all packages, note which are not active: if active: if self.options.fields: active_status = '' else: active_status = 'active' else: if self.options.fields: active_status = '*' else: active_status = 'non-active' if develop: if self.options.fields: development_status = '! ({})'.format(develop) else: development_status = 'development ({})'.format(develop) else: development_status = installed_by status = '{} {}'.format(active_status, development_status) if self.options.fields: print('{} ({}){} {}'.format(metadata['Name'], version, active_status, development_status)) else: # Need intelligent justification. print(metadata['Name'].ljust(15) + ' - ' + version.ljust(12) + ' - ' + status) if self.options.fields: for field in metadata.keys(): if field.lower() in self.options.fields: print(u' {}: {}'.format(field, metadata[field])) print() elif show_metadata: for field in metadata.keys(): if field != 'Name' and field != 'Summary': print(u' {}: {}'.format(field, metadata[field])) def show_deps(self): """Show dependencies for package(s) @returns: 0 - success 1 - No dependency info supplied """ pkgs = pkg_resources.Environment() for pkg in pkgs[self.project_name]: if not self.version: print(pkg.project_name, pkg.version) i = len(list(pkg._dep_map.values())[0]) if i: while i: if (not self.version or self.version and pkg.version == self.version): if self.version and i == len( list(pkg._dep_map.values())[0]): print(pkg.project_name, pkg.version) print(u' ' + str(list(pkg._dep_map.values())[0][i - 1])) i -= 1 else: return 1 return 0 def show_pypi_changelog(self): """Show detailed PyPI ChangeLog for the last `hours` @returns: 0 = success or 1 if failed to retrieve from XML-RPC server """ hours = self.options.show_pypi_changelog if not hours.isdigit(): print('You must supply an integer', file=sys.stderr) return 1 try: changelog = self.pypi.changelog(int(hours)) except XMLRPCFault as err_msg: print(err_msg, file=sys.stderr) print("Couldn't retrieve changelog", file=sys.stderr) return 1 last_pkg = '' for entry in changelog: pkg = entry[0] if pkg != last_pkg: print(u'{} {}\n\t{}'.format(entry[0], entry[1], entry[3])) last_pkg = pkg else: print(u'\t{}'.format(entry[3])) return 0 def show_pypi_releases(self): """Show PyPI releases for the last number of `hours` @returns: 0 = success or 1 if failed to retrieve from XML-RPC server """ try: hours = int(self.options.show_pypi_releases) except ValueError: print('You must supply an integer', file=sys.stderr) return 1 try: latest_releases = self.pypi.updated_releases(hours) except XMLRPCFault as err_msg: print(err_msg, file=sys.stderr) print("Couldn't retrieve latest releases.", file=sys.stderr) return 1 for release in latest_releases: print(u'{} {}'.format(release[0], release[1])) return 0 def show_download_links(self): """Query PyPI for pkg download URI for a packge. @returns: 0 """ # In case they specify version as 'dev' instead of using -T svn, # don't show three svn URI's if self.options.file_type == 'all' and self.version == 'dev': self.options.file_type = 'svn' if self.options.file_type == 'svn': version = 'dev' else: if self.version: version = self.version else: version = self.all_versions[0] if self.options.file_type == 'all': # Search for source, egg, and svn. self.print_download_uri(version, True) self.print_download_uri(version, False) self.print_download_uri('dev', True) else: if self.options.file_type == 'source': source = True else: source = False self.print_download_uri(version, source) return 0 def print_download_uri(self, version, source): """@param version: version number or 'dev' for svn. @type version: string @param source: download source or egg @type source: boolean @returns: None """ if version == 'dev': source = True # Use setuptools monkey-patch to grab url. url = get_download_uri(self.project_name, version, source, self.options.pypi_index) if url: print(u'{}'.format(url)) def fetch(self): """Download a package. @returns: 0 = success or 1 if failed download """ source = True directory = '.' if self.options.file_type == 'svn': svn_uri = get_download_uri(self.project_name, 'dev', True) if svn_uri: directory = self.project_name + '_svn' return self.fetch_svn(svn_uri, directory) else: print('No subversion repository found for {}'.format( self.project_name), file=sys.stderr) return 1 elif self.options.file_type == 'source': source = True elif self.options.file_type == 'egg': source = False uri = get_download_uri(self.project_name, self.version, source) if uri: return self.fetch_uri(directory, uri) else: print(u'No {} URI found for package: {}'.format( self.options.file_type, self.project_name)) return 1 def fetch_uri(self, directory, uri): """Use ``urllib.urlretrieve`` to download package to file in sandbox dir. @param directory: directory to download to @type directory: string @param uri: uri to download @type uri: string @returns: 0 = success or 1 for failed download """ filename = os.path.basename(urlparse(uri)[2]) if os.path.exists(filename): print(u'File exists: ' + filename, file=sys.stderr) return 1 try: downloaded_filename, headers = urlretrieve(uri, filename) except IOError as err_msg: print('Error downloading package {} from URL {}'.format( filename, uri), file=sys.stderr) print(str(err_msg), file=sys.stderr) return 1 if 'text/html' in headers: dfile = open(downloaded_filename) if re.search('404 Not Found', ''.join(dfile.readlines())): dfile.close() print("'404 Not Found' error", file=sys.stderr) return 1 dfile.close() return 0 def fetch_svn(self, svn_uri, directory): """Fetch subversion repository. @param svn_uri: subversion repository uri to check out @type svn_uri: string @param directory: directory to download to @type directory: string """ if not command_successful(['svn', '--version']): raise YolkException('Do you have subversion installed?') if os.path.exists(directory): raise YolkException( 'Checkout directory exists - {}'.format(directory)) try: os.mkdir(directory) except OSError as err_msg: raise YolkException('' + str(err_msg)) cwd = os.path.realpath(os.curdir) os.chdir(directory) status, _ = run_command(['svn', 'checkout', svn_uri]) os.chdir(cwd) def browse_website(self, browser=None): """Launch web browser at project's homepage. @param browser: name of web browser to use @type browser: string @returns: 0 if homepage found, 1 if no homepage found """ if len(self.all_versions): metadata = self.pypi.release_data(self.project_name, self.all_versions[0]) if 'home_page' in metadata: if browser == 'konqueror': browser = webbrowser.Konqueror() else: browser = webbrowser.get() browser.open(metadata['home_page'], 2) return 0 print('No homepage URL found', file=sys.stderr) return 1 def query_metadata_pypi(self): """Show pkg metadata queried from PyPI. @returns: 0 """ if self.version and self.version in self.all_versions: metadata = self.pypi.release_data(self.project_name, self.version) else: # Give highest version metadata = self.pypi.release_data(self.project_name, self.all_versions[0]) if metadata: if len(self.options.fields) == 1: try: print(metadata[self.options.fields[0]]) except KeyError: pass else: for key in metadata.keys(): if (not self.options.fields or (self.options.fields and key.lower() in self.options.fields)): print(u'{}: {}'.format(key, metadata[key])) return 0 def versions_available(self): """Query PyPI for a particular version or all versions of a package. @returns: 0 if version(s) found or 1 if none found """ if self.all_versions and self.version in self.all_versions: print_pkg_versions(self.project_name, [self.version]) elif not self.version and self.all_versions: print_pkg_versions(self.project_name, self.all_versions) else: if self.version: print('No package found for version {}'.format(self.version), file=sys.stderr) else: print('No package found for {}'.format(self.project_name), file=sys.stderr) return 1 return 0 def parse_search_spec(self, spec): """Parse search args and return spec dict for PyPI. * Owwww, my eyes!. Re-write this. @param spec: Cheese Shop package search spec e.g. name=Cheetah license=ZPL license=ZPL AND name=Cheetah @type spec: string @returns: tuple with spec and operator """ usage = """You can search PyPI by the following: name version author author_email maintainer maintainer_email home_page license summary description keywords platform download_url e.g. yolk -S name=Cheetah yolk -S name=yolk AND license=PSF """ if not spec: print(usage, file=sys.stderr) return (None, None) try: spec = (' ').join(spec) operator = 'AND' first = second = '' if ' AND ' in spec: (first, second) = spec.split('AND') elif ' OR ' in spec: (first, second) = spec.split('OR') operator = 'OR' else: first = spec (key1, term1) = first.split('=') key1 = key1.strip() if second: (key2, term2) = second.split('=') key2 = key2.strip() spec = {} spec[key1] = term1 if second: spec[key2] = term2 except: print(usage, file=sys.stderr) spec = operator = None return (spec, operator) def pypi_search(self): """Search PyPI by metadata keyword e.g. yolk -S name=yolk AND license=GPL @param spec: Cheese Shop search spec @type spec: list of strings spec examples: ["name=yolk"] ["license=GPL"] ["name=yolk", "AND", "license=GPL"] @returns: 0 on success or 1 if mal-formed search spec """ spec = self.pkg_spec # Add remaining cli arguments to options.pypi_search. search_arg = self.options.pypi_search spec.insert(0, search_arg.strip()) (spec, operator) = self.parse_search_spec(spec) if not spec: return 1 for pkg in self.pypi.search(spec, operator): if pkg['summary']: summary = pkg['summary'].encode('utf-8') else: summary = '' print("""{} ({}): {} """.format(pkg['name'].encode('utf-8'), pkg['version'], summary)) return 0 def show_entry_map(self): """Show entry map for a package. @param dist: package @param type: string @returns: 0 for success or 1 if error """ pprinter = pprint.PrettyPrinter() try: entry_map = pkg_resources.get_entry_map( self.options.show_entry_map) if entry_map: pprinter.pprint(entry_map) except pkg_resources.DistributionNotFound: print('Distribution not found: {}'.format( self.options.show_entry_map), file=sys.stderr) return 1 return 0 def show_entry_points(self): """Show entry points for a module. @returns: 0 for success or 1 if error """ found = False for entry_point in pkg_resources.iter_entry_points( self.options.show_entry_points): found = True try: plugin = entry_point.load() print(plugin.__module__) print(u' {}'.format(entry_point)) if plugin.__doc__: print(plugin.__doc__) print() except ImportError: pass if not found: print('No entry points found for {}'.format( self.options.show_entry_points), file=sys.stderr) return 1 return 0 def yolk_version(self): """Show yolk's version.""" print(u'yolk {}'.format(VERSION)) def parse_pkg_ver(self, want_installed): """Return tuple with project_name and version from CLI args If the user gave the wrong case for the project name, this corrects it. @param want_installed: whether package we want is installed or not @type want_installed: boolean @returns: tuple(project_name, version, all_versions) """ all_versions = [] arg_str = self.pkg_spec if '==' not in arg_str: # No version specified. project_name = arg_str version = None else: (project_name, version) = arg_str.split('==') project_name = project_name.strip() version = version.strip() # Find proper case for package name. if want_installed: project_name = yolklib.case_sensitive_name(project_name) else: (project_name, all_versions) = self.pypi.query_versions_pypi(project_name) if not len(all_versions): msg = "I'm afraid we have no '{}' at ".format(project_name) msg += 'The Cheese Shop. A little Red Leicester, perhaps?' print(msg, file=sys.stderr) sys.exit(2) return (project_name, version, all_versions)
class Yolk(object): """Main class for yolk.""" def __init__(self): # PyPI project name with proper case self.project_name = '' # PyPI project version self.version = '' # List of all versions not hidden on PyPI self.all_versions = [] self.pkg_spec = None self.options = None # Squelch output from setuptools # Add future offenders to this list. shut_up = ['distutils.log'] sys.stdout = StdOut(sys.stdout, shut_up) sys.stderr = StdOut(sys.stderr, shut_up) self.pypi = None def get_plugin(self, method): """Return plugin object if CLI option is activated and method exists. @param method: name of plugin's method we're calling @type method: string @returns: list of plugins with `method` """ all_plugins = [] for entry_point in pkg_resources.iter_entry_points('yolk.plugins'): plugin_obj = entry_point.load() plugin = plugin_obj() plugin.configure(self.options, None) if plugin.enabled: if not hasattr(plugin, method): plugin = None else: all_plugins.append(plugin) return all_plugins def run(self): """Perform actions based on CLI options. @returns: status code """ parser = setup_parser() try: import argcomplete argcomplete.autocomplete(parser) except ImportError: pass self.options = parser.parse_args() pkg_spec = validate_pypi_opts(parser) if not pkg_spec: pkg_spec = self.options.pkg_spec self.pkg_spec = pkg_spec if self.options.fields: self.options.fields = [s.strip().lower() for s in self.options.fields.split(',')] else: self.options.fields = [] if ( not self.options.pypi_search and len(sys.argv) == 1 ): parser.print_help() return 2 # Options that depend on querying installed packages, not PyPI. # We find the proper case for package names if they are installed, # otherwise PyPI returns the correct case. if ( self.options.show_deps or self.options.show_all or self.options.show_active or self.options.show_non_active or (self.options.show_updates and pkg_spec) or self.options.upgrade ): want_installed = True else: want_installed = False # show_updates may or may not have a pkg_spec if ( not want_installed or self.options.show_updates or self.options.upgrade ): self.pypi = CheeseShop(self.options.debug) # XXX: We should return 2 here if we couldn't create xmlrpc server if pkg_spec: (self.project_name, self.version, self.all_versions) = self.parse_pkg_ver(want_installed) if want_installed and not self.project_name: print('{} is not installed'.format(pkg_spec), file=sys.stderr) return 1 # I could prefix all these with 'cmd_' and the methods also # and then iterate over the `options` dictionary keys... commands = ['show_deps', 'query_metadata_pypi', 'fetch', 'versions_available', 'show_updates', 'upgrade', 'browse_website', 'show_download_links', 'pypi_search', 'show_pypi_changelog', 'show_pypi_releases', 'yolk_version', 'show_all', 'show_active', 'show_non_active', 'show_entry_map', 'show_entry_points'] # Run first command it finds, and only the first command, then return # XXX: Check if more than one command was set in options and give # error? for action in commands: if getattr(self.options, action): return getattr(self, action)() parser.print_help() def show_active(self): """Show installed active packages.""" return self.show_distributions('active') def show_non_active(self): """Show installed non-active packages.""" return self.show_distributions('nonactive') def show_all(self): """Show all installed packages.""" return self.show_distributions('all') def show_updates(self): """Check installed packages for available updates on PyPI. @param project_name: optional package name to check; checks every installed package if none specified @type project_name: string @returns: None """ if self.project_name: pkg_list = [self.project_name] else: pkg_list = get_pkglist() for (project_name, version, newest) in _updates( pkg_list, self.pypi, user_installs_only=self.options.user): print('{} {} ({})'.format(project_name, version, newest)) return 0 def upgrade(self): """Check installed packages for available updates on PyPI and upgrade. @param project_name: optional package name to check; checks every installed package if none specified @type project_name: string @returns: None """ if self.project_name: pkg_list = [self.project_name] else: pkg_list = get_pkglist() names = [values[0] for values in _updates(pkg_list, self.pypi, user_installs_only=self.options.user)] if names: subprocess.call( [sys.executable, '-m', 'pip', 'install', '--upgrade'] + (['--user'] if self.options.user else []) + names) return 0 def show_distributions(self, show): """Show list of installed activated OR non-activated packages. @param show: type of pkgs to show (all, active or nonactive) @type show: string @returns: None or 2 if error """ # Search for any plugins with active CLI options with add_column() # method. plugins = self.get_plugin('add_column') # Some locations show false positive for 'development' packages: ignores = ['/UNIONFS', '/KNOPPIX.IMG'] # See http://cheeseshop.python.org/pypi/workingenv.py for details. workingenv = os.environ.get('WORKING_ENV') if workingenv: ignores.append(workingenv) results = None for (dist, active) in yolklib.get_distributions(show, self.project_name, self.version): metadata = get_metadata(dist) for prefix in ignores: if dist.location.startswith(prefix): dist.location = dist.location.replace(prefix, '') # Case-insensitive search because of Windows. if dist.location.lower().startswith(get_python_lib().lower()): develop = '' else: develop = dist.location if metadata: add_column_text = '' for my_plugin in plugins: # See if package is 'owned' by a package manager such as # portage, apt, rpm etc. add_column_text += my_plugin.add_column(dist) + ' ' self.print_metadata(metadata, develop, active, add_column_text) else: print(str(dist) + ' has no metadata') results = True if not results and self.project_name: if self.version: pkg_spec = '{}=={}'.format(self.project_name, self.version) else: pkg_spec = self.project_name if show == 'all': print('There are no versions of {} installed'.format(pkg_spec), file=sys.stderr) else: print( 'There are no {} versions of {} installed'.format( show, pkg_spec), file=sys.stderr) return 2 elif show == 'all' and results and self.options.fields: print("Versions with '*' are non-active.") print("Versions with '!' are deployed in development mode.") def print_metadata(self, metadata, develop, active, installed_by): """Print out formatted metadata. @param metadata: package's metadata @type metadata: pkg_resources Distribution obj @param develop: path to pkg if its deployed in development mode @type develop: string @param active: show if package is activated or not @type active: boolean @param installed_by: Shows if pkg was installed by a package manager other than setuptools @type installed_by: string @returns: None """ show_metadata = self.options.metadata version = metadata['Version'] # When showing all packages, note which are not active: if active: if self.options.fields: active_status = '' else: active_status = 'active' else: if self.options.fields: active_status = '*' else: active_status = 'non-active' if develop: if self.options.fields: development_status = '! ({})'.format(develop) else: development_status = 'development ({})'.format(develop) else: development_status = installed_by status = '{} {}'.format(active_status, development_status) if self.options.fields: print( '{} ({}){} {}'.format(metadata['Name'], version, active_status, development_status)) else: # Need intelligent justification. print(metadata['Name'].ljust(15) + ' - ' + version.ljust(12) + ' - ' + status) if self.options.fields: for field in metadata.keys(): if field.lower() in self.options.fields: print(' {}: {}'.format(field, metadata[field])) print() elif show_metadata: for field in metadata.keys(): if field != 'Name' and field != 'Summary': print(' {}: {}'.format(field, metadata[field])) def show_deps(self): """Show dependencies for package(s) @returns: 0 - success 1 - No dependency info supplied """ pkgs = pkg_resources.Environment() for pkg in pkgs[self.project_name]: if not self.version: print(pkg.project_name, pkg.version) i = len(list(pkg._dep_map.values())[0]) if i: while i: if ( not self.version or self.version and pkg.version == self.version ): if self.version and i == len(list( pkg._dep_map.values())[0]): print(pkg.project_name, pkg.version) print(' ' + str(list( pkg._dep_map.values())[0][i - 1])) i -= 1 else: return 1 return 0 def show_pypi_changelog(self): """Show detailed PyPI ChangeLog for the last `hours` @returns: 0 = success or 1 if failed to retrieve from XML-RPC server """ hours = self.options.show_pypi_changelog if not hours.isdigit(): print('You must supply an integer', file=sys.stderr) return 1 try: changelog = self.pypi.changelog(int(hours)) except XMLRPCFault as err_msg: print(err_msg, file=sys.stderr) print("Couldn't retrieve changelog", file=sys.stderr) return 1 last_pkg = '' for entry in changelog: pkg = entry[0] if pkg != last_pkg: print('{} {}\n\t{}'.format(entry[0], entry[1], entry[3])) last_pkg = pkg else: print('\t{}'.format(entry[3])) return 0 def show_pypi_releases(self): """Show PyPI releases for the last number of `hours` @returns: 0 = success or 1 if failed to retrieve from XML-RPC server """ try: hours = int(self.options.show_pypi_releases) except ValueError: print('You must supply an integer', file=sys.stderr) return 1 try: latest_releases = self.pypi.updated_releases(hours) except XMLRPCFault as err_msg: print(err_msg, file=sys.stderr) print("Couldn't retrieve latest releases.", file=sys.stderr) return 1 for release in latest_releases: print('{} {}'.format(release[0], release[1])) return 0 def show_download_links(self): """Query PyPI for pkg download URI for a packge. @returns: 0 """ # In case they specify version as 'dev' instead of using -T svn, # don't show three svn URI's if self.options.file_type == 'all' and self.version == 'dev': self.options.file_type = 'svn' if self.options.file_type == 'svn': version = 'dev' else: if self.version: version = self.version else: version = self.all_versions[0] if self.options.file_type == 'all': # Search for source, egg, and svn. self.print_download_uri(version, True) self.print_download_uri(version, False) self.print_download_uri('dev', True) else: if self.options.file_type == 'source': source = True else: source = False self.print_download_uri(version, source) return 0 def print_download_uri(self, version, source): """@param version: version number or 'dev' for svn. @type version: string @param source: download source or egg @type source: boolean @returns: None """ if version == 'dev': source = True # Use setuptools monkey-patch to grab url. url = get_download_uri(self.project_name, version, source, self.options.pypi_index) if url: print('{}'.format(url)) def fetch(self): """Download a package. @returns: 0 = success or 1 if failed download """ source = True directory = '.' if self.options.file_type == 'svn': svn_uri = get_download_uri(self.project_name, 'dev', True) if svn_uri: directory = self.project_name + '_svn' return self.fetch_svn(svn_uri, directory) else: print( 'No subversion repository found for {}'.format( self.project_name), file=sys.stderr) return 1 elif self.options.file_type == 'source': source = True elif self.options.file_type == 'egg': source = False uri = get_download_uri(self.project_name, self.version, source) if uri: return self.fetch_uri(directory, uri) else: print('No {} URI found for package: {}'.format( self.options.file_type, self.project_name)) return 1 def fetch_uri(self, directory, uri): """Use ``urllib.urlretrieve`` to download package to file in sandbox dir. @param directory: directory to download to @type directory: string @param uri: uri to download @type uri: string @returns: 0 = success or 1 for failed download """ filename = os.path.basename(urlparse(uri)[2]) if os.path.exists(filename): print('File exists: ' + filename, file=sys.stderr) return 1 try: downloaded_filename, headers = urlretrieve(uri, filename) except IOError as err_msg: print( 'Error downloading package {} from URL {}'.format( filename, uri), file=sys.stderr) print(str(err_msg), file=sys.stderr) return 1 if 'text/html' in headers: dfile = open(downloaded_filename) if re.search('404 Not Found', ''.join(dfile.readlines())): dfile.close() print("'404 Not Found' error", file=sys.stderr) return 1 dfile.close() return 0 def fetch_svn(self, svn_uri, directory): """Fetch subversion repository. @param svn_uri: subversion repository uri to check out @type svn_uri: string @param directory: directory to download to @type directory: string """ if not command_successful(['svn', '--version']): raise YolkException('Do you have subversion installed?') if os.path.exists(directory): raise YolkException( 'Checkout directory exists - {}'.format(directory)) try: os.mkdir(directory) except OSError as err_msg: raise YolkException('' + str(err_msg)) cwd = os.path.realpath(os.curdir) os.chdir(directory) status, _ = run_command(['svn', 'checkout', svn_uri]) os.chdir(cwd) def browse_website(self, browser=None): """Launch web browser at project's homepage. @param browser: name of web browser to use @type browser: string @returns: 0 if homepage found, 1 if no homepage found """ if len(self.all_versions): metadata = self.pypi.release_data(self.project_name, self.all_versions[0]) if 'home_page' in metadata: if browser == 'konqueror': browser = webbrowser.Konqueror() else: browser = webbrowser.get() browser.open(metadata['home_page'], 2) return 0 print('No homepage URL found', file=sys.stderr) return 1 def query_metadata_pypi(self): """Show pkg metadata queried from PyPI. @returns: 0 """ if self.version and self.version in self.all_versions: metadata = self.pypi.release_data(self.project_name, self.version) else: # Give highest version metadata = self.pypi.release_data(self.project_name, self.all_versions[0]) if metadata: if len(self.options.fields) == 1: try: print(metadata[self.options.fields[0]]) except KeyError: pass else: for key in metadata.keys(): if ( not self.options.fields or (self.options.fields and key.lower() in self.options.fields) ): print('{}: {}'.format(key, metadata[key])) return 0 def versions_available(self): """Query PyPI for a particular version or all versions of a package. @returns: 0 if version(s) found or 1 if none found """ if self.all_versions and self.version in self.all_versions: print_pkg_versions(self.project_name, [self.version]) elif not self.version and self.all_versions: print_pkg_versions(self.project_name, self.all_versions) else: if self.version: print( 'No package found for version {}'.format(self.version), file=sys.stderr) else: print( 'No package found for {}'.format(self.project_name), file=sys.stderr) return 1 return 0 def parse_search_spec(self, spec): """Parse search args and return spec dict for PyPI. * Owwww, my eyes!. Re-write this. @param spec: Cheese Shop package search spec e.g. name=Cheetah license=ZPL license=ZPL AND name=Cheetah @type spec: string @returns: tuple with spec and operator """ usage = """You can search PyPI by the following: name version author author_email maintainer maintainer_email home_page license summary description keywords platform download_url e.g. yolk -S name=Cheetah yolk -S name=yolk AND license=PSF """ if not spec: print(usage, file=sys.stderr) return (None, None) try: spec = (' ').join(spec) operator = 'AND' first = second = '' if ' AND ' in spec: (first, second) = spec.split('AND') elif ' OR ' in spec: (first, second) = spec.split('OR') operator = 'OR' else: first = spec (key1, term1) = first.split('=') key1 = key1.strip() if second: (key2, term2) = second.split('=') key2 = key2.strip() spec = {} spec[key1] = term1 if second: spec[key2] = term2 except: print(usage, file=sys.stderr) spec = operator = None return (spec, operator) def pypi_search(self): """Search PyPI by metadata keyword e.g. yolk -S name=yolk AND license=GPL @param spec: Cheese Shop search spec @type spec: list of strings spec examples: ["name=yolk"] ["license=GPL"] ["name=yolk", "AND", "license=GPL"] @returns: 0 on success or 1 if mal-formed search spec """ spec = self.pkg_spec # Add remaining cli arguments to options.pypi_search. search_arg = self.options.pypi_search spec.insert(0, search_arg.strip()) (spec, operator) = self.parse_search_spec(spec) if not spec: return 1 for pkg in self.pypi.search(spec, operator): if pkg['summary']: summary = pkg['summary'].encode('utf-8') else: summary = '' print("""{} ({}): {} """.format(pkg['name'].encode('utf-8'), pkg['version'], summary)) return 0 def show_entry_map(self): """Show entry map for a package. @param dist: package @param type: string @returns: 0 for success or 1 if error """ pprinter = pprint.PrettyPrinter() try: entry_map = pkg_resources.get_entry_map( self.options.show_entry_map) if entry_map: pprinter.pprint(entry_map) except pkg_resources.DistributionNotFound: print( 'Distribution not found: {}'.format( self.options.show_entry_map), file=sys.stderr) return 1 return 0 def show_entry_points(self): """Show entry points for a module. @returns: 0 for success or 1 if error """ found = False for entry_point in pkg_resources.iter_entry_points( self.options.show_entry_points): found = True try: plugin = entry_point.load() print(plugin.__module__) print(' {}'.format(entry_point)) if plugin.__doc__: print(plugin.__doc__) print() except ImportError: pass if not found: print( 'No entry points found for {}'.format( self.options.show_entry_points), file=sys.stderr) return 1 return 0 def yolk_version(self): """Show yolk's version.""" print('yolk {}'.format(VERSION)) def parse_pkg_ver(self, want_installed): """Return tuple with project_name and version from CLI args If the user gave the wrong case for the project name, this corrects it. @param want_installed: whether package we want is installed or not @type want_installed: boolean @returns: tuple(project_name, version, all_versions) """ all_versions = [] arg_str = self.pkg_spec if '==' not in arg_str: # No version specified. project_name = arg_str version = None else: (project_name, version) = arg_str.split('==') project_name = project_name.strip() version = version.strip() # Find proper case for package name. if want_installed: project_name = yolklib.case_sensitive_name(project_name) else: (project_name, all_versions) = self.pypi.query_versions_pypi( project_name) if not len(all_versions): msg = "I'm afraid we have no '{}' at ".format(project_name) msg += 'The Cheese Shop. A little Red Leicester, perhaps?' print(msg, file=sys.stderr) sys.exit(2) return (project_name, version, all_versions)