File: configuration.py

package info (click to toggle)
python-executing 2.2.0-0.3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,860 kB
  • sloc: python: 10,235; sh: 48; makefile: 10
file content (199 lines) | stat: -rw-r--r-- 7,696 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import inspect
import os
import sys
import threading
from collections import Set, Mapping, Sequence
from io import open

import six

import snoop as package
from snoop.formatting import DefaultFormatter
from snoop.pp_module import PP
from snoop.tracer import Spy, Tracer
from snoop.utils import builtins as builtins_module, is_pathlike, shitcode, ensure_tuple, QuerySet

try:
    # Enable ANSI escape codes in Windows 10
    import ctypes

    kernel32 = ctypes.windll.kernel32
    kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
    can_color = True
except Exception:
    can_color = os.name != 'nt'


def install(
        builtins=True,
        snoop="snoop",
        pp="pp",
        spy="spy",
        out=None,
        prefix='',
        columns='time',
        overwrite=False,
        color=None,
        enabled=True,
        watch_extras=(),
        replace_watch_extras=None,
        formatter_class=DefaultFormatter,
):
    """
    Configure output, enable or disable, and add names to builtins. Parameters:
    
    - builtins: set to False to not add any names to builtins,
        so importing will still be required.
    - snoop, pp, and spy: set to other strings 
        to choose the names of these functions in builtins
    - `out`: determines the output destination. By default this is stderr. You can also pass:
        - A string or a `Path` object to write to a file at that location. By default this always will append to the file. Pass `overwrite=True` to clear the file initially.
        - Anything with a `write` method, e.g. `sys.stdout` or a file object.
        - Any callable with a single string argument, e.g. `logger.info`.
    - `color`: determines whether the output includes escape characters to display colored text in the console. If you see weird characters in your output, your console doesn't support colors, so pass `color=False`.
        - Code is syntax highlighted using [Pygments](http://pygments.org/), and this argument is passed as the style. You can choose a different color scheme by passing a string naming a style (see [this gallery](https://help.farbox.com/pygments.html)) or a style class. The default style is monokai.   
        - By default this parameter is set to `out.isatty()`, which is usually true for stdout and stderr but will be false if they are redirected or piped. Pass `True` or a style if you want to force coloring.
        - To see colors in the PyCharm Run window, edit the Run Configuration and tick "Emulate terminal in output console".
    - `prefix`: Pass a string to start all snoop lines with that string so you can grep for them easily.
    - `columns`: This specifies the columns at the start of each output line. You can pass a string with the names of built in columns separated by spaces or commas. These are the available columns:
        - `time`: The current time. This is the only column by default.
        - `thread`: The name of the current thread.  
        - `thread_ident`: The [identifier](https://docs.python.org/3/library/threading.html#threading.Thread.ident) of the current thread, in case thread names are not unique.
        - `file`: The filename (not the full path) of the current function.
        - `full_file`: The full path to the file (also shown anyway when the function is called).
        - `function`: The name of the current function.
        - `function_qualname`: The qualified name of the current function.
        
        If you want a custom column, please open an issue to tell me what you're interested in! In the meantime, you can pass a list, where the elements are either strings or callables. The callables should take one argument, which will be an `Event` object. It has attributes `frame`, `event`, and `arg`, as specified in [`sys.settrace()`](https://docs.python.org/3/library/sys.html#sys.settrace), and other attributes which may change. 
    """
    
    if builtins:
        setattr(builtins_module, snoop, package.snoop)
        setattr(builtins_module, pp, package.pp)
        setattr(builtins_module, spy, package.spy)
    config = Config(
        out=out,
        prefix=prefix,
        columns=columns,
        overwrite=overwrite,
        color=color,
        enabled=enabled,
        watch_extras=watch_extras,
        replace_watch_extras=replace_watch_extras,
        formatter_class=formatter_class,
    )
    package.snoop.config = config
    package.pp.config = config
    package.spy.config = config


class Config(object):
    """"
    If you need more control than the global `install` function, e.g. if you want to write to several different files in one process, you can create a `Config` object, e.g: `config = snoop.Config(out=filename)`. Then `config.snoop`, `config.pp` and `config.spy` will use that configuration rather than the global one.
    
    The arguments are the same as the arguments of `install()` relating to output configuration and `enabled`.
    """
    
    def __init__(
            self,
            out=None,
            prefix='',
            columns='time',
            overwrite=False,
            color=None,
            enabled=True,
            watch_extras=(),
            replace_watch_extras=None,
            formatter_class=DefaultFormatter,
    ):
        if can_color:
            if color is None:
                isatty = getattr(out or sys.stderr, 'isatty', lambda: False)
                color = bool(isatty())
        else:
            color = False

        self.write = get_write_function(out, overwrite)
        self.formatter = formatter_class(prefix, columns, color)
        self.enabled = enabled
        self.pp = PP(self)

        class ConfiguredTracer(Tracer):
            config = self

        self.snoop = ConfiguredTracer
        self.spy = Spy(self)

        self.last_frame = None
        self.thread_local = threading.local()

        if replace_watch_extras is not None:
            self.watch_extras = ensure_tuple(replace_watch_extras)
        else:
            self.watch_extras = (len_shape_watch, dtype_watch) + ensure_tuple(watch_extras)


def len_shape_watch(source, value):
    try:
        shape = value.shape
    except Exception:
        pass
    else:
        if not inspect.ismethod(shape):
            return '{}.shape'.format(source), shape

    if isinstance(value, QuerySet):
        # Getting the length of a Django queryset evaluates it
        return None

    length = len(value)
    if (
            (isinstance(value, six.string_types)
             and length < 50) or
            (isinstance(value, (Mapping, Set, Sequence))
             and length == 0)
    ):
        return None

    return 'len({})'.format(source), length


def dtype_watch(source, value):
    dtype = value.dtype
    if not inspect.ismethod(dtype):
        return '{}.dtype'.format(source), dtype


def get_write_function(output, overwrite):
    is_path = (
        isinstance(output, six.string_types)
        or is_pathlike(output)
    )
    if is_path:
        return FileWriter(output, overwrite).write
    elif callable(output):
        write = output
    else:
        def write(s):
            stream = output

            if stream is None:
                stream = sys.stderr

            try:
                stream.write(s)
            except UnicodeEncodeError:
                # God damn Python 2
                stream.write(shitcode(s))
    return write


class FileWriter(object):
    def __init__(self, path, overwrite):
        self.path = six.text_type(path)
        self.overwrite = overwrite

    def write(self, s):
        with open(self.path, 'w' if self.overwrite else 'a', encoding='utf-8') as f:
            f.write(s)
        self.overwrite = False