1
"""
2
Provides a base class for a 'dummy' setup.py command that has no functionality
3
(probably due to a missing requirement).  This dummy command can raise an
4
exception when it is run, explaining to the user what dependencies must be met
5
to use this command.
6

7
The reason this is at all tricky is that we want the command to be able to
8
provide this message even when the user passes arguments to the command.  If we
9
don't know ahead of time what arguments the command can take, this is
10
difficult, because distutils does not allow unknown arguments to be passed to a
11
setup.py command.  This hacks around that restriction to provide a useful error
12
message even when a user passes arguments to the dummy implementation of a
13
command.
14

15
Use this like:
16

17
    try:
18
        from some_dependency import SetupCommand
19
    except ImportError:
20
        from ._dummy import _DummyCommand
21

22
        class SetupCommand(_DummyCommand):
23
            description = \
24
                'Implementation of SetupCommand from some_dependency; '
25
                'some_dependency must be installed to run this command'
26

27
            # This is the message that will be raised when a user tries to
28
            # run this command--define it as a class attribute.
29
            error_msg = \
30
                "The 'setup_command' command requires the some_dependency "
31
                "package to be installed and importable."
32
"""
33

34 20
import sys
35 20
from setuptools import Command
36 20
from distutils.errors import DistutilsArgError
37 20
from textwrap import dedent
38

39

40 20
class _DummyCommandMeta(type):
41
    """
42
    Causes an exception to be raised on accessing attributes of a command class
43
    so that if ``./setup.py command_name`` is run with additional command-line
44
    options we can provide a useful error message instead of the default that
45
    tells users the options are unrecognized.
46
    """
47

48 20
    def __init__(cls, name, bases, members):
49 20
        if bases == (Command, object):
50
            # This is the _DummyCommand base class, presumably
51 20
            return
52

53 20
        if not hasattr(cls, 'description'):
54 0
            raise TypeError(
55
                "_DummyCommand subclass must have a 'description' "
56
                "attribute.")
57

58 20
        if not hasattr(cls, 'error_msg'):
59 0
            raise TypeError(
60
                "_DummyCommand subclass must have an 'error_msg' "
61
                "attribute.")
62

63 20
    def __getattribute__(cls, attr):
64 20
        if attr in ('description', 'error_msg') or attr.startswith('_'):
65
            # Allow cls.description to work so that `./setup.py
66
            # --help-commands` still works
67 20
            return super(_DummyCommandMeta, cls).__getattribute__(attr)
68

69 0
        raise DistutilsArgError(cls.error_msg)
70

71

72 20
class _DummyCommand(Command, object, metaclass=_DummyCommandMeta):
73 20
    pass

Read our documentation on viewing source code .

Loading