mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Basic dependency checking. setup() has two new optional arguments
requires and provides. requires is a sequence of strings, of the form 'packagename-version'. The dependency checking so far merely does an '__import__(packagename)' and checks for packagename.__version__ You can also leave off the version, and any version of the package will be installed. There's a special case for the package 'python' - sys.version_info is used, so requires= ( 'python-2.3', ) just works. Provides is of the same format as requires - but if it's not supplied, a provides is generated by adding the version to each entry in packages, or modules if packages isn't there. Provides is currently only used in the PKG-INFO file. Shortly, PyPI will grow the ability to accept these lines, and register will be updated to send them. There's a new command 'checkdep' command that runs these checks. For this version, only greater-than-or-equal checking is done. We'll add the ability to specify an optional operator later.
This commit is contained in:
		
							parent
							
								
									a3837a0d63
								
							
						
					
					
						commit
						05f842bae2
					
				
					 5 changed files with 144 additions and 3 deletions
				
			
		|  | @ -24,6 +24,7 @@ | |||
|            'bdist_dumb', | ||||
|            'bdist_rpm', | ||||
|            'bdist_wininst', | ||||
|            'checkdep', | ||||
|            # These two are reserved for future use: | ||||
|            #'bdist_sdux', | ||||
|            #'bdist_pkgtool', | ||||
|  |  | |||
							
								
								
									
										70
									
								
								Lib/distutils/command/checkdep.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								Lib/distutils/command/checkdep.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | |||
| """distutils.command.x | ||||
| 
 | ||||
| Implements the Distutils 'x' command. | ||||
| """ | ||||
| 
 | ||||
| # created 2000/mm/dd, John Doe | ||||
| 
 | ||||
| __revision__ = "$Id$" | ||||
| 
 | ||||
| from distutils.core import Command | ||||
| 
 | ||||
| class DependencyFailure(Exception): pass | ||||
| 
 | ||||
| class VersionTooOld(DependencyFailure): pass | ||||
| 
 | ||||
| class VersionNotKnown(DependencyFailure): pass | ||||
| 
 | ||||
| class checkdep (Command): | ||||
| 
 | ||||
|     # Brief (40-50 characters) description of the command | ||||
|     description = "check package dependencies" | ||||
| 
 | ||||
|     # List of option tuples: long name, short name (None if no short | ||||
|     # name), and help string. | ||||
|     # Later on, we might have auto-fetch and the like here. Feel free. | ||||
|     user_options = [] | ||||
| 
 | ||||
|     def initialize_options (self): | ||||
|         self.debug = None | ||||
| 
 | ||||
|     # initialize_options() | ||||
| 
 | ||||
| 
 | ||||
|     def finalize_options (self): | ||||
|         pass | ||||
|     # finalize_options() | ||||
| 
 | ||||
| 
 | ||||
|     def run (self): | ||||
|         from distutils.version import LooseVersion | ||||
|         failed = [] | ||||
|         for pkg, ver in self.distribution.metadata.requires: | ||||
|             if pkg == 'python': | ||||
|                 if ver is not None: | ||||
|                     # Special case the 'python' package | ||||
|                     import sys | ||||
|                     thisver = LooseVersion('%d.%d.%d'%sys.version_info[:3]) | ||||
|                     if thisver < ver: | ||||
|                         failed.append(((pkg,ver), VersionTooOld(thisver))) | ||||
|                 continue | ||||
|             # Kinda hacky - we should do more here | ||||
|             try: | ||||
|                 mod = __import__(pkg) | ||||
|             except Exception, e: | ||||
|                 failed.append(((pkg,ver), e)) | ||||
|                 continue | ||||
|             if ver is not None: | ||||
|                 if hasattr(mod, '__version__'): | ||||
|                     thisver = LooseVersion(mod.__version__) | ||||
|                     if thisver < ver: | ||||
|                         failed.append(((pkg,ver), VersionTooOld(thisver))) | ||||
|                 else: | ||||
|                     failed.append(((pkg,ver), VersionNotKnown())) | ||||
| 
 | ||||
