Completed
Push — develop ( 972d96...d6d9b7 )
by
unknown
33s
created

_setup_logging()   A

Complexity

Conditions 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
dl 0
loc 21
rs 9.3142
1
"""
2
Anaconda Cloud command line manager
3
"""
4
from __future__ import print_function, unicode_literals
5
6
import logging
7
import sys
8
9
from argparse import ArgumentParser, RawDescriptionHelpFormatter
10
from os import makedirs
11
from os.path import join, exists, isfile
12
from logging.handlers import RotatingFileHandler
13
14
from binstar_client import __version__ as version
15
from binstar_client import commands as command_module
16
from binstar_client.commands.login import interactive_login
17
from binstar_client import errors
18
from binstar_client.utils import USER_LOGDIR
19
20
from clyent import add_subparser_modules
21
from six import PY2
22
23
logger = logging.getLogger('binstar')
24
25
26
def file_or_token(value):
27
    """
28
    If value is a file path and the file exists its contents are stripped and returned,
29
    otherwise value is returned.
30
    """
31
    if isfile(value):
32
        with open(value) as fd:
33
            return fd.read().strip()
34
35
    if any(char in value for char in '/\\.'):
36
        # This chars will never be in a token value, but may be in a path
37
        # The error message will be handled by the parser
38
        raise ValueError()
39
40
    return value
41
42
43
def _custom_excepthook(logger, show_traceback=False):
44
    def excepthook(exc_type, exc_value, exc_traceback):
45
        if issubclass(exc_type, KeyboardInterrupt) or issubclass(exc_type, errors.BinstarError):
46
            return
47
48
        if show_traceback:
49
            logger.error('', exc_info=(exc_type, exc_value, exc_traceback))
50
        else:
51
            logger.error('Error: %s', exc_value)
52
53
    return excepthook
54
55
56
class ConsoleFormatter(logging.Formatter):
57
    def format(self, record):
58
        fmt = '%(message)s' if record.levelno == logging.INFO \
59
            else '[%(levelname)s] %(message)s'
60
        if PY2:
61
            self._fmt = fmt
62
        else:
63
            self._style._fmt = fmt
64
        return super(ConsoleFormatter, self).format(record)
65
66
67
def _setup_logging(logger, log_level=logging.INFO, show_traceback=False):
68
    logger.setLevel(logging.DEBUG)
69
70
    if not exists(USER_LOGDIR):
71
        makedirs(USER_LOGDIR)
72
73
    log_file = join(USER_LOGDIR, 'cli.log')
74
75
    file_handler = RotatingFileHandler(log_file, maxBytes=10 * (1024 ** 2), backupCount=5)
76
    file_handler.setLevel(logging.DEBUG)
77
78
    console_handler = logging.StreamHandler()
79
    console_handler.setLevel(log_level)
80
81
    console_handler.setFormatter(ConsoleFormatter())
82
    file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)-8s %(name)-15s %(message)s'))
83
84
    logger.addHandler(console_handler)
85
    logger.addHandler(file_handler)
86
87
    sys.excepthook = _custom_excepthook(logger, show_traceback=show_traceback)
88
89
90
def add_default_arguments(parser, version=None):
91
    output_group = parser.add_argument_group('output')
92
    output_group.add_argument('--show-traceback', action='store_true',
93
                              help='Show the full traceback for chalmers user errors (default: %(default)s)')
94
    output_group.add_argument('-v', '--verbose',
95
                              action='store_const', help='print debug information ot the console',
96
                              dest='log_level',
97
                              default=logging.INFO, const=logging.DEBUG)
98
    output_group.add_argument('-q', '--quiet',
99
                              action='store_const', help='Only show warnings or errors the console',
100
                              dest='log_level', const=logging.WARNING)
101
102
    if version:
103
        parser.add_argument('-V', '--version', action='version',
104
                            version="%%(prog)s Command line client (version %s)" % (version,))
105
106
107
def binstar_main(sub_command_module, args=None, exit=True, description=None, version=None, epilog=None):
108
    parser = ArgumentParser(description=description, epilog=epilog,
109
                            formatter_class=RawDescriptionHelpFormatter)
110
111
    add_default_arguments(parser, version)
112
    bgroup = parser.add_argument_group('anaconda-client options')
113
    bgroup.add_argument('-t', '--token', type=file_or_token,
114
                        help="Authentication token to use. "
115
                             "May be a token or a path to a file containing a token")
116
    bgroup.add_argument('-s', '--site',
117
                        help='select the anaconda-client site to use', default=None)
118
119
    add_subparser_modules(parser, sub_command_module, 'conda_server.subcommand')
120
121
    args = parser.parse_args(args)
122
123
    _setup_logging(logger, log_level=args.log_level, show_traceback=args.show_traceback)
124
125
    try:
126
        try:
127
            if not hasattr(args, 'main'):
128
                parser.error("A sub command must be given. "
129
                             "To show all available sub commands, run:\n\n\t anaconda -h\n")
130
            return args.main(args)
131
        except errors.Unauthorized:
132
            if not sys.stdin.isatty() or args.token:
133
                # Don't try the interactive login
134
                # Just exit
135
                raise
136
137
            logger.info('The action you are performing requires authentication, '
138
                        'please sign in:')
139
            interactive_login(args)
140
            return args.main(args)
141
    except errors.ShowHelp:
142
        args.sub_parser.print_help()
143
        if exit:
144
            raise SystemExit(1)
145
        else:
146
            return 1
147
148
149
def main(args=None, exit=True):
150
    binstar_main(command_module, args, exit,
151
                 description=__doc__, version=version)
152
153
154
if __name__ == '__main__':
155
    main()
156