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

_setup_logging()   B

Complexity

Conditions 3

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

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