• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kivy / python-for-android / 24714384767

21 Apr 2026 09:18AM UTC coverage: 62.932% (-0.5%) from 63.382%
24714384767

Pull #3301

github

T-Dynamos
add back cython for pandas
Pull Request #3301: fix PYTHONPATH hacks

1832 of 3175 branches covered (57.7%)

Branch coverage included in aggregate %.

61 of 150 new or added lines in 8 files covered. (40.67%)

9 existing lines in 2 files now uncovered.

5329 of 8204 relevant lines covered (64.96%)

5.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.06
/pythonforandroid/logger.py
1
import logging
8 ✔
2
import os
8 ✔
3
import re
8 ✔
4
import sh
8 ✔
5
from sys import stdout, stderr
8 ✔
6
from math import log10
8 ✔
7
from collections import defaultdict
8 ✔
8
from colorama import Style as Colo_Style, Fore as Colo_Fore
8 ✔
9

10

11
# monkey patch to show full output
12
sh.ErrorReturnCode.truncate_cap = 999999
8 ✔
13

14

15
class LevelDifferentiatingFormatter(logging.Formatter):
8 ✔
16
    def format(self, record):
8 ✔
17
        if record.levelno > 30:
8 ✔
18
            record.msg = '{}{}[ERROR]{}{}:   '.format(
8 ✔
19
                Err_Style.BRIGHT, Err_Fore.RED, Err_Fore.RESET,
20
                Err_Style.RESET_ALL) + record.msg
21
        elif record.levelno > 20:
8 ✔
22
            record.msg = '{}{}[WARNING]{}{}: '.format(
8 ✔
23
                Err_Style.BRIGHT, Err_Fore.RED, Err_Fore.RESET,
24
                Err_Style.RESET_ALL) + record.msg
25
        elif record.levelno > 10:
8 ✔
26
            record.msg = '{}[INFO]{}:    '.format(
8 ✔
27
                Err_Style.BRIGHT, Err_Style.RESET_ALL) + record.msg
28
        else:
29
            record.msg = '{}{}[DEBUG]{}{}:   '.format(
8 ✔
30
                Err_Style.BRIGHT, Err_Fore.LIGHTBLACK_EX, Err_Fore.RESET,
31
                Err_Style.RESET_ALL) + record.msg
32
        return super().format(record)
8 ✔
33

34

35
logger = logging.getLogger('p4a')
8 ✔
36
# Necessary as importlib reloads this,
37
# which would add a second handler and reset the level
38
if not hasattr(logger, 'touched'):
8 !
39
    logger.setLevel(logging.INFO)
8 ✔
40
    logger.touched = True
8 ✔
41
    ch = logging.StreamHandler(stderr)
8 ✔
42
    formatter = LevelDifferentiatingFormatter('%(message)s')
8 ✔
43
    ch.setFormatter(formatter)
8 ✔
44
    logger.addHandler(ch)
8 ✔
45
info = logger.info
8 ✔
46
debug = logger.debug
8 ✔
47
warning = logger.warning
8 ✔
48
error = logger.error
8 ✔
49

50

51
class colorama_shim:
8 ✔
52

53
    def __init__(self, real):
8 ✔
54
        self._dict = defaultdict(str)
8 ✔
55
        self._real = real
8 ✔
56
        self._enabled = False
8 ✔
57

58
    def __getattr__(self, key):
8 ✔
59
        return getattr(self._real, key) if self._enabled else self._dict[key]
8 ✔
60

61
    def enable(self, enable):
8 ✔
62
        self._enabled = enable
8 ✔
63

64

65
Out_Style = colorama_shim(Colo_Style)
8 ✔
66
Out_Fore = colorama_shim(Colo_Fore)
8 ✔
67
Err_Style = colorama_shim(Colo_Style)
8 ✔
68
Err_Fore = colorama_shim(Colo_Fore)
8 ✔
69

70

71
def setup_color(color):
8 ✔
72
    enable_out = (False if color == 'never' else
8 ✔
73
                  True if color == 'always' else
74
                  stdout.isatty())
75
    Out_Style.enable(enable_out)
8 ✔
76
    Out_Fore.enable(enable_out)
8 ✔
77

