├── .gitignore ├── .travis.yml ├── LICENSE ├── MENIFEST.in ├── README.md ├── deploy.sh ├── dist ├── pyprnt-1.0.0-py3-none-any.whl ├── pyprnt-2.5.0-py3-none-any.whl └── pyprnt-2.5.1-py3-none-any.whl ├── pyprnt ├── __init__.py ├── prnt.py └── util.py ├── setup.cfg ├── setup.py └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | build/ 3 | pyprnt.egg-info/ 4 | main.py -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.4" 4 | - "3.5" 5 | - "3.6" 6 | - "3.7" 7 | script: 8 | - python test.py 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kevin Kim 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. 22 | -------------------------------------------------------------------------------- /MENIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyPrnt 2 | 3 | [![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/) 4 | [![PyPI pyversions](https://img.shields.io/pypi/pyversions/pyprnt)](https://pypi.org/project/pyprnt/) 5 | [![MIT LICENSE](https://img.shields.io/pypi/l/pyprnt)](https://github.com/kevink1103/pyprnt/blob/master/LICENSE) 6 | [![PyPI version](https://badge.fury.io/py/pyprnt.svg)](https://badge.fury.io/py/pyprnt) 7 | [![Weekly Downloads](https://pepy.tech/badge/pyprnt/week)](https://pepy.tech/project/pyprnt/week) 8 | [![Monthly Downloads](https://pepy.tech/badge/pyprnt/month)](https://pepy.tech/project/pyprnt/month) 9 | [![Total Downloads](https://pepy.tech/badge/pyprnt)](https://pepy.tech/project/pyprnt) 10 | [![Build Status](https://travis-ci.com/kevink1103/pyprnt.svg?branch=master)](https://travis-ci.com/kevink1103/pyprnt) 11 | 12 | PyPrnt helps you to print a list (sequence container) or a dictionary (mapping object) in an organized table form. 13 | Just try it out! 14 | Don't use `print()` anymore. Use `prnt()` for the rest of your life :) 15 | 16 | ## How to install 17 | 18 | ```bash 19 | # If you have both Python 2 and 3, 20 | pip3 install pyprnt 21 | # If you only have Python 3, 22 | pip install pyprnt 23 | ``` 24 | 25 | ## How to use 26 | 27 | ```python 28 | from pyprnt import prnt 29 | 30 | creation = ["Adam", "Eve"] 31 | menu = { 32 | "Kimchi": 5000, 33 | "Ice Cream": 100 34 | } 35 | print(creation) 36 | prnt(creation) # Magic! 37 | print(menu) 38 | prnt(menu) # Magic! 39 | prnt("Eat apple.") # Magic ignored for types other than list & dict 40 | ``` 41 | 42 | You should see this... 43 | 44 | ```text 45 | ['Adam', 'Eve'] 46 | ┌─┬────┐ 47 | │0│Adam│ 48 | │1│Eve │ 49 | └─┴────┘ 50 | {'Kimchi': 5000, 'Ice Cream': 100} 51 | ┌─────────┬────┐ 52 | │Kimchi │5000│ 53 | │Ice Cream│100 │ 54 | └─────────┴────┘ 55 | Eat apple. 56 | ``` 57 | 58 | Wanna see more? 59 | Check out the content of a block with ease. 60 | 61 | ```python 62 | block = {'index':1,'transaction':[{"sender":"Block_Reward","receipient":"30819f300d06092a864886f70d010101050003818d0030818902818100b9cadf2ca51ca6714cf645f015652a80b9b8fc7e1aafc888334ac6f4f7dc177465595ef713765b027ab97ca7929820d1afb54b64a03cb971f0f46582d5266568f78746d30c4a651b0a0cf14dacdd619f034b330f4c14f253c72496778ff921a1b907aa0e6201369bffb2bd2e0a059d034e711ef004a3100a8998c2786349579f0203010001","value":"5.0"},{"sender":"30819f300d06092a864886f70d010101050003818d0030818902818100b9cadf2ca51ca6714cf645f015652a80b9b8fc7e1aafc888334ac6f4f7dc177465595ef713765b027ab97ca7929820d1afb54b64a03cb971f0f46582d5266568f78746d30c4a651b0a0cf14dacdd619f034b330f4c14f253c72496778ff921a1b907aa0e6201369bffb2bd2e0a059d034e711ef004a3100a8998c2786349579f0203010001","receipient":"30819f300d06092a864886f70d010101050003818d0030818902818100ab65b338fc66d9fc4870b7319f3c21aaf5a0082bce02caf9e3de6dc159c9df91477786028e7380be451d2fb94ed83070e85b588b4ed9d540461d3256bd2aafd3ae0fefa92f82799064414d0ed9e667bc18ad0f48505a2ae9b790a4363fcbef4b526453f91e9572835feabb25aebe2ff38c9abff32b6140c39cb71f8cf0491b850203010001","value":5.0,"signature":"a3da555fe4afe5fc957d466161dbae8b7fbb02c22780cae6fd5a4bbdc3ad7b8753361f74948db662086209c4272ebdadf5b7a14216c18be7f1c3b86ddb3aa43267792f3edc99cc7294fa89bc95f90cfb0ecd2df73b0dde8520499836f86b57af79d837b3c3dc806a37d067ca4a55caee7883bec035fed0b2df40c910cdde99a2"}],'timestamp':'09/23/2019,16:08:19','previous_hash':'This_Is_Genesis_Block','hash':'00e63fb0a8474d78df37e0ba99816d526ba110fc16098ecae65358890975a645','nonce':222} 63 | 64 | # print(block) - don't use this! 65 | prnt(block, truncate=True) # Magic! 66 | ``` 67 | 68 | ```text 69 | ┌─────────────┬──────────────────────────────────┐ 70 | │index │1 │ 71 | │transaction │┌─┬──────────────────────────────┐│ 72 | │ ││0│┌──────────┬─────────────────┐││ 73 | │ ││ ││sender │Block_Reward │││ 74 | │ ││ ││receipient│30819f300d0609...│││ 75 | │ ││ ││value │5.0 │││ 76 | │ ││ │└──────────┴─────────────────┘││ 77 | │ ││1│┌──────────┬─────────────────┐││ 78 | │ ││ ││sender │30819f300d0609...│││ 79 | │ ││ ││receipient│30819f300d0609...│││ 80 | │ ││ ││value │5.0 │││ 81 | │ ││ ││signature │a3da555fe4afe5...│││ 82 | │ ││ │└──────────┴─────────────────┘││ 83 | │ │└─┴──────────────────────────────┘│ 84 | │timestamp │09/23/2019,16:08:19 │ 85 | │previous_hash│This_Is_Genesis_Block │ 86 | │hash │00e63fb0a8474d78df37e0ba99816d5...│ 87 | │nonce │222 │ 88 | └─────────────┴──────────────────────────────────┘ 89 | ``` 90 | 91 | Isn't this amazing? **Learn how to use this with a few more options** 92 | 93 | ## Options 94 | 95 | > prnt(obj, enable=True, both=False, truncate=False, depth=-1, output=False, width=get_terminal_size(), sep=' ', end='\n', file=sys.stdout, flush=False) 96 | 97 | ### enable: bool (default: True) 98 | 99 | Enable `prnt()` form. 100 | 101 | ```python 102 | prnt(creation, enable=False) 103 | ``` 104 | 105 | ```text 106 | ['Adam', 'Eve'] 107 | ``` 108 | 109 | ### both: bool (default: False) 110 | 111 | Print in both original `print()` form and `prnt()` form. 112 | 113 | ```python 114 | prnt(creation, both=True) 115 | ``` 116 | 117 | ```text 118 | ['Adam', 'Eve'] 119 | ┌─┬────┐ 120 | │0│Adam│ 121 | │1│Eve │ 122 | └─┴────┘ 123 | ``` 124 | 125 | ### truncate: bool (default: False) 126 | 127 | Truncate output values if they exceed the maximum width of Terminal. 128 | The maximum width of Terminal is 50 in this example. 129 | 130 | ```python 131 | # truncate = False 132 | prnt(["abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", 12345678910]) 133 | # truncate = True 134 | prnt(["abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", 12345678910], truncate=True) 135 | ``` 136 | 137 | ```text 138 | ┌─┬──────────────────────────────────────────────┐ 139 | │0│abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst│ 140 | │ │uvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn│ 141 | │ │opqrstuvwxyz │ 142 | │1│12345678910 │ 143 | └─┴──────────────────────────────────────────────┘ 144 | ┌─┬──────────────────────────────────────────────┐ 145 | │0│abcdefghijklmnopqrstuvwxyzabcdefghijklmnopq...│ 146 | │1│12345678910 │ 147 | └─┴──────────────────────────────────────────────┘ 148 | ``` 149 | 150 | ### depth: int (default: -1 no limit) 151 | 152 | Set the depth of recursive `prnt()`. 153 | Depth should be either -1 or bigger than 0. 154 | The contents beyond the depth will be printed as string. 155 | 156 | ```python 157 | prnt(block, depth=2) 158 | ``` 159 | 160 | ```text 161 | ┌─────────────┬──────────────────────────────────┐ 162 | │index │1 │ 163 | │transaction │┌─┬──────────────────────────────┐│ 164 | │ ││0│{'sender': 'Block_Reward', ...││ 165 | │ ││1│{'sender': '30819f300d06092...││ 166 | │ │└─┴──────────────────────────────┘│ 167 | │timestamp │09/23/2019,16:08:19 │ 168 | │previous_hash│This_Is_Genesis_Block │ 169 | │hash │00e63fb0a8474d78df37e0ba99816d5...│ 170 | │nonce │222 │ 171 | └─────────────┴──────────────────────────────────┘ 172 | ``` 173 | 174 | ### width: int (default: current Terminal width or 50) 175 | 176 | Set the maximum width of Terminal. 177 | Width should be bigger than 20. 178 | 179 | ```python 180 | prnt(["Kevin Kim is a developer."], width=20) 181 | ``` 182 | 183 | ```text 184 | ┌─┬────────────────┐ 185 | │0│Kevin Kim is a d│ 186 | │ │eveloper. │ 187 | └─┴────────────────┘ 188 | ``` 189 | 190 | ### output: bool (default: False) 191 | 192 | Get a return string for the printed content from `prnt()`. 193 | `prnt()` will not print anything. 194 | 195 | ```python 196 | output_data = prnt(creation, output=True) 197 | print(repr(output_data)) 198 | print(output_data) 199 | ``` 200 | 201 | ```text 202 | '┌─┬────┐\n│0│Adam│\n│1│Eve │\n└─┴────┘' 203 | ┌─┬────┐ 204 | │0│Adam│ 205 | │1│Eve │ 206 | └─┴────┘ 207 | ``` 208 | 209 | ### sep: str (default: ' ') 210 | 211 | Default `print()` parameter. 212 | Put a separator between each input. 213 | 214 | ```python 215 | prnt("010", "8282", "8282", sep="-") 216 | ``` 217 | 218 | ```text 219 | 010-8282-8282 220 | ``` 221 | 222 | ### end: str (default: '\n') 223 | 224 | Default `print()` parameter. 225 | Put at the end of an output. 226 | 227 | ```python 228 | prnt(creation, end="") 229 | prnt("The force is with me") 230 | ``` 231 | 232 | ```text 233 | ┌─┬────┐ 234 | │0│Adam│ 235 | │1│Eve │ 236 | └─┴────┘The force is with me 237 | ``` 238 | 239 | ### file: object with a write method (default: sys.stdout) 240 | 241 | Default `print()` parameter. 242 | 243 | ### flush: bool (default: False) 244 | 245 | Default `print()` parameter. 246 | Specify if the output is flushed (True) or buffered (False). 247 | 248 | ## Caution 249 | 250 | If the length of label part (index for list, key for dict) goes beyond the half size of your width, 251 | the label will be truncated to the half size of your width in order to secure enough space for displaying values. 252 | 253 | For example, 254 | 255 | ```python 256 | disease = { 257 | "pneumonoultramicroscopicsilicovolcanoconiosis": "an invented long word said to mean a lung disease caused by inhaling very fine ash and sand dust." 258 | } 259 | prnt(disease) 260 | ``` 261 | 262 | ```text 263 | ┌────────────────────────┬───────────────────────┐ 264 | │pneumonoultramicrosco...│an invented long word s│ 265 | │ │aid to mean a lung dise│ 266 | │ │ase caused by inhaling │ 267 | │ │very fine ash and sand │ 268 | │ │dust. │ 269 | └────────────────────────┴───────────────────────┘ 270 | ``` 271 | 272 | ## Author 273 | 274 | Copyright (c) 2019 Kevin Kim 275 | [Website](https://kevink1103.github.io/) 276 | [GitHub](https://github.com/kevink1103) 277 | [LinkedIn](https://www.linkedin.com/in/kimsungbum/) 278 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | # For Test, 2 | # twine upload --repository-url https://test.pypi.org/legacy/ dist/* 3 | vi setup.py && python3 setup.py bdist_wheel && twine upload dist/* 4 | -------------------------------------------------------------------------------- /dist/pyprnt-1.0.0-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevink1103/pyprnt/8f9f1d950ec068f49c77d96c5525a5657aab465d/dist/pyprnt-1.0.0-py3-none-any.whl -------------------------------------------------------------------------------- /dist/pyprnt-2.5.0-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevink1103/pyprnt/8f9f1d950ec068f49c77d96c5525a5657aab465d/dist/pyprnt-2.5.0-py3-none-any.whl -------------------------------------------------------------------------------- /dist/pyprnt-2.5.1-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevink1103/pyprnt/8f9f1d950ec068f49c77d96c5525a5657aab465d/dist/pyprnt-2.5.1-py3-none-any.whl -------------------------------------------------------------------------------- /pyprnt/__init__.py: -------------------------------------------------------------------------------- 1 | from pyprnt.prnt import prnt 2 | -------------------------------------------------------------------------------- /pyprnt/prnt.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Mapping 2 | 3 | import sys 4 | 5 | from pyprnt.util import get_terminal_size, prnt_iteratable, is_sequence_container 6 | 7 | def prnt(*obj, enable=True, both=False, truncate=False, 8 | depth=-1, width=None, output=False, 9 | sep=' ', end='\n', file=sys.stdout, flush=False): 10 | width = width or get_terminal_size() 11 | 12 | try: 13 | if width < 20: 14 | raise ValueError('width should be bigger than 20') 15 | if depth != -1 and depth <= 0: 16 | raise ValueError('depth should be either -1 or bigger than 0') 17 | 18 | # Separator in action 19 | if len(obj) > 1 and (sep != '' and sep != ' '): 20 | print(*obj, sep=sep, end=end, file=file, flush=flush) 21 | if output: return sep.join(obj) 22 | return 23 | 24 | output_data = "" 25 | 26 | for i, o in enumerate(obj): 27 | if enable and (is_sequence_container(o) or isinstance(o, Mapping)): 28 | if both: 29 | print(o, sep=sep, file=file, flush=flush) 30 | output_data = str(o) + "\n" 31 | output_data += prnt_iteratable(o, end='', truncate=truncate, depth=depth, width=width, output=output, file=file, flush=flush, last=i==len(obj)-1) 32 | else: 33 | temp_end = ' ' if i < len(obj)-1 else '' 34 | print(o, sep=sep, end=temp_end, file=file, flush=flush) 35 | 36 | if output_data != None and len(output_data) > 0: 37 | output_data += str(o) + temp_end 38 | else: 39 | output_data = str(o) + temp_end 40 | 41 | if i < len(obj)-1 and (is_sequence_container(obj[i+1]) or isinstance(obj[i+1], Mapping)): 42 | print() 43 | print(end=end) 44 | if end != '\n': 45 | output_data += end 46 | 47 | if output: 48 | return output_data 49 | except Exception as e: 50 | print(*obj) 51 | print(e) 52 | -------------------------------------------------------------------------------- /pyprnt/util.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Mapping, Sequence 2 | 3 | import os 4 | 5 | def get_terminal_size(): 6 | try: 7 | return os.get_terminal_size().columns 8 | except: 9 | return 50 10 | 11 | def border(position, width, label, value): 12 | if 3 + label + value > width: 13 | value = width - label - 3 14 | 15 | if position == "top": 16 | return "┌" + "─" * (label) + "┬" + "─" * (value) + "┐" 17 | elif position == "bottom": 18 | return "└" + "─" * (label) + "┴" + "─" * (value) + "┘" 19 | 20 | def prnt_iteratable(obj, end, truncate, depth, width, output, file, flush, last): 21 | output_data = create_output(obj, truncate=truncate, level=0, depth=depth, width=width) 22 | if not output: 23 | print_output(output_data, end=end, file=file, flush=flush) 24 | if not last: 25 | print() 26 | 27 | if type(output_data) != list: 28 | return output_data 29 | else: 30 | return "\n".join(output_data) 31 | 32 | def create_output(obj, truncate, level, depth, width): 33 | # This function is RECURSIVE 34 | if width < 10 or level == depth or len(obj) == 0: 35 | return safe_str(obj) 36 | 37 | if is_sequence_container(obj): 38 | label = str(len(obj)-1) 39 | max_label_length = len(label) 40 | value = list(map(safe_str, obj)) 41 | max_value_length = len(max(value, key=len)) 42 | iterate_items = enumerate(obj) 43 | elif isinstance(obj, Mapping): 44 | label = list([safe_str(key) for key in obj.keys()]) 45 | max_label_length = len(max(label, key=len)) 46 | value = list(map(safe_str, obj.values())) 47 | max_value_length = len(max(value, key=len)) 48 | iterate_items = obj.items() 49 | else: 50 | raise TypeError('need a list or a dictionary') 51 | 52 | # Resize if the label is too long 53 | half_width = int(width/2)-1 54 | if max_label_length > half_width: 55 | max_label_length = half_width 56 | allowed_space = allowed_space = width - max_label_length - 3 57 | 58 | # Prepare output 59 | output = [] 60 | allowed_space = width - max_label_length - 3 # Max Allowed Space for Value 61 | top_border = border("top", width, max_label_length, max_value_length) 62 | bottom_border = border("bottom", width, max_label_length, max_value_length) 63 | 64 | output.append(top_border) 65 | for i, j in iterate_items: 66 | # Label 67 | label = safe_str(i) 68 | label_empty = " " * (max_label_length - len(label)) 69 | # Truncate if the label is too long 70 | if len(label) > half_width: 71 | label = label[:half_width-3] + "..." 72 | label_empty = "" 73 | 74 | # Value 75 | if is_sequence_container(j) or isinstance(j, Mapping): 76 | value = create_output(j, truncate=truncate, level=level+1, depth=depth, width=width-max_label_length-3) 77 | else: 78 | value = safe_str(j) 79 | if allowed_space > max_value_length: 80 | value_empty = " " * (max_value_length - len(value)) 81 | else: 82 | value_empty = " " * (allowed_space - len(value)) 83 | 84 | if not is_sequence_container(j) and not isinstance(j, Mapping) and len(value) > allowed_space and not truncate: 85 | # Extra rows to print full long value 86 | while True: 87 | if len(value) == 0: 88 | break 89 | 90 | new_value = value[:allowed_space] 91 | value_empty = " " * (allowed_space - len(new_value)) 92 | output.append("│{}{}│{}{}│".format(label, label_empty, new_value, value_empty)) 93 | label = " " * (max_label_length) 94 | label_empty = "" 95 | value = value[allowed_space:] 96 | else: 97 | if is_sequence_container(value): 98 | for ii, line in enumerate(value): 99 | if ii > 0: label = " " * len(label) 100 | # output.append(f"│{label}{label_empty}│{line}│") above 3.7 101 | output.append("│{}{}│{}│".format(label, label_empty, line)) 102 | else: 103 | if type(value) == str and len(value) > allowed_space: 104 | value = value[:allowed_space - 3] + "..." 105 | output.append("│{}{}│{}{}│".format(label, label_empty, value, value_empty)) 106 | output.append(bottom_border) 107 | output = tailor_output(output) 108 | return output 109 | 110 | def tailor_output(output): 111 | if not is_sequence_container(output): 112 | return output 113 | 114 | top_border = output[0] 115 | bottom_border = output[len(output)-1] 116 | max_width = 0 117 | 118 | # Skip very first and very last 119 | for i in range(1, len(output)-1): 120 | row = output[i] 121 | if len(row) >= max_width: 122 | max_width = len(row) 123 | 124 | diff = max_width - len(top_border) 125 | 126 | if diff >= 0: 127 | top_border = top_border[:-1] 128 | top_border += "─" * (diff) 129 | top_border += "┐" 130 | bottom_border = bottom_border[:-1] 131 | bottom_border += "─" * (diff) 132 | bottom_border += "┘" 133 | output[0] = top_border 134 | output[len(output)-1] = bottom_border 135 | else: 136 | top_border = top_border[:diff-1] 137 | top_border += "─" * (diff) 138 | top_border += "┐" 139 | bottom_border = bottom_border[:diff-1] 140 | bottom_border += "─" * (diff) 141 | bottom_border += "┘" 142 | output[0] = top_border 143 | output[len(output)-1] = bottom_border 144 | 145 | for i in range(1, len(output)-1): 146 | row = output[i] 147 | diff = max_width - len(row) 148 | if diff > 0: 149 | output[i] = row[:-1] + (" " * (diff)) + "│" 150 | 151 | return output 152 | 153 | def print_output(output, end, file, flush): 154 | if type(output) == str: 155 | print(output, end='', file=file, flush=flush) 156 | return 157 | for i, line in enumerate(output): 158 | if i < len(output) - 1: 159 | print(line, file=file, flush=flush) 160 | else: 161 | print(line, end='', file=file, flush=flush) 162 | 163 | def is_sequence_container(obj): 164 | return not isinstance(obj, str) and isinstance(obj, Sequence) 165 | 166 | def safe_str(obj, newline='\\n'): 167 | """This function eliminates newlines and replaces them with the explicit character newline string 168 | passed. Defaults to the showing '\n' explicitly""" 169 | return str(obj).replace('\n', newline) 170 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | version = "2.5.1" 4 | 5 | with open("README.md", encoding="utf-8") as f: 6 | long_description = f.read() 7 | 8 | setup( 9 | name = "pyprnt", 10 | version = version, 11 | description = "A Modern Python Pretty Printer", 12 | long_description = long_description, 13 | long_description_content_type = "text/markdown", 14 | author = "Kevin Kim", 15 | author_email = "kevink1103@gmail.com", 16 | license = "MIT", 17 | url = "https://github.com/kevink1103/pyprnt", 18 | download_url = "https://github.com/kevink1103/pyprnt/dist/pyprnt-{}-py3-none-any.whl".format(version), 19 | install_requires = [], 20 | packages = find_packages(exclude = []), 21 | keywords = ["pretty", "print"], 22 | python_requires = ">=3.3", 23 | zip_safe = False, 24 | classifiers = [ 25 | "Programming Language :: Python :: 3.3", 26 | "Programming Language :: Python :: 3.4", 27 | "Programming Language :: Python :: 3.5", 28 | "Programming Language :: Python :: 3.6", 29 | "Programming Language :: Python :: 3.7", 30 | "Programming Language :: Python :: 3.8", 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import sys 4 | import collections 5 | 6 | from pyprnt.util import get_terminal_size 7 | from pyprnt.util import border 8 | from pyprnt import prnt 9 | 10 | class TestTerminalMethods(unittest.TestCase): 11 | 12 | def test_get_terminal_size(self): 13 | testee = get_terminal_size() 14 | expect = 1 15 | self.assertGreater(testee, expect) 16 | 17 | class TestBorderMethods(unittest.TestCase): 18 | 19 | def test_top_border(self): 20 | testee = border("top", label=5, value=5, width=20) 21 | expect = "┌─────┬─────┐" 22 | self.assertEqual(testee, expect) 23 | 24 | def test_bottom_border(self): 25 | testee = border("bottom", label=5, value=5, width=20) 26 | expect = "└─────┴─────┘" 27 | self.assertEqual(testee, expect) 28 | 29 | def test_exceed_border(self): 30 | testee1 = border("top", label=5, value=20, width=20) 31 | testee2 = len(testee1) 32 | expect1 = "┌─────┬────────────┐" 33 | expect2 = 20 34 | self.assertEqual(testee1, expect1) 35 | self.assertEqual(testee2, expect2) 36 | 37 | class TestPrintEnable(unittest.TestCase): 38 | 39 | def test_enable_false(self): 40 | creation = ['Adam', 'Eve'] 41 | testee = prnt(creation, enable=False, output=True, width=50) 42 | expect = "['Adam', 'Eve']" 43 | self.assertEqual(testee, expect) 44 | 45 | class TestPrintBoth(unittest.TestCase): 46 | 47 | def test_both_true(self): 48 | creation = ['Adam', 'Eve'] 49 | testee = prnt(creation, both=True, output=True, width=50) 50 | expect = "['Adam', 'Eve']\n┌─┬────┐\n│0│Adam│\n│1│Eve │\n└─┴────┘" 51 | self.assertEqual(testee, expect) 52 | 53 | class TestPrintTruncate(unittest.TestCase): 54 | 55 | def test_truncate_false(self): 56 | testee = prnt(["abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", 12345678910], output=True, width=50) 57 | expect = "┌─┬──────────────────────────────────────────────┐\n│0│abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst│\n│ │uvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn│\n│ │opqrstuvwxyzabcdefghijklmnopqrstuvwxyz │\n│1│12345678910 │\n└─┴──────────────────────────────────────────────┘" 58 | self.assertEqual(testee, expect) 59 | 60 | def test_truncate_true(self): 61 | testee = prnt(["abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", 12345678910], truncate=True, output=True, width=50) 62 | expect = "┌─┬──────────────────────────────────────────────┐\n│0│abcdefghijklmnopqrstuvwxyzabcdefghijklmnopq...│\n│1│12345678910 │\n└─┴──────────────────────────────────────────────┘" 63 | self.assertEqual(testee, expect) 64 | 65 | class TestPrintDepth(unittest.TestCase): 66 | 67 | def test_depth_infinity_basic(self): 68 | testee = prnt([[[]], []], output=True, width=50) 69 | print(repr(testee)) 70 | expect = "┌─┬──────┐\n│0│┌─┬──┐│\n│ ││0│[]││\n│ │└─┴──┘│\n│1│[] │\n└─┴──────┘" 71 | self.assertEqual(testee, expect) 72 | 73 | def test_depth_infinity_complex(self): 74 | testee = prnt([[[{}]], []], output=True, width=50) 75 | expect = "┌─┬──────────┐\n│0│┌─┬──────┐│\n│ ││0│┌─┬──┐││\n│ ││ ││0│{}│││\n│ ││ │└─┴──┘││\n│ │└─┴──────┘│\n│1│[] │\n└─┴──────────┘" 76 | self.assertEqual(testee, expect) 77 | 78 | def test_depth_1_basic(self): 79 | testee = prnt([[[]], []], depth=1, output=True, width=50) 80 | print(repr(testee)) 81 | expect = "┌─┬────┐\n│0│[[]]│\n│1│[] │\n└─┴────┘" 82 | self.assertEqual(testee, expect) 83 | 84 | def test_depth_1_complex(self): 85 | testee = prnt([[[{}]], []], depth=1, output=True, width=50) 86 | expect = "┌─┬──────┐\n│0│[[{}]]│\n│1│[] │\n└─┴──────┘" 87 | self.assertEqual(testee, expect) 88 | 89 | def test_depth_2_basic(self): 90 | testee = prnt([[[]], []], depth=2, output=True, width=50) 91 | print(repr(testee)) 92 | expect = "┌─┬──────┐\n│0│┌─┬──┐│\n│ ││0│[]││\n│ │└─┴──┘│\n│1│[] │\n└─┴──────┘" 93 | self.assertEqual(testee, expect) 94 | 95 | def test_depth_2_complex(self): 96 | testee = prnt([[[{}]], []], depth=2, output=True, width=50) 97 | expect = "┌─┬────────┐\n│0│┌─┬────┐│\n│ ││0│[{}]││\n│ │└─┴────┘│\n│1│[] │\n└─┴────────┘" 98 | self.assertEqual(testee, expect) 99 | 100 | class TestPrintWidth(unittest.TestCase): 101 | 102 | def test_width_minimum_20(self): 103 | testee = prnt(["Kevin Kim is a developer."], output=True, width=20) 104 | expect = "┌─┬────────────────┐\n│0│Kevin Kim is a d│\n│ │eveloper. │\n└─┴────────────────┘" 105 | self.assertEqual(testee, expect) 106 | 107 | class TestPrintSep(unittest.TestCase): 108 | 109 | def test_separator_empty(self): 110 | testee = prnt("010", "8282", "8282", output=True, width=50) 111 | expect = "010 8282 8282" 112 | self.assertEqual(testee, expect) 113 | 114 | def test_separator_dash(self): 115 | testee = prnt("010", "8282", "8282", sep="-", output=True, width=50) 116 | expect = "010-8282-8282" 117 | self.assertEqual(testee, expect) 118 | 119 | class TestPrintEnd(unittest.TestCase): 120 | 121 | def test_end_empty(self): 122 | testee = prnt("010", "8282", "8282", end="", output=True, width=50) 123 | expect = "010 8282 8282" 124 | self.assertEqual(testee, expect) 125 | 126 | def test_end_newline(self): 127 | testee = prnt("010", "8282", "8282", output=True, width=50) 128 | expect = "010 8282 8282" 129 | self.assertEqual(testee, expect) 130 | 131 | def test_end_dash(self): 132 | testee = prnt("010", "8282", "8282", end="--", output=True, width=50) 133 | expect = "010 8282 8282--" 134 | self.assertEqual(testee, expect) 135 | 136 | class TestPrintBasic(unittest.TestCase): 137 | 138 | def test_list_basic(self): 139 | creation = ["Adam", "Eve"] 140 | testee = prnt(creation, output=True, width=50) 141 | expect = "┌─┬────┐\n│0│Adam│\n│1│Eve │\n└─┴────┘" 142 | self.assertEqual(testee, expect) 143 | 144 | def test_dict_basic(self): 145 | # For python version 3.4 and below 146 | menu = collections.OrderedDict() 147 | menu["kimchi"] = 5000 148 | menu["Ice Cream"] = 100 149 | testee = prnt(menu, output=True, width=50) 150 | expect = "┌─────────┬────┐\n│kimchi │5000│\n│Ice Cream│100 │\n└─────────┴────┘" 151 | self.assertEqual(testee, expect) 152 | 153 | def test_list_with_newline(self): 154 | creation = ["Ad\nam", "Eve"] 155 | testee = prnt(creation, output=True, width=50) 156 | expect = "┌─┬──────┐\n│0│Ad\\nam│\n│1│Eve │\n└─┴──────┘" 157 | self.assertEqual(testee, expect) 158 | 159 | def test_dict_with_newline(self): 160 | # For python version 3.4 and below 161 | menu = collections.OrderedDict() 162 | menu["kimchi"] = 5000 163 | menu["Ice\nCream"] = "1 €\n1.08 $" 164 | testee = prnt(menu, output=True, width=50) 165 | expect = "┌──────────┬───────────┐\n│kimchi │5000 │\n│Ice\\nCream│1 €\\n1.08 $│\n└──────────┴───────────┘" 166 | self.assertEqual(testee, expect) 167 | 168 | if __name__ == "__main__": 169 | unittest.main() 170 | --------------------------------------------------------------------------------