|         if failed: | ||||
|             raise DependencyFailure, failed | ||||
| 
 | ||||
|     # run() | ||||
| 
 | ||||
| # class x | ||||
|  | @ -126,6 +126,8 @@ class install (Command): | |||
|          "force installation (overwrite any existing files)"), | ||||
|         ('skip-build', None, | ||||
|          "skip rebuilding everything (for testing/debugging)"), | ||||
|         ('skip-checkdep', None, | ||||
|          "skip checking dependencies (use at own risk)"), | ||||
| 
 | ||||
|         # Where to install documentation (eventually!) | ||||
|         #('doc-format=', None, "format of documentation to generate"), | ||||
|  | @ -183,12 +185,15 @@ def initialize_options (self): | |||
| 
 | ||||
|         # 'force' forces installation, even if target files are not | ||||
|         # out-of-date.  'skip_build' skips running the "build" command, | ||||
|         # handy if you know it's not necessary.  'warn_dir' (which is *not* | ||||
|         # handy if you know it's not necessary.  'skip_checkdep' skips | ||||
|         # the 'checkdep' command, if you are sure you can work around the | ||||
|         # dependency failure in another way. 'warn_dir' (which is *not* | ||||
|         # a user option, it's just there so the bdist_* commands can turn | ||||
|         # it off) determines whether we warn about installing to a | ||||
|         # directory not in sys.path. | ||||
|         self.force = 0 | ||||
|         self.skip_build = 0 | ||||
|         self.skip_checkdep = 0 | ||||
|         self.warn_dir = 1 | ||||
| 
 | ||||
|         # These are only here as a conduit from the 'build' command to the | ||||
|  | @ -500,6 +505,12 @@ def run (self): | |||
|         if not self.skip_build: | ||||
|             self.run_command('build') | ||||
| 
 | ||||
|         # We check dependencies before we install | ||||
|         # For now, this is disabled. Before 2.4 is released, this will | ||||
|         # be turned on. | ||||
|         #if not self.skip_checkdep: | ||||
|         #    self.run_command('checkdep') | ||||
| 
 | ||||
|         # Run all sub-commands (at least those that need to be run) | ||||
|         for cmd_name in self.get_sub_commands(): | ||||
|             self.run_command(cmd_name) | ||||
|  |  | |||
|  | @ -47,7 +47,8 @@ def gen_usage (script_name): | |||
|                   'name', 'version', 'author', 'author_email', | ||||
|                   'maintainer', 'maintainer_email', 'url', 'license', | ||||
|                   'description', 'long_description', 'keywords', | ||||
|                   'platforms', 'classifiers', 'download_url') | ||||
|                   'platforms', 'classifiers', 'download_url', | ||||
|                   'provides', 'requires', ) | ||||
| 
 | ||||