78
    enable_err = (False if color == 'never' else
8 ✔
79
                  True if color == 'always' else
80
                  stderr.isatty())
81
    Err_Style.enable(enable_err)
8 ✔
82
    Err_Fore.enable(enable_err)
8 ✔
83

84

85
def info_main(*args):
8 ✔
86
    logger.info(''.join([Err_Style.BRIGHT, Err_Fore.GREEN] + list(args) +
8 ✔
87
                        [Err_Style.RESET_ALL, Err_Fore.RESET]))
88

89

90
def info_notify(s):
8 ✔
91
    info('{}{}{}{}'.format(Err_Style.BRIGHT, Err_Fore.LIGHTBLUE_EX, s,
8 ✔
92
                           Err_Style.RESET_ALL))
93

94

95
def shorten_string(string, max_width):
8 ✔
96
    ''' make limited length string in form:
97
      "the string is very lo...(and 15 more)"
98
    '''
99
    string_len = len(string)
8 ✔
100
    if string_len <= max_width:
8 ✔
101
        return string
8 ✔
102
    visible = max_width - 16 - int(log10(string_len))
8 ✔
103
    # expected suffix len "...(and XXXXX more)"
104
    if not isinstance(string, str):
8 ✔
105
        visstring = str(string[:visible], errors='ignore')
8 ✔
106
    else:
107
        visstring = string[:visible]
8 ✔
108
    return u''.join((visstring, u'...(and ',
8 ✔
109
                     str(string_len - visible), u' more)'))
110

111

112
def get_console_width():
8 ✔
113
    try:
8 ✔
114
        cols = int(os.environ['COLUMNS'])
8 ✔
115
    except (KeyError, ValueError):
8 ✔
116
        pass
8 ✔
117
    else:
118
        if cols >= 25:
8 !
119
            return cols
8 ✔
120

121
    try:
8 ✔
122
        cols = max(25, int(os.popen('stty size', 'r').read().split()[1]))
8 ✔
123
    except Exception:
8 ✔
124
        pass
8 ✔
125
    else:
126
        return cols
8 ✔
127

128
    return 100
8 ✔
129

130

131
def shprint(command, *args, **kwargs):
8 ✔
132
    '''Runs the command (which should be an sh.Command instance), while
133
    logging the output.'''
134
    kwargs["_iter"] = True
8 ✔
135
    kwargs["_out_bufsize"] = 1
8 ✔
136
    kwargs["_err_to_out"] = True
8 ✔
137
    kwargs["_bg"] = True
8 ✔
138

139
    # silent mode
140
    silent = kwargs.get("silent", False)
8 ✔
141
    kwargs.pop("silent", False)
8 ✔
142
    if silent:
8 !
NEW
143
        kwargs["_out"] = None
×
144

145
    is_critical = kwargs.pop('_critical', False)
8 ✔
146
    tail_n = kwargs.pop('_tail', None)
8 ✔
147
    full_debug = False
8 ✔
148
    if "P4A_FULL_DEBUG" in os.environ:
8 ✔
149
        tail_n = 0
8 ✔
150
        full_debug = True
8 ✔
151
    filter_in = kwargs.pop('_filter', None)
8 ✔
152
    filter_out = kwargs.pop('_filterout', None)
8 ✔
153
    if len(logger.handlers) > 1:
8 !
154
        logger.removeHandler(logger.handlers[1])
×
155
    columns = get_console_width()
8 ✔
156
    command_path = str(command).split('/')
8 ✔
157
    command_string = command_path[-1]
8 ✔
158
    string = ' '.join(['{}->{} running'.format(Out_Fore.LIGHTBLACK_EX,
8 ✔
159
                                               Out_Style.RESET_ALL),
160
                       command_string] + list(args))
161

162
    # If logging is not in DEBUG mode, trim the command if necessary
163
    if logger.level > logging.DEBUG:
8 !
164
        logger.info('{}{}'.format(shorten_string(string, columns - 12),
8 ✔
165
                                  Err_Style.RESET_ALL))
166
    else:
167
        logger.debug('{}{}'.format(string, Err_Style.RESET_ALL))
×
168

169
    need_closing_newline = False
8 ✔
170
    try:
