colorful-log 0.4.0__tar.gz

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.
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.3
2
+ Name: colorful-log
3
+ Version: 0.4.0
4
+ Summary: tools that allow you to manipulate the style of the string
5
+ Author: Finsa-SC
6
+ Author-email: Finsa-SC <finsakusumaputra@gmail.com>
7
+ Requires-Dist: typing-extensions>=4.10
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+
11
+ # colorful-log
12
+
13
+ [![PyPI version](https://img.shields.io/pypi/v/colorful-log)](https://pypi.org/project/colorful-log/)
14
+ [![Python versions](https://img.shields.io/pypi/pyversions/colorful-log)](https://pypi.org/project/colorful-log/)
15
+ [![License](https://img.shields.io/github/license/Finsa-SC/colorfull-log)](LICENSE)
16
+
17
+ A small Python library for coloring and styling terminal text, without writing raw ANSI escape codes by hand.
18
+
19
+ ```python
20
+ from colorful_log import PrintLog, Colors
21
+
22
+ print(PrintLog("Build succeeded").color(Colors.GREEN).bold())
23
+ ```
24
+
25
+ ## Why colorful-log?
26
+
27
+ Terminal colors are done with escape sequences like `\033[1;32m`, which are hard to read, easy to get wrong, and easy to forget to reset. colorful-log wraps them in a small, readable API:
28
+
29
+ - **Readable code.** `PrintLog("Done").color(Colors.GREEN).bold()` says what it does. No escape codes to memorize.
30
+ - **Hex colors out of the box.** Use `"#6381f6"` directly, for both text and background.
31
+ - **Reusable styles.** Build a template once (for example an "error" style) and reuse it for any text.
32
+ - **Lightweight.** One small dependency (`typing-extensions`), fully typed, and works on Python 3.10+.
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install colorful-log
38
+ ```
39
+
40
+ Requires Python 3.10 or newer.
41
+
42
+ ## Features
43
+
44
+ - Foreground and background colors from built-in colors, hex codes, or your own registered colors
45
+ - Text styles: bold, dim, italic, underline, reverse, and strikethrough
46
+ - Method chaining and a short `|` operator for setting the text color
47
+ - Reusable templates with `copy()`, and an easy way to swap the text with a call
48
+ - Custom color registry, which can also override built-in colors
49
+ - `plain` property to get the text without any formatting
50
+ - Separate resets for colors (`reset()`) and styles (`reset_style()`)
51
+ - Clear `ValueError` for invalid hex colors
52
+
53
+ ## Usage
54
+
55
+ ### Basic
56
+
57
+ ```python
58
+ from colorful_log import PrintLog, Colors
59
+
60
+ print(PrintLog("Hello, World!").color(Colors.CYAN))
61
+ print(PrintLog("Hello, World!").color("#6381f6").background("#1e1e2e"))
62
+ ```
63
+
64
+ Built-in colors: `BLACK`, `RED`, `GREEN`, `YELLOW`, `BLUE`, `MAGENTA`, `CYAN`, `WHITE`.
65
+
66
+ ### Text styles
67
+
68
+ Styles can be chained in any order.
69
+
70
+ ```python
71
+ PrintLog("Important").bold().underline()
72
+ PrintLog("Deprecated").dim().strikethrough()
73
+ ```
74
+
75
+ Available styles: `bold()`, `dim()`, `italic()`, `underline()`, `revert()` (reverse video), `strikethrough()`.
76
+
77
+ ### Setting the color with `|`
78
+
79
+ The `|` operator is a shortcut for setting the text color.
80
+
81
+ ```python
82
+ print(PrintLog("Warning: disk almost full") | Colors.YELLOW)
83
+ print(PrintLog("Custom") | "#ff8800")
84
+ ```
85
+
86
+ ### Custom colors
87
+
88
+ Register a color once and use it anywhere.
89
+
90
+ ```python
91
+ Colors.register("brand", "#6381f6")
92
+
93
+ print(PrintLog("Brand color") | Colors.brand)
94
+ ```
95
+
96
+ Registering a name that already exists replaces it, including the built-in colors:
97
+
98
+ ```python
99
+ Colors.register("RED", "#ff5555") # Colors.RED is now #ff5555
100
+ ```
101
+
102
+ This is global, so it affects every use of `Colors.RED` in your program.
103
+ `RESET`, `register`, and names starting with `_` are reserved and raise a `ValueError`.
104
+
105
+ ### Templates
106
+
107
+ Calling a `PrintLog` instance replaces its text and keeps its formatting, so you can define a style once and reuse it.
108
+
109
+ ```python
110
+ error = PrintLog().color(Colors.WHITE).background(Colors.RED).bold()
111
+
112
+ print(error("Something went wrong"))
113
+ print(error("Another error"))
114
+ ```
115
+
116
+ > Calling an instance (and using `+`) modifies that same object and returns it.
117
+ > If you need independent results, use `copy()`:
118
+ >
119
+ > ```python
120
+ > a = error.copy()("First")
121
+ > b = error.copy()("Second")
122
+ > ```
123
+
124
+ ### Appending text
125
+
126
+ ```python
127
+ msg = PrintLog("Hello")
128
+ print(msg + ", World!") # appends to the existing text
129
+ ```
130
+
131
+ ### Plain text
132
+
133
+ `plain` returns the text without any color or style codes, which is useful for logs or files.
134
+
135
+ ```python
136
+ error("Disk full").plain # "Disk full"
137
+ ```
138
+
139
+ ### Resetting
140
+
141
+ ```python
142
+ style = PrintLog("text").bold().color(Colors.RED)
143
+
144
+ style.reset() # clears foreground and background colors, keeps styles
145
+ style.reset_style() # clears text styles, keeps colors
146
+ ```
147
+
148
+ ### Invalid colors
149
+
150
+ Invalid hex values raise a `ValueError` with a clear message.
151
+
152
+ ```python
153
+ PrintLog("x") | "#GGGGGG"
154
+ # ValueError: Invalid HEX color: '#GGGGGG'. Expected format: '#RRGGBB'.
155
+ ```
156
+
157
+ Only the `#RRGGBB` format is supported.
158
+
159
+ ## Notes
160
+
161
+ - Hex colors use 24-bit ("truecolor") ANSI codes. Most modern terminals support them, but some older ones do not.
162
+ - Output contains ANSI escape codes. Use `.plain` if you need clean text, for example when writing to a file.
163
+
164
+ ## License
165
+
166
+ Released under the [MIT License](LICENSE).
167
+
168
+ ## Author
169
+
170
+ Finsa Kusuma Putra ([@Finsa-SC](https://github.com/Finsa-SC))
@@ -0,0 +1,160 @@
1
+ # colorful-log
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/colorful-log)](https://pypi.org/project/colorful-log/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/colorful-log)](https://pypi.org/project/colorful-log/)
5
+ [![License](https://img.shields.io/github/license/Finsa-SC/colorfull-log)](LICENSE)
6
+
7
+ A small Python library for coloring and styling terminal text, without writing raw ANSI escape codes by hand.
8
+
9
+ ```python
10
+ from colorful_log import PrintLog, Colors
11
+
12
+ print(PrintLog("Build succeeded").color(Colors.GREEN).bold())
13
+ ```
14
+
15
+ ## Why colorful-log?
16
+
17
+ Terminal colors are done with escape sequences like `\033[1;32m`, which are hard to read, easy to get wrong, and easy to forget to reset. colorful-log wraps them in a small, readable API:
18
+
19
+ - **Readable code.** `PrintLog("Done").color(Colors.GREEN).bold()` says what it does. No escape codes to memorize.
20
+ - **Hex colors out of the box.** Use `"#6381f6"` directly, for both text and background.
21
+ - **Reusable styles.** Build a template once (for example an "error" style) and reuse it for any text.
22
+ - **Lightweight.** One small dependency (`typing-extensions`), fully typed, and works on Python 3.10+.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install colorful-log
28
+ ```
29
+
30
+ Requires Python 3.10 or newer.
31
+
32
+ ## Features
33
+
34
+ - Foreground and background colors from built-in colors, hex codes, or your own registered colors
35
+ - Text styles: bold, dim, italic, underline, reverse, and strikethrough
36
+ - Method chaining and a short `|` operator for setting the text color
37
+ - Reusable templates with `copy()`, and an easy way to swap the text with a call
38
+ - Custom color registry, which can also override built-in colors
39
+ - `plain` property to get the text without any formatting
40
+ - Separate resets for colors (`reset()`) and styles (`reset_style()`)
41
+ - Clear `ValueError` for invalid hex colors
42
+
43
+ ## Usage
44
+
45
+ ### Basic
46
+
47
+ ```python
48
+ from colorful_log import PrintLog, Colors
49
+
50
+ print(PrintLog("Hello, World!").color(Colors.CYAN))
51
+ print(PrintLog("Hello, World!").color("#6381f6").background("#1e1e2e"))
52
+ ```
53
+
54
+ Built-in colors: `BLACK`, `RED`, `GREEN`, `YELLOW`, `BLUE`, `MAGENTA`, `CYAN`, `WHITE`.
55
+
56
+ ### Text styles
57
+
58
+ Styles can be chained in any order.
59
+
60
+ ```python
61
+ PrintLog("Important").bold().underline()
62
+ PrintLog("Deprecated").dim().strikethrough()
63
+ ```
64
+
65
+ Available styles: `bold()`, `dim()`, `italic()`, `underline()`, `revert()` (reverse video), `strikethrough()`.
66
+
67
+ ### Setting the color with `|`
68
+
69
+ The `|` operator is a shortcut for setting the text color.
70
+
71
+ ```python
72
+ print(PrintLog("Warning: disk almost full") | Colors.YELLOW)
73
+ print(PrintLog("Custom") | "#ff8800")
74
+ ```
75
+
76
+ ### Custom colors
77
+
78
+ Register a color once and use it anywhere.
79
+
80
+ ```python
81
+ Colors.register("brand", "#6381f6")
82
+
83
+ print(PrintLog("Brand color") | Colors.brand)
84
+ ```
85
+
86
+ Registering a name that already exists replaces it, including the built-in colors:
87
+
88
+ ```python
89
+ Colors.register("RED", "#ff5555") # Colors.RED is now #ff5555
90
+ ```
91
+
92
+ This is global, so it affects every use of `Colors.RED` in your program.
93
+ `RESET`, `register`, and names starting with `_` are reserved and raise a `ValueError`.
94
+
95
+ ### Templates
96
+
97
+ Calling a `PrintLog` instance replaces its text and keeps its formatting, so you can define a style once and reuse it.
98
+
99
+ ```python
100
+ error = PrintLog().color(Colors.WHITE).background(Colors.RED).bold()
101
+
102
+ print(error("Something went wrong"))
103
+ print(error("Another error"))
104
+ ```
105
+
106
+ > Calling an instance (and using `+`) modifies that same object and returns it.
107
+ > If you need independent results, use `copy()`:
108
+ >
109
+ > ```python
110
+ > a = error.copy()("First")
111
+ > b = error.copy()("Second")
112
+ > ```
113
+
114
+ ### Appending text
115
+
116
+ ```python
117
+ msg = PrintLog("Hello")
118
+ print(msg + ", World!") # appends to the existing text
119
+ ```
120
+
121
+ ### Plain text
122
+
123
+ `plain` returns the text without any color or style codes, which is useful for logs or files.
124
+
125
+ ```python
126
+ error("Disk full").plain # "Disk full"
127
+ ```
128
+
129
+ ### Resetting
130
+
131
+ ```python
132
+ style = PrintLog("text").bold().color(Colors.RED)
133
+
134
+ style.reset() # clears foreground and background colors, keeps styles
135
+ style.reset_style() # clears text styles, keeps colors
136
+ ```
137
+
138
+ ### Invalid colors
139
+
140
+ Invalid hex values raise a `ValueError` with a clear message.
141
+
142
+ ```python
143
+ PrintLog("x") | "#GGGGGG"
144
+ # ValueError: Invalid HEX color: '#GGGGGG'. Expected format: '#RRGGBB'.
145
+ ```
146
+
147
+ Only the `#RRGGBB` format is supported.
148
+
149
+ ## Notes
150
+
151
+ - Hex colors use 24-bit ("truecolor") ANSI codes. Most modern terminals support them, but some older ones do not.
152
+ - Output contains ANSI escape codes. Use `.plain` if you need clean text, for example when writing to a file.
153
+
154
+ ## License
155
+
156
+ Released under the [MIT License](LICENSE).
157
+
158
+ ## Author
159
+
160
+ Finsa Kusuma Putra ([@Finsa-SC](https://github.com/Finsa-SC))
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "colorful-log"
3
+ version = "0.4.0"
4
+ description = "tools that allow you to manipulate the style of the string"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Finsa-SC", email = "finsakusumaputra@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "typing-extensions>=4.10",
12
+ ]
13
+
14
+ [build-system]
15
+ requires = ["uv_build>=0.11.18,<0.12.0"]
16
+ build-backend = "uv_build"
17
+
18
+ [dependency-groups]
19
+ dev = [
20
+ "pytest>=9.1.1",
21
+ ]
@@ -0,0 +1,3 @@
1
+ from colorful_log.logger import PrintLog
2
+ from colorful_log.colors import Colors
3
+ from colorful_log.styles import Styles
@@ -0,0 +1,34 @@
1
+ class ColorsMeta(type):
2
+ def __getattribute__(cls, name: str):
3
+ if not name.startswith("_"):
4
+ custom = type.__getattribute__(cls, "_custom_colors")
5
+ if name in custom:
6
+ return custom[name]
7
+
8
+ try:
9
+ return type.__getattribute__(cls, name)
10
+ except AttributeError as e:
11
+ raise AttributeError(
12
+ f"Colors has no attribute {name!r}"
13
+ ) from e
14
+
15
+ class Colors(metaclass=ColorsMeta):
16
+ _custom_colors: dict[str, str] = {}
17
+ _reserved = {"RESET", "register"}
18
+
19
+ @classmethod
20
+ def register(cls, name: str, value: str):
21
+ if name in cls._reserved or name.startswith("_"):
22
+ raise ValueError(f"{name!r} is reserved and cannot be registered")
23
+
24
+ cls._custom_colors[name] = value
25
+
26
+ BLACK = "0"
27
+ RED = "1"
28
+ GREEN = "2"
29
+ YELLOW = "3"
30
+ BLUE = "4"
31
+ MAGENTA = "5"
32
+ CYAN = "6"
33
+ WHITE = "7"
34
+ RESET = "9"
@@ -0,0 +1,36 @@
1
+ import re
2
+
3
+
4
+ class PrintLogEngine:
5
+ @staticmethod
6
+ def _set_text(num: str) -> str:
7
+ if num.startswith("\033"):
8
+ return num
9
+ return f"\033[3{num}m"
10
+
11
+ @staticmethod
12
+ def _set_back(num: str) -> str:
13
+ if num.startswith("\033"):
14
+ return num
15
+ return f"\033[4{num}m"
16
+
17
+ @staticmethod
18
+ def hex_to_rgb(hex_color: str, background: bool = False) -> str:
19
+ if not re.fullmatch(r"#[0-9a-fA-F]{6}", hex_color):
20
+ raise ValueError(
21
+ f"Invalid HEX color: {hex_color!r}. "
22
+ "Expected format: '#RRGGBB'."
23
+ )
24
+
25
+ hex_color = hex_color.lstrip('#')
26
+
27
+ if len(hex_color) != 6:
28
+ raise ValueError("HEX color must contain 6 digits.")
29
+
30
+ r = int(hex_color[0:2], 16)
31
+ g = int(hex_color[2:4], 16)
32
+ b = int(hex_color[4:6], 16)
33
+
34
+ code = "4" if background else "3"
35
+
36
+ return f"\033[{code}8;2;{r};{g};{b}m"
@@ -0,0 +1,67 @@
1
+ from colorful_log.engine import PrintLogEngine
2
+ from colorful_log.colors import Colors
3
+ from colorful_log.styles import StylesAPI
4
+ from copy import copy
5
+
6
+ class PrintLog(PrintLogEngine, StylesAPI):
7
+ def __init__(self, text: str = ""):
8
+ super().__init__("")
9
+ self._palette = Colors
10
+ self.text = text
11
+ self._text_color = ""
12
+ self._background_color = ""
13
+ self._text_style = ""
14
+
15
+ """Dunder"""
16
+ def __str__(self) -> str:
17
+ return (
18
+ f"{self._background_color}"
19
+ f"{self._text_color}"
20
+ f"{self._text_style}"
21
+ f"{self.text}"
22
+ f"\033[0m"
23
+ )
24
+
25
+ def __or__(self, other: str) -> "PrintLog":
26
+ if other.startswith('#'):
27
+ self._text_color = self.hex_to_rgb(other)
28
+ else:
29
+ self._text_color = self._set_text(other)
30
+ return self
31
+
32
+ def __add__(self, other) -> "PrintLog":
33
+ self.text += other
34
+ return self
35
+
36
+ def __call__(self, text: str):
37
+ self.text = text
38
+ return self
39
+
40
+ """API"""
41
+ ## Foreground and Background colors
42
+ def color(self, color: str) -> "PrintLog":
43
+ if color.startswith("#"):
44
+ self._text_color = self.hex_to_rgb(color)
45
+ else:
46
+ self._text_color = self._set_text(color)
47
+
48
+ return self
49
+
50
+ def background(self, color: str) -> "PrintLog":
51
+ if color.startswith("#"):
52
+ self._background_color = self.hex_to_rgb(color, background=True)
53
+ else:
54
+ self._background_color = self._set_back(color)
55
+ return self
56
+
57
+ def reset(self) -> "PrintLog":
58
+ self._text_color = self._set_text(self._palette.RESET)
59
+ self._background_color = self._set_back(self._palette.RESET)
60
+ return self
61
+
62
+ @property
63
+ def plain(self) -> str:
64
+ return self.text
65
+
66
+ def copy(self) -> "PrintLog":
67
+ return copy(self)
File without changes
@@ -0,0 +1,43 @@
1
+ from typing_extensions import Self
2
+
3
+ class Styles:
4
+ BOLD = "\033[1m"
5
+ DIM = "\033[2m"
6
+ ITALIC = "\033[3m"
7
+ UNDERLINE = "\033[4m"
8
+ REVERSE = "\033[7m"
9
+ STRIKETHROUGH = "\033[9m"
10
+ RESET = "\033[22m\033[23m\033[24m\033[27m\033[29m"
11
+
12
+ class StylesAPI:
13
+ def __init__(self, text_style: str):
14
+ self._text_style = text_style
15
+ self._styles = Styles()
16
+
17
+ def bold(self) -> Self:
18
+ self._text_style += self._styles.BOLD
19
+ return self
20
+
21
+ def italic(self) -> Self:
22
+ self._text_style += self._styles.ITALIC
23
+ return self
24
+
25
+ def underline(self) -> Self:
26
+ self._text_style += self._styles.UNDERLINE
27
+ return self
28
+
29
+ def revert(self) -> Self:
30
+ self._text_style += self._styles.REVERSE
31
+ return self
32
+
33
+ def dim(self) -> Self:
34
+ self._text_style += self._styles.DIM
35
+ return self
36
+
37
+ def strikethrough(self) -> Self:
38
+ self._text_style += self._styles.STRIKETHROUGH
39
+ return self
40
+
41
+ def reset_style(self) -> Self:
42
+ self._text_style = self._styles.RESET
43
+ return self