blob: 6715db90b32fb3e5ba4de0a2563bfad346484cf7 [file] [log] [blame]
Tarek Ziade1231a4e2011-05-19 13:07:25 +02001"""Check PEP compliance of metadata."""
2
3from packaging import logger
4from packaging.command.cmd import Command
5from packaging.errors import PackagingSetupError
6from packaging.util import resolve_name
7
8class check(Command):
9
10 description = "check PEP compliance of metadata"
11
12 user_options = [('metadata', 'm', 'Verify metadata'),
13 ('all', 'a',
14 ('runs extended set of checks')),
15 ('strict', 's',
16 'Will exit with an error if a check fails')]
17
18 boolean_options = ['metadata', 'all', 'strict']
19
20 def initialize_options(self):
21 """Sets default values for options."""
22 self.all = False
23 self.metadata = True
24 self.strict = False
25 self._warnings = []
26
27 def finalize_options(self):
28 pass
29
30 def warn(self, msg, *args):
31 """Wrapper around logging that also remembers messages."""
32 # XXX we could use a special handler for this, but would need to test
33 # if it works even if the logger has a too high level
34 self._warnings.append((msg, args))
Éric Araujo8c86ecd2011-06-02 14:54:44 +020035 return logger.warning('%s: %s' % (self.get_command_name(), msg), *args)
Tarek Ziade1231a4e2011-05-19 13:07:25 +020036
37 def run(self):
38 """Runs the command."""
39 # perform the various tests
40 if self.metadata:
41 self.check_metadata()
42 if self.all:
43 self.check_restructuredtext()
44 self.check_hooks_resolvable()
45
46 # let's raise an error in strict mode, if we have at least
47 # one warning
48 if self.strict and len(self._warnings) > 0:
49 msg = '\n'.join(msg % args for msg, args in self._warnings)
50 raise PackagingSetupError(msg)
51
52 def check_metadata(self):
53 """Ensures that all required elements of metadata are supplied.
54
55 name, version, URL, author
56
57 Warns if any are missing.
58 """
59 missing, warnings = self.distribution.metadata.check(strict=True)
60 if missing != []:
61 self.warn('missing required metadata: %s', ', '.join(missing))
62 for warning in warnings:
63 self.warn(warning)
64
65 def check_restructuredtext(self):
66 """Checks if the long string fields are reST-compliant."""
67 missing, warnings = self.distribution.metadata.check(restructuredtext=True)
68 if self.distribution.metadata.docutils_support:
69 for warning in warnings:
70 line = warning[-1].get('line')
71 if line is None:
72 warning = warning[1]
73 else:
74 warning = '%s (line %s)' % (warning[1], line)
75 self.warn(warning)
76 elif self.strict:
77 raise PackagingSetupError('The docutils package is needed.')
78
79 def check_hooks_resolvable(self):
80 for options in self.distribution.command_options.values():
81 for hook_kind in ("pre_hook", "post_hook"):
82 if hook_kind not in options:
83 break
84 for hook_name in options[hook_kind][1].values():
85 try:
86 resolve_name(hook_name)
87 except ImportError:
88 self.warn('name %r cannot be resolved', hook_name)