8 ✔
171
        msg_hdr = '           working: '
8 ✔
172
        msg_width = columns - len(msg_hdr) - 1
8 ✔
173
        output = command(*args, **kwargs)
8 ✔
174
        for line in output:
8 ✔
175
            if isinstance(line, bytes):
8 !
176
                line = line.decode('utf-8', errors='replace')
×
177
            if logger.level > logging.DEBUG:
8 !
178
                if full_debug:
8 ✔
179
                    stdout.write(line)
8 ✔
180
                    stdout.flush()
8 ✔
181
                    continue
8 ✔
182
                msg = line.replace(
8 ✔
183
                    '\n', ' ').replace(
184
                        '\t', ' ').replace(
185
                            '\b', ' ').rstrip()
186
                if msg:
8 !
187
                    if "CI" not in os.environ:
8 !
188
                        stdout.write(u'{}\r{}{:<{width}}'.format(
8 ✔
189
                            Err_Style.RESET_ALL, msg_hdr,
190
                            shorten_string(msg, msg_width), width=msg_width))
191
                        stdout.flush()
8 ✔
192
                        need_closing_newline = True
8 ✔
193
            else:
194
                logger.debug(''.join(['\t', line.rstrip()]))
×
195
        if need_closing_newline:
8 ✔
196
            stdout.write('{}\r{:>{width}}\r'.format(
8 ✔
197
                Err_Style.RESET_ALL, ' ', width=(columns - 1)))
198
            stdout.flush()
8 ✔
199
    except sh.ErrorReturnCode as err:
8 ✔
200
        if silent:
8 !
NEW
201
            if is_critical:
×
NEW
202
                exit(1)
×
203
            else:
NEW
204
                raise
×
205
        if need_closing_newline:
8 !
206
            stdout.write('{}\r{:>{width}}\r'.format(
×
207
                Err_Style.RESET_ALL, ' ', width=(columns - 1)))
208
            stdout.flush()
×
209
        if tail_n is not None or filter_in or filter_out:
8 !
210
            def printtail(out, name, forecolor, tail_n=0,
8 ✔
211
                          re_filter_in=None, re_filter_out=None):
212
                lines = out.splitlines()
8 ✔
213
                if re_filter_in is not None:
8 !
214
                    lines = [line for line in lines if re_filter_in.search(line)]
×
215
                if re_filter_out is not None:
8 !
216
                    lines = [line for line in lines if not re_filter_out.search(line)]
×
217
                if tail_n == 0 or len(lines) <= tail_n:
8 !
218
                    info('{}:\n{}\t{}{}'.format(
8 ✔
219
                        name, forecolor, '\t\n'.join(lines), Out_Fore.RESET))
220
                else:
221
                    info('{} (last {} lines of {}):\n{}\t{}{}'.format(
×
222
                        name, tail_n, len(lines),
223
                        forecolor, '\t\n'.join([s for s in lines[-tail_n:]]),
224
                        Out_Fore.RESET))
225
            printtail(err.stdout.decode('utf-8'), 'STDOUT', Out_Fore.YELLOW, tail_n,
8 ✔
226
                      re.compile(filter_in) if filter_in else None,
227
                      re.compile(filter_out) if filter_out else None)
228
            printtail(err.stderr.decode('utf-8'), 'STDERR', Err_Fore.RED)
8 ✔
229
        if is_critical or full_debug:
8 !
230
            env = kwargs.get("_env")
8 ✔
231
            if env is not None:
8 !
232
                info("{}ENV:{}\n{}\n".format(
×
233
                    Err_Fore.YELLOW, Err_Fore.RESET, "\n".join(
234
                        "export {}='{}'".format(n, v) for n, v in env.items())))
235
            info("{}COMMAND:{}\ncd {} && {} {}\n".format(
8 ✔
236
                Err_Fore.YELLOW, Err_Fore.RESET, os.getcwd(), command,
237
                ' '.join(args)))
238
            warning("{}ERROR: {} failed!{}".format(
8 ✔
239
                Err_Fore.RED, command, Err_Fore.RESET))
240
        if is_critical:
8 !
241
            exit(1)
8 ✔
242
        else:
243
            raise
1 ✔
244

245
    return output
8 ✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc