├── 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 | 37 | 38 | 39 | 40 | 41 | 42 |
Light paletteDark palette
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 | --------------------------------------------------------------------------------