textfmt 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {textfmt-0.2.0.dist-info → textfmt-0.2.1.dist-info}/METADATA +24 -2
- textfmt-0.2.1.dist-info/RECORD +10 -0
- textformat/__init__.py +6 -2
- textformat/progress.py +43 -31
- textformat/table.py +7 -2
- textformat/textformat.py +18 -1
- textfmt-0.2.0.dist-info/RECORD +0 -10
- {textfmt-0.2.0.dist-info → textfmt-0.2.1.dist-info}/WHEEL +0 -0
- {textfmt-0.2.0.dist-info → textfmt-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {textfmt-0.2.0.dist-info → textfmt-0.2.1.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: textfmt
|
3
|
-
Version: 0.2.
|
4
|
-
Summary:
|
3
|
+
Version: 0.2.1
|
4
|
+
Summary: Terminal Formatting for Humans
|
5
5
|
Author-email: Rihaan Meher <meherrihaan@gmail.com>
|
6
6
|
License: MIT
|
7
7
|
Project-URL: Homepage, https://github.com/sharktide/textformat
|
@@ -55,6 +55,28 @@ data = [
|
|
55
55
|
print(TableFormatter.generate(headers, data))
|
56
56
|
```
|
57
57
|
|
58
|
+
File Download Progress Bars
|
59
|
+
|
60
|
+
```python
|
61
|
+
from textformat.progress import Color, DownloadProgressBar
|
62
|
+
from pathlib import Path
|
63
|
+
def download_with_progress(url: str, output_path: Path, download_color, complete_color):
|
64
|
+
response = requests.get(url, stream=True)
|
65
|
+
total = int(response.headers.get('content-length', 0))
|
66
|
+
|
67
|
+
if response.status_code != 200:
|
68
|
+
raise Exception(f"Failed to download from {url}. HTTP status: {response.status_code}")
|
69
|
+
|
70
|
+
progress = DownloadProgressBar(total, prefix=output_path.name,
|
71
|
+
download_color=download_color, complete_color=complete_color)
|
72
|
+
with open(output_path, "wb") as f:
|
73
|
+
for chunk in response.iter_content(chunk_size=8192):
|
74
|
+
if chunk:
|
75
|
+
f.write(chunk)
|
76
|
+
progress.update(progress.downloaded + len(chunk))
|
77
|
+
print()
|
78
|
+
download_with_progress('https://huggingface.co/sharktide/recyclebot0/resolve/main/tf_model.h5', Path("tf_model.h5"), Colors.CYAN, Colors.GREEN)
|
79
|
+
```
|
58
80
|
## License
|
59
81
|
|
60
82
|
MIT License – Free to use and modify.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
textfmt-0.2.1.dist-info/licenses/LICENSE,sha256=Msmy46KiAR2wmzeCe3kSl-wEt7ptbTZOAzdwUUyFq_M,1090
|
2
|
+
textformat/__init__.py,sha256=Yz9706rsciMHmMHDvzqS1LxQm04N9NM5rO-xUaFLm8c,306
|
3
|
+
textformat/progress.py,sha256=2EXWQxEVjnd9eHmEJUGmH6e3TVqXC5ZNcS-ez6L-W-A,3444
|
4
|
+
textformat/table.py,sha256=W51T9__0Qtgv_nxn6s2bit4tduhgiXZ3FwjODGvLf3I,746
|
5
|
+
textformat/textformat.py,sha256=hYGgLkra7HIdG5mtVcDvAHOMzbv-0q9TFs82jjdOdew,1681
|
6
|
+
textformat/word_art.py,sha256=GsJka0ukV23FTvhuyuGRChhNGOjTWOdKBK3h0e-LnX8,318
|
7
|
+
textfmt-0.2.1.dist-info/METADATA,sha256=BserKxfvwaxtgi0GjyNnAseG1XfHSys2VtH9tdd9sMA,2539
|
8
|
+
textfmt-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
9
|
+
textfmt-0.2.1.dist-info/top_level.txt,sha256=Nf6G1sI6EqA2opfIdIio6ZqKNbpT2EtYf5cd0sKgPRo,11
|
10
|
+
textfmt-0.2.1.dist-info/RECORD,,
|
textformat/__init__.py
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# textfmt/__init__.py
|
2
|
-
from .textformat import TextFormat, bcolors
|
2
|
+
from .textformat import TextFormat, bcolors, enable_windows_ansi_support
|
3
3
|
from .word_art import WordArt
|
4
4
|
from .table import TableFormatter
|
5
|
-
|
5
|
+
import os
|
6
|
+
|
7
|
+
enable_windows_ansi_support()
|
8
|
+
|
9
|
+
__all__ = ["TextFormat", "bcolors", "WordArt", "TableFormatter", "enable_windows_ansi_support"]
|
textformat/progress.py
CHANGED
@@ -20,28 +20,14 @@ class Colors:
|
|
20
20
|
LIGHT_MAGENTA = "\033[95m"
|
21
21
|
LIGHT_CYAN = "\033[96m"
|
22
22
|
|
23
|
-
# List of all available colors for easy access
|
24
23
|
ALL_COLORS = [
|
25
24
|
GREEN, BLUE, RED, YELLOW, MAGENTA, CYAN, WHITE,
|
26
25
|
LIGHT_GREEN, LIGHT_BLUE, LIGHT_RED, LIGHT_YELLOW, LIGHT_MAGENTA, LIGHT_CYAN
|
27
26
|
]
|
28
27
|
|
29
|
-
class
|
30
|
-
def __init__(self, total, prefix='', length=40, fill='━', empty=' ', print_end="\r",
|
28
|
+
class DownloadProgressBar:
|
29
|
+
def __init__(self, total, prefix='', length=40, fill='━', empty=' ', print_end="\r",
|
31
30
|
download_color=Colors.GREEN, complete_color=Colors.BLUE):
|
32
|
-
"""
|
33
|
-
Initializes the progress bar.
|
34
|
-
|
35
|
-
Parameters:
|
36
|
-
total (int): Total number of bytes to download.
|
37
|
-
prefix (str): Prefix for the progress bar.
|
38
|
-
length (int): Length of the progress bar.
|
39
|
-
fill (str): Character to fill the progress bar.
|
40
|
-
empty (str): Character to represent empty space in the progress bar.
|
41
|
-
print_end (str): Ending character for printing.
|
42
|
-
download_color (str): Color code for progress bar during download.
|
43
|
-
complete_color (str): Color code for progress bar when download is complete.
|
44
|
-
"""
|
45
31
|
self.total = total
|
46
32
|
self.prefix = prefix
|
47
33
|
self.length = length
|
@@ -54,33 +40,59 @@ class ProgressBar:
|
|
54
40
|
self.downloaded = 0
|
55
41
|
|
56
42
|
def update(self, downloaded):
|
43
|
+
now = time.time()
|
57
44
|
self.downloaded = downloaded
|
58
|
-
percent = ("{0:.1f}").format(100 * (self.downloaded / float(self.total)))
|
59
|
-
filled_length = int(self.length * self.downloaded // self.total)
|
60
|
-
bar = self.fill * filled_length + self.empty * (self.length - filled_length)
|
61
45
|
|
62
|
-
|
46
|
+
if self.total > 0:
|
47
|
+
percent = 100 * (self.downloaded / float(self.total))
|
48
|
+
filled_length = int(self.length * self.downloaded // self.total)
|
49
|
+
total_size = self.format_size(self.total)
|
50
|
+
else:
|
51
|
+
percent = 100
|
52
|
+
filled_length = 40
|
53
|
+
total_size = "?"
|
54
|
+
|
55
|
+
bar = self.fill * filled_length + self.empty * (self.length - filled_length)
|
63
56
|
current_size = self.format_size(self.downloaded)
|
64
|
-
total_size = self.format_size(self.total)
|
65
57
|
|
66
|
-
#
|
67
|
-
|
68
|
-
|
69
|
-
else
|
70
|
-
|
58
|
+
# Speed (bytes per second)
|
59
|
+
elapsed = now - self.start_time
|
60
|
+
speed_bps = self.downloaded / elapsed if elapsed > 0 else 0
|
61
|
+
speed_str = self.format_size(speed_bps) + "/s" if speed_bps > 0 else "?"
|
62
|
+
|
63
|
+
# ETA
|
64
|
+
remaining = self.total - self.downloaded if self.total > 0 else 0
|
65
|
+
eta_seconds = remaining / speed_bps if self.total > 0 and speed_bps > 0 else 0
|
66
|
+
eta_str = self.format_time(eta_seconds) if self.total > 0 else "?"
|
67
|
+
|
68
|
+
# Choose color
|
69
|
+
color = self.complete_color if self.downloaded == self.total and self.total > 0 else self.download_color
|
71
70
|
|
72
|
-
# Print
|
73
|
-
sys.stdout.write(
|
71
|
+
# Print
|
72
|
+
sys.stdout.write(
|
73
|
+
f'\r{self.prefix} {color}{bar}{Colors.RESET} {percent:5.1f}% • '
|
74
|
+
f'{current_size}/{total_size} • {speed_str} • {eta_str}'
|
75
|
+
)
|
74
76
|
sys.stdout.flush()
|
75
77
|
|
76
|
-
if self.downloaded == self.total:
|
78
|
+
if self.downloaded == self.total and self.total > 0:
|
77
79
|
sys.stdout.write(self.print_end)
|
78
80
|
sys.stdout.flush()
|
79
81
|
|
80
|
-
|
81
|
-
|
82
|
+
@staticmethod
|
83
|
+
def format_size(size_in_bytes):
|
84
|
+
"""Format the size in a human-readable format."""
|
82
85
|
for unit in ['B', 'KiB', 'MiB', 'GiB', 'TiB']:
|
83
86
|
if size_in_bytes < 1024:
|
84
87
|
return f"{size_in_bytes:.1f} {unit}"
|
85
88
|
size_in_bytes /= 1024
|
89
|
+
return f"{size_in_bytes:.1f} PiB"
|
86
90
|
|
91
|
+
@staticmethod
|
92
|
+
def format_time(seconds):
|
93
|
+
"""Format seconds into H:MM:SS or M:SS."""
|
94
|
+
if seconds < 0.1:
|
95
|
+
return "0:00"
|
96
|
+
m, s = divmod(int(seconds + 0.5), 60)
|
97
|
+
h, m = divmod(m, 60)
|
98
|
+
return f"{h}:{m:02}:{s:02}" if h else f"{m}:{s:02}"
|
textformat/table.py
CHANGED
@@ -4,10 +4,15 @@ class TableFormatter:
|
|
4
4
|
@staticmethod
|
5
5
|
def generate(headers, data):
|
6
6
|
""" Creates a formatted table for structured output """
|
7
|
+
# Calculate max width per column
|
7
8
|
col_widths = [max(len(str(item)) for item in col) for col in zip(headers, *data)]
|
8
|
-
|
9
|
+
|
10
|
+
def build_border():
|
11
|
+
return "+" + "+".join("-" * (w + 2) for w in col_widths) + "+"
|
9
12
|
|
10
13
|
def format_row(row):
|
11
14
|
return "| " + " | ".join(f"{str(item).ljust(w)}" for item, w in zip(row, col_widths)) + " |"
|
12
15
|
|
13
|
-
|
16
|
+
border = build_border()
|
17
|
+
rows = [format_row(headers), border] + [format_row(row) for row in data]
|
18
|
+
return f"{border}\n" + "\n".join(rows) + f"\n{border}"
|
textformat/textformat.py
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
def enable_windows_ansi_support() -> None:
|
4
|
+
"""
|
5
|
+
Enables ANSI escape sequence support in Windows terminals (safe version).
|
6
|
+
"""
|
7
|
+
if os.name == 'nt':
|
8
|
+
try:
|
9
|
+
import ctypes
|
10
|
+
kernel32 = ctypes.windll.kernel32
|
11
|
+
handle = kernel32.GetStdHandle(-11)
|
12
|
+
mode = ctypes.c_ulong()
|
13
|
+
if kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
|
14
|
+
mode.value |= 0x0004
|
15
|
+
kernel32.SetConsoleMode(handle, mode)
|
16
|
+
except Exception:
|
17
|
+
pass
|
18
|
+
|
1
19
|
class bcolors:
|
2
20
|
HEADER = '\033[95m'
|
3
21
|
OKBLUE = '\033[94m'
|
@@ -9,7 +27,6 @@ class bcolors:
|
|
9
27
|
ENDC = '\033[0m'
|
10
28
|
BOLD = '\033[1m'
|
11
29
|
UNDERLINE = '\033[4m'
|
12
|
-
|
13
30
|
class TextFormat:
|
14
31
|
""" ANSI Escape Code Formatting for Python CLI Output """
|
15
32
|
RESET = "\x1b[0m"
|
textfmt-0.2.0.dist-info/RECORD
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
textfmt-0.2.0.dist-info/licenses/LICENSE,sha256=Msmy46KiAR2wmzeCe3kSl-wEt7ptbTZOAzdwUUyFq_M,1090
|
2
|
-
textformat/__init__.py,sha256=fyib7AnpkuEZ_PAzx6AO5SVKNTB5kyqk5NnJBXx7TYw,200
|
3
|
-
textformat/progress.py,sha256=9s0pndMjZ-S5dgcTZuibgAoB-oKeAAWvsjHqxij3Rtw,3253
|
4
|
-
textformat/table.py,sha256=GA7MsMWNuug61XxK9Ot3SOg5bl9zh5GO9Hae8-Y2qeE,604
|
5
|
-
textformat/textformat.py,sha256=yz6KstoDtrIgoA_b4ZFJAY9EfcylSjeeJ_euhPf8cpw,1121
|
6
|
-
textformat/word_art.py,sha256=GsJka0ukV23FTvhuyuGRChhNGOjTWOdKBK3h0e-LnX8,318
|
7
|
-
textfmt-0.2.0.dist-info/METADATA,sha256=TABcMirGCQYbNUoj0bp6jViVQKbAUQl0zjyE9UxcxmU,1567
|
8
|
-
textfmt-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
9
|
-
textfmt-0.2.0.dist-info/top_level.txt,sha256=Nf6G1sI6EqA2opfIdIio6ZqKNbpT2EtYf5cd0sKgPRo,11
|
10
|
-
textfmt-0.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|