| # Legal keyword arguments for the Extension constructor | ||||
| extension_keywords = ('name', 'sources', 'include_dirs', | ||||
|  |  | |||
|  | @ -214,6 +214,51 @@ def __init__ (self, attrs=None): | |||
|                 else: | ||||
|                     sys.stderr.write(msg + "\n") | ||||
| 
 | ||||
|             # Build up the requires sequence | ||||
|             from distutils.version import LooseVersion | ||||
|             requires = attrs.get('requires') | ||||
|             if requires: | ||||
|                 if isinstance(requires, type('')): | ||||
|                     raise DistutilsOptionError, 'requires should be a sequence' | ||||
|                 newreq = [] | ||||
|                 for req in requires: | ||||
|                     if '-' not in req: | ||||
|                         # We have a plain package name - any version will do | ||||
|                         newreq.append((req,None)) | ||||
|                     else: | ||||
|                         pkg, ver = string.split(req, '-', 1) | ||||
|                         newreq.append((pkg, LooseVersion(ver))) | ||||
|                 attrs['requires'] = newreq | ||||
| 
 | ||||
|             # Build up the provides object. If the setup() has no  | ||||
|             # provides line, we use packages or modules and the version | ||||
|             # to synthesise the provides. If no version is provided (no | ||||
|             # pun intended) we don't have a provides entry at all. | ||||
|             provides = attrs.get('provides') | ||||
|             if provides: | ||||
|                 if isinstance(provides, type('')): | ||||
|                     raise DistutilsOptionError, 'provides should be a sequence' | ||||
|                 newprov = [] | ||||
|                 for prov in provides: | ||||
|                     if '-' not in prov: | ||||
|                         # We have a plain package name - any version will do | ||||
|                         newprov.append((prov,None)) | ||||
|                     else: | ||||
|                         pkg, ver = string.split(prov, '-', 1) | ||||
|                         newprov.append((pkg, LooseVersion(ver))) | ||||
|                 attrs['provides'] = newprov | ||||
|             elif attrs.get('version'): | ||||
|                 # Build a provides line | ||||
|                 prov = [] | ||||
|                 if attrs.get('packages'): | ||||
|                     for pkg in attrs['packages']: | ||||
|                         pkg = string.replace(pkg, '/', '.') | ||||
|                         prov.append('%s-%s'%(pkg, attrs['version'])) | ||||
|                 elif attrs.get('modules'): | ||||
|                     for mod in attrs['modules']: | ||||
|                         prov.append('%s-%s'%(mod, attrs['version'])) | ||||
|                 attrs['provides'] = prov | ||||
| 
 | ||||
|             # Now work on the rest of the attributes.  Any attribute that's | ||||
|             # not already defined is invalid! | ||||
|             for (key,val) in attrs.items(): | ||||
|  | @ -974,7 +1019,7 @@ class DistributionMetadata: | |||
|                          "license", "description", "long_description", | ||||
|                          "keywords", "platforms", "fullname", "contact", | ||||
|                          "contact_email", "license", "classifiers", | ||||
|                          "download_url") | ||||
|                          "download_url", "provides", "requires",) | ||||
| 
 | ||||
|     def __init__ (self): | ||||
|         self.name = None | ||||
|  | @ -991,6 +1036,8 @@ def __init__ (self): | |||
|         self.platforms = None | ||||
|         self.classifiers = None | ||||
|         self.download_url = None | ||||
|         self.requires = [] | ||||
|         self.provides = [] | ||||
| 
 | ||||
|     def write_pkg_info (self, base_dir): | ||||
|         """Write the PKG-INFO file into the release tree. | ||||
|  | @ -1006,6 +1053,10 @@ def write_pkg_info (self, base_dir): | |||
|         pkg_info.write('Author: %s\n' % self.get_contact() ) | ||||
|         pkg_info.write('Author-email: %s\n' % self.get_contact_email() ) | ||||
|         pkg_info.write('License: %s\n' % self.get_license() ) | ||||
|         for req in self.get_requires(): | ||||
|             pkg_info.write('Requires: %s\n' % req ) | ||||
|         for prov in self.get_provides(): | ||||
|             pkg_info.write('Provides: %s\n' % prov ) | ||||
|         if self.download_url: | ||||
|             pkg_info.write('Download-URL: %s\n' % self.download_url) | ||||
| 
 | ||||
|  | @ -1084,6 +1135,13 @@ def get_classifiers(self): | |||
|     def get_download_url(self): | ||||
|         return self.download_url or "UNKNOWN" | ||||
| 
 | ||||
|     def get_requires(self): | ||||
|         return [ '%s%s%s'%(x, (y and '-') or '', y or '')  | ||||
|                                                 for x,y in self.requires ] | ||||
| 
 | ||||
|     def get_provides(self): | ||||
|         return self.provides | ||||
| 
 | ||||
| # class DistributionMetadata | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Anthony Baxter
						Anthony Baxter