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