├── LICENSE
├── README.md
├── setup.cfg
├── setup.py
└── typed_print
├── __init__.py
└── typed_print.py
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Andrea Palazzi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Example
6 | If you print something in your terminal or notebook, e.g.
7 | ```python
8 | print('Epoch [4/300]', 3, 3231.32, 'loss=-22.4e-9 time=121mc')
9 | ```
10 | you will see a very bleak output:
11 |
12 |
13 |
14 |
15 | **Typed_print** gets your logs a little bit more cheerful and easy to read.
16 |
17 | The output is colorized based on the argument's type. Additionally, some keywords can be highlighted (like `Epoch` here). If you print a string, the numbers in the string will get highlighted.
18 |
19 | - For a light background you will get:
20 |
21 |
22 |
23 |
24 | - For a dark background:
25 |
26 |
27 |
28 |
29 |
30 | -------
31 | Of course, everything is customizable. For example, you can override list printing like that:
32 | ```python
33 | print([131, 'I love cats', 'String with number 9'])
34 | ```
35 |
36 | Light palette |
37 | Dark palette |
38 |
39 |  |
40 |  |
41 |
42 |
43 |
44 |
45 | # Features
46 |
47 | - Type-based coloring and printing layout.
48 | - Automatic highlighting guided by regexp or list of keywords.
49 | - Extensible and customizable framework: easily change representation for objects of any type including the built-in types like `int`, `list`, `dict`, etc.
50 |
51 | # Install
52 |
53 | ```
54 | pip install typed_print
55 | ```
56 |
57 | Tested with Ubuntu 14.04 and Python 3.6.
58 |
59 | # Usage
60 |
61 | For the examples above the printing function is initialized as follows:
62 | ```python
63 | import typed_print as tp
64 |
65 | print = tp.init(palette='light', # or 'dark'
66 | str_mode=tp.HIGHLIGHT_NUMBERS,
67 | highlight_word_list=['Epoch'])
68 | ```
69 |
70 | ## Arguments
71 |
72 | - `palette`: highlighting palette. Use `dark` if the background of your terminal is dark and `light` otherwise
73 | - `str_mode`: what to highlight in strings and sting representations of objects of unknown types. Possible values are:
74 | - `tp.HIGHLIGHT_NOTHING`
75 | - `tp.HIGHLIGHT_DIGITS`
76 | - `tp.HIGHLIGHT_NUMBERS`
77 | - `tp.HIGHLIGHT_CUSTOM`: in this case you should pass a compiled regex extractor as `re_custom` argument to `tp.init`, e.g.
78 | - `re_custom=re.compile("\d+")`
79 | - `custom_type_map`: a dictionary with correspondence `type:processor_fn`. `processor_fn` should return string representation for the object, similarly to `__str__` or `__repr__`.
80 | - `highlight_word_list`: a list of words to highlight. In the example above `highlight_word_list=['Epoch']`
81 |
82 | ## Why overriding print function?
83 | I did not find a way to override `__str__` or `__repr__` methods for the built-in types. Thus the only way to change the way the objects of built-in type are printed is to override `print` function.
84 |
85 | UPDATE: @alex-bender mentioned [here](https://github.com/DmitryUlyanov/typed_print/issues/1) that there is actually a way to override built-in types.
86 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 |
4 | [bdist_wheel]
5 | universal=1
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup(
4 | name='typed_print',
5 | version='0.1',
6 | packages=['typed_print'],
7 | install_requires=['huepy'],
8 | url='https://github.com/DmitryUlyanov/typed_print',
9 | download_url='https://github.com/DmitryUlyanov/typed_print/archive/0.1.tar.gz',
10 | license='MIT',
11 | author='Dmitry Ulyanov',
12 | author_email='dmitry.ulyanov.msu@gmail.com',
13 | description='Printing function with automated coloring.'
14 | )
--------------------------------------------------------------------------------
/typed_print/__init__.py:
--------------------------------------------------------------------------------
1 | from .typed_print import *
--------------------------------------------------------------------------------
/typed_print/typed_print.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import re
3 | from huepy import *
4 |
5 | try:
6 | import __builtin__
7 | except ImportError:
8 | import builtins as __builtin__ # Python 3
9 |
10 |
11 | # Save normal print
12 | printr = __builtin__.print
13 |
14 |
15 | # Custom formatters
16 | def format_str(x):
17 | '''
18 | This processing is applied to all arguments of all types other than the ones found in `type_map`.
19 | E.g. an object `obj` of type NeuralNet wighout a rule in `type_map` is first represented as string str(obj)
20 | and then some parts of the string are highlighted using this function.
21 | '''
22 | if TP.str_mode == HIGHLIGHT_DIGITS:
23 | return TP.apply_regexp(str(x), TP.re_digits)
24 |
25 | elif TP.str_mode == HIGHLIGHT_NUMBERS:
26 | return TP.apply_regexp(str(x), TP.re_numbers)
27 |
28 | elif TP.str_mode == HIGHLIGHT_CUSTOM:
29 | return TP.apply_regexp(str(x), TP.re_custom)
30 |
31 | elif TP.str_mode == HIGHLIGHT_NOTHING:
32 | return str(x)
33 |
34 | else:
35 | assert False, 'Wrong str_mode'
36 |
37 | def format_list(x):
38 | return bold(good('')) + bold('List length = {}\n\n'.format(len(x))) + TP.typed_print_format(str(x))
39 |
40 | def format_dict(x):
41 | return bold(good('')) + bold('Dict size = {}\n\n'.format(len(x))) + TP.typed_print_format(str(x))
42 |
43 |
44 |
45 | # String modes
46 | HIGHLIGHT_NOTHING = 0
47 | HIGHLIGHT_DIGITS = 1
48 | HIGHLIGHT_NUMBERS = 2
49 | HIGHLIGHT_CUSTOM = 3
50 |
51 |
52 | class TP(object):
53 |
54 | re_digits = re.compile("\d+")
55 | re_numbers = re.compile("[-]?[.]?[\d]+[\.]?\d*(?:[eE][-+]?\d+)?")
56 | re_custom = None
57 |
58 | str_mode = None
59 |
60 | # Default palettes
61 | palette = dict(
62 | light = {
63 | int: lightcyan,
64 | float: blue,
65 | list: format_list,
66 | dict: format_dict
67 | },
68 | dark = {
69 | int: cyan,
70 | float: orange,
71 | list: format_list,
72 | dict: format_dict
73 | }
74 | )
75 |
76 | type_map = {}
77 |
78 | highlight_word_list = []
79 |
80 | @classmethod
81 | def apply_regexp(cls, x, re_compiled):
82 | '''
83 | Splits string using regexp and applies the rules from type_map to each part
84 | '''
85 | indices = []
86 | for m in re_compiled.finditer(str(x)):
87 | indices.append(m.start())
88 | indices.append(m.end())
89 |
90 | if len(indices) == 0:
91 | return x
92 |
93 | shift = 0
94 | if indices[0] != 0:
95 | indices.insert(0, 0)
96 | shift = 1
97 |
98 | parts = [x[i:j] for i,j in zip(indices, indices[1:]+[None])]
99 |
100 | for i in range(shift, len(parts), 2):
101 | parts[i] = cls.type_map[int](parts[i])
102 |
103 |
104 | return ''.join(parts)
105 |
106 | @classmethod
107 | def typed_print_format(cls, *args, **kwargs):
108 | """
109 | This function returns formatted string
110 | """
111 |
112 | res = ' '.join([cls.type_map.get(type(arg), format_str)(arg) for arg in args])
113 |
114 | for word in cls.highlight_word_list:
115 | res = res.replace(str(word), bold(red(str(word))))
116 |
117 | return res
118 |
119 | @classmethod
120 | def typed_print(cls, *args, **kwargs):
121 | res = cls.typed_print_format(*args, **kwargs)
122 | return __builtin__.print(res, **kwargs)
123 |
124 |
125 |
126 |
127 |
128 | def init(palette, str_mode=HIGHLIGHT_NUMBERS, custom_type_map=None, highlight_word_list=None, re_custom=None):
129 | '''
130 |
131 | '''
132 |
133 | if custom_type_map is not None:
134 | if not isinstance(custom_type_map, dict):
135 | assert False, 'custom_type_map should be a dictionary.'
136 |
137 | TP.type_map = custom_type_map
138 | else:
139 | if palette not in TP.palette:
140 | assert False, 'Please either choose from standad themes: ' \
141 | + str(list(TP.palette.keys())) \
142 | + ' or provide `custom_type_map`'
143 |
144 | TP.type_map = TP.palette[palette]
145 |
146 | TP.highlight_word_list = highlight_word_list if highlight_word_list is not None else []
147 |
148 | TP.str_mode = str_mode
149 |
150 | if TP.str_mode == HIGHLIGHT_CUSTOM:
151 | if re_custom is None:
152 | assert False, 'Please provide custom compiled re pattern'
153 |
154 | TP.re_custom = re_custom
155 |
156 | return TP.typed_print
157 |
--------------------------------------------------------------------------------