| 
									
										
										
										
											2011-05-19 13:07:25 +02:00
										 |  |  | """Check PEP compliance of metadata.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from packaging import logger | 
					
						
							|  |  |  | from packaging.command.cmd import Command | 
					
						
							|  |  |  | from packaging.errors import PackagingSetupError | 
					
						
							|  |  |  | from packaging.util import resolve_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class check(Command): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     description = "check PEP compliance of metadata" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     user_options = [('metadata', 'm', 'Verify metadata'), | 
					
						
							|  |  |  |                     ('all', 'a', | 
					
						
							|  |  |  |                      ('runs extended set of checks')), | 
					
						
							|  |  |  |                     ('strict', 's', | 
					
						
							|  |  |  |                      'Will exit with an error if a check fails')] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     boolean_options = ['metadata', 'all', 'strict'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def initialize_options(self): | 
					
						
							|  |  |  |         """Sets default values for options.""" | 
					
						
							|  |  |  |         self.all = False | 
					
						
							|  |  |  |         self.metadata = True | 
					
						
							|  |  |  |         self.strict = False | 
					
						
							|  |  |  |         self._warnings = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def finalize_options(self): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def warn(self, msg, *args): | 
					
						
							|  |  |  |         """Wrapper around logging that also remembers messages.""" | 
					
						
							|  |  |  |         # XXX we could use a special handler for this, but would need to test | 
					
						
							|  |  |  |         # if it works even if the logger has a too high level | 
					
						
							|  |  |  |         self._warnings.append((msg, args)) | 
					
						
							| 
									
										
										
										
											2011-06-02 14:54:44 +02:00
										 |  |  |         return logger.warning('%s: %s' % (self.get_command_name(), msg), *args) | 
					
						
							| 
									
										
										
										
											2011-05-19 13:07:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def run(self): | 
					
						
							|  |  |  |         """Runs the command.""" | 
					
						
							|  |  |  |         # perform the various tests | 
					
						
							|  |  |  |         if self.metadata: | 
					
						
							|  |  |  |             self.check_metadata() | 
					
						
							|  |  |  |         if self.all: | 
					
						
							|  |  |  |             self.check_restructuredtext() | 
					
						
							|  |  |  |             self.check_hooks_resolvable() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # let's raise an error in strict mode, if we have at least | 
					
						
							|  |  |  |         # one warning | 
					
						
							|  |  |  |         if self.strict and len(self._warnings) > 0: | 
					
						
							|  |  |  |             msg = '\n'.join(msg % args for msg, args in self._warnings) | 
					
						
							|  |  |  |             raise PackagingSetupError(msg) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_metadata(self): | 
					
						
							|  |  |  |         """Ensures that all required elements of metadata are supplied.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         name, version, URL, author | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Warns if any are missing. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         missing, warnings = self.distribution.metadata.check(strict=True) | 
					
						
							|  |  |  |         if missing != []: | 
					
						
							|  |  |  |             self.warn('missing required metadata: %s', ', '.join(missing)) | 
					
						
							|  |  |  |         for warning in warnings: | 
					
						
							|  |  |  |             self.warn(warning) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_restructuredtext(self): | 
					
						
							|  |  |  |         """Checks if the long string fields are reST-compliant.""" | 
					
						
							|  |  |  |         missing, warnings = self.distribution.metadata.check(restructuredtext=True) | 
					
						
							|  |  |  |         if self.distribution.metadata.docutils_support: | 
					
						
							|  |  |  |             for warning in warnings: | 
					
						
							|  |  |  |                 line = warning[-1].get('line') | 
					
						
							|  |  |  |                 if line is None: | 
					
						
							|  |  |  |                     warning = warning[1] | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     warning = '%s (line %s)' % (warning[1], line) | 
					
						
							|  |  |  |                 self.warn(warning) | 
					
						
							|  |  |  |         elif self.strict: | 
					
						
							|  |  |  |             raise PackagingSetupError('The docutils package is needed.') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_hooks_resolvable(self): | 
					
						
							|  |  |  |         for options in self.distribution.command_options.values(): | 
					
						
							|  |  |  |             for hook_kind in ("pre_hook", "post_hook"): | 
					
						
							|  |  |  |                 if hook_kind not in options: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |                 for hook_name in options[hook_kind][1].values(): | 
					
						
							|  |  |  |                     try: | 
					
						
							|  |  |  |                         resolve_name(hook_name) | 
					
						
							|  |  |  |                     except ImportError: | 
					
						
							|  |  |  |                         self.warn('name %r cannot be resolved', hook_name) |