oakley 2.1.10__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.
- oakley-2.1.10/LICENCE +19 -0
- oakley-2.1.10/PKG-INFO +125 -0
- oakley-2.1.10/README.md +111 -0
- oakley-2.1.10/oakley/__init__.py +6 -0
- oakley-2.1.10/oakley/config.py +34 -0
- oakley-2.1.10/oakley/fancy_context_manager.py +32 -0
- oakley-2.1.10/oakley/fancy_string.py +237 -0
- oakley-2.1.10/oakley/message.py +342 -0
- oakley-2.1.10/oakley/mutable_class.py +541 -0
- oakley-2.1.10/oakley/print_stack.py +183 -0
- oakley-2.1.10/oakley/progress_bar.py +535 -0
- oakley-2.1.10/oakley/status.py +229 -0
- oakley-2.1.10/oakley/task.py +133 -0
- oakley-2.1.10/oakley.egg-info/PKG-INFO +125 -0
- oakley-2.1.10/oakley.egg-info/SOURCES.txt +18 -0
- oakley-2.1.10/oakley.egg-info/dependency_links.txt +1 -0
- oakley-2.1.10/oakley.egg-info/requires.txt +1 -0
- oakley-2.1.10/oakley.egg-info/top_level.txt +1 -0
- oakley-2.1.10/pyproject.toml +22 -0
- oakley-2.1.10/setup.cfg +4 -0
oakley-2.1.10/LICENCE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2018 The Python Packaging Authority
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
oakley-2.1.10/PKG-INFO
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oakley
|
|
3
|
+
Version: 2.1.10
|
|
4
|
+
Summary: A small collection of lightweight, opinionated utilities for printing fancy console output in Python: colored strings, pretty messages, simple progress bars, task context managers, and tiny system/status helpers.
|
|
5
|
+
Author-email: Jonas Wehrung-Montpezat <kiwi.grodoudou@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ProfesseurShadoko/oakley
|
|
8
|
+
Keywords: console,progress bar,utilities,colored output
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENCE
|
|
12
|
+
Requires-Dist: psutil>=5.9
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# oakley
|
|
17
|
+
|
|
18
|
+
A small collection of lightweight, opinionated utilities for printing "fancy" console output in Python: colored strings, pretty messages, simple progress bars, task context managers, and tiny system/status helpers.
|
|
19
|
+
|
|
20
|
+
This package is designed for developer convenience when running short scripts or CLI-style tasks. It provides a few cooperating classes that make it easy to print colored, indented, and optionally muted output, plus a couple of helpers for memory and time display.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- Colored strings with convenient format specifiers (`cstr`) — easy ANSI coloring and simple format shortcuts.
|
|
25
|
+
- `Message` class for categorized, pretty messages (info, warning, error, success).
|
|
26
|
+
- `Task` context manager to wrap and time operations, with neat completion/abort output.
|
|
27
|
+
- `ProgressBar` iterator wrapper with estimated remaining time and non-intrusive whisper messages.
|
|
28
|
+
- `MutableClass` base with global mute/tab behavior so multiple components coordinate console output.
|
|
29
|
+
- Small status helpers: `MemoryView`, `TODO` and `DateTime` for quick runtime info.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
You can downlaod the package with this command:
|
|
34
|
+
```bash
|
|
35
|
+
pip install oakley
|
|
36
|
+
```
|
|
37
|
+
Alternatively, you can download the most up-to-date version from my github repository:
|
|
38
|
+
```bash
|
|
39
|
+
pip install git+https://github.com/ProfesseurShadoko/oakley.git
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick examples
|
|
43
|
+
|
|
44
|
+
Colored strings
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from oakley import cstr
|
|
48
|
+
print(cstr('hello world').green().bold())
|
|
49
|
+
print(f"Progress: {cstr('ok'):g}") # short color spec
|
|
50
|
+
print(f"{cstr('Done'):gb}") # in green and bold
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Pretty messages
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from oakley import Message
|
|
57
|
+
Message("Build succeeded", "#") # green success prefix
|
|
58
|
+
Message("Something might be wrong", "?")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Tasks and timing
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from oakley import Task
|
|
65
|
+
import time
|
|
66
|
+
|
|
67
|
+
with Task("Compute something heavy"):
|
|
68
|
+
time.sleep(1.2)
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Progress bar
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from oakley import ProgressBar
|
|
76
|
+
import time
|
|
77
|
+
|
|
78
|
+
for i in ProgressBar(range(50), size=50):
|
|
79
|
+
time.sleep(0.02)
|
|
80
|
+
if i == 25:
|
|
81
|
+
ProgressBar.whisper("Halfway there!")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Status helpers
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from oakley import MemoryView, TODO
|
|
88
|
+
MemoryView() # prints a short memory usage line (requires psutil)
|
|
89
|
+
TODO("Refactor the parser")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Mute and indentation
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from oakley import MutableClass, Message
|
|
96
|
+
|
|
97
|
+
MutableClass.mute() # globally mute printing
|
|
98
|
+
MutableClass.unmute()
|
|
99
|
+
|
|
100
|
+
with Message.mute():
|
|
101
|
+
Message.print("This won't be printed")
|
|
102
|
+
Message.print("This will be printed again!")
|
|
103
|
+
|
|
104
|
+
with Message("The following messages will be indented"): # increase indentation for nested prints
|
|
105
|
+
Message.print("This will be indented")
|
|
106
|
+
MemoryView() # also indented
|
|
107
|
+
Message("This won't be indented")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Examples
|
|
111
|
+
|
|
112
|
+
Take a look at [this notebook](https://github.com/ProfesseurShadoko/oakley/blob/main/example.ipynb) for the most detailed and up to date examples.
|
|
113
|
+
|
|
114
|
+
Alternatively, run the command:
|
|
115
|
+
```bash
|
|
116
|
+
python -m oakley.<filename_without_dot_py>
|
|
117
|
+
```
|
|
118
|
+
to see examples for each object.
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
## Development notes
|
|
122
|
+
|
|
123
|
+
- The package is intentionally tiny and uses ANSI escape sequences for coloring; compatibility is best on UNIX-like terminals.
|
|
124
|
+
- `MemoryView` depends on `psutil` — the package runs fine even when `psutil` is not available, but the `MemoryView` object cannot be used.
|
|
125
|
+
- There are simple demo blocks in each module under `if __name__ == '__main__'` for manual testing.
|
oakley-2.1.10/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
|
|
2
|
+
# oakley
|
|
3
|
+
|
|
4
|
+
A small collection of lightweight, opinionated utilities for printing "fancy" console output in Python: colored strings, pretty messages, simple progress bars, task context managers, and tiny system/status helpers.
|
|
5
|
+
|
|
6
|
+
This package is designed for developer convenience when running short scripts or CLI-style tasks. It provides a few cooperating classes that make it easy to print colored, indented, and optionally muted output, plus a couple of helpers for memory and time display.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Colored strings with convenient format specifiers (`cstr`) — easy ANSI coloring and simple format shortcuts.
|
|
11
|
+
- `Message` class for categorized, pretty messages (info, warning, error, success).
|
|
12
|
+
- `Task` context manager to wrap and time operations, with neat completion/abort output.
|
|
13
|
+
- `ProgressBar` iterator wrapper with estimated remaining time and non-intrusive whisper messages.
|
|
14
|
+
- `MutableClass` base with global mute/tab behavior so multiple components coordinate console output.
|
|
15
|
+
- Small status helpers: `MemoryView`, `TODO` and `DateTime` for quick runtime info.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
You can downlaod the package with this command:
|
|
20
|
+
```bash
|
|
21
|
+
pip install oakley
|
|
22
|
+
```
|
|
23
|
+
Alternatively, you can download the most up-to-date version from my github repository:
|
|
24
|
+
```bash
|
|
25
|
+
pip install git+https://github.com/ProfesseurShadoko/oakley.git
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick examples
|
|
29
|
+
|
|
30
|
+
Colored strings
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from oakley import cstr
|
|
34
|
+
print(cstr('hello world').green().bold())
|
|
35
|
+
print(f"Progress: {cstr('ok'):g}") # short color spec
|
|
36
|
+
print(f"{cstr('Done'):gb}") # in green and bold
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Pretty messages
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from oakley import Message
|
|
43
|
+
Message("Build succeeded", "#") # green success prefix
|
|
44
|
+
Message("Something might be wrong", "?")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Tasks and timing
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from oakley import Task
|
|
51
|
+
import time
|
|
52
|
+
|
|
53
|
+
with Task("Compute something heavy"):
|
|
54
|
+
time.sleep(1.2)
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Progress bar
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from oakley import ProgressBar
|
|
62
|
+
import time
|
|
63
|
+
|
|
64
|
+
for i in ProgressBar(range(50), size=50):
|
|
65
|
+
time.sleep(0.02)
|
|
66
|
+
if i == 25:
|
|
67
|
+
ProgressBar.whisper("Halfway there!")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Status helpers
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from oakley import MemoryView, TODO
|
|
74
|
+
MemoryView() # prints a short memory usage line (requires psutil)
|
|
75
|
+
TODO("Refactor the parser")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Mute and indentation
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from oakley import MutableClass, Message
|
|
82
|
+
|
|
83
|
+
MutableClass.mute() # globally mute printing
|
|
84
|
+
MutableClass.unmute()
|
|
85
|
+
|
|
86
|
+
with Message.mute():
|
|
87
|
+
Message.print("This won't be printed")
|
|
88
|
+
Message.print("This will be printed again!")
|
|
89
|
+
|
|
90
|
+
with Message("The following messages will be indented"): # increase indentation for nested prints
|
|
91
|
+
Message.print("This will be indented")
|
|
92
|
+
MemoryView() # also indented
|
|
93
|
+
Message("This won't be indented")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
Take a look at [this notebook](https://github.com/ProfesseurShadoko/oakley/blob/main/example.ipynb) for the most detailed and up to date examples.
|
|
99
|
+
|
|
100
|
+
Alternatively, run the command:
|
|
101
|
+
```bash
|
|
102
|
+
python -m oakley.<filename_without_dot_py>
|
|
103
|
+
```
|
|
104
|
+
to see examples for each object.
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
## Development notes
|
|
108
|
+
|
|
109
|
+
- The package is intentionally tiny and uses ANSI escape sequences for coloring; compatibility is best on UNIX-like terminals.
|
|
110
|
+
- `MemoryView` depends on `psutil` — the package runs fine even when `psutil` is not available, but the `MemoryView` object cannot be used.
|
|
111
|
+
- There are simple demo blocks in each module under `if __name__ == '__main__'` for manual testing.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
_default_config = {
|
|
6
|
+
"terminal_width": -1, # if -1, auto-detect,
|
|
7
|
+
"spinner": [],
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
# 1. Load the config.json file if it exists.
|
|
11
|
+
_config_path = os.path.join(os.path.dirname(__file__), 'config.json')
|
|
12
|
+
|
|
13
|
+
if not os.path.exists(_config_path):
|
|
14
|
+
# if not exist create it with default settings # should not happen!
|
|
15
|
+
with open(_config_path, 'w') as f:
|
|
16
|
+
json.dump(_default_config, f, indent=4)
|
|
17
|
+
|
|
18
|
+
with open(_config_path, 'r') as f:
|
|
19
|
+
file_config = json.load(f)
|
|
20
|
+
_default_config.update(file_config)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ConfigDict(dict):
|
|
24
|
+
|
|
25
|
+
def __setitem__(self, key, value):
|
|
26
|
+
super().__setitem__(key, value)
|
|
27
|
+
# directly update the config file on every change
|
|
28
|
+
with open(_config_path, 'w') as f:
|
|
29
|
+
json.dump(self, f, indent=4)
|
|
30
|
+
|
|
31
|
+
print("[C] Config updated:", key, "=", value)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
config = ConfigDict(_default_config) # put the dictionnary inside. default config contains the loaded file already.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
import traceback as tb
|
|
3
|
+
from .fancy_string import cstr
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FancyCM:
|
|
8
|
+
"""
|
|
9
|
+
Updates some counter on enter/exit.
|
|
10
|
+
"""
|
|
11
|
+
lvl = 0
|
|
12
|
+
|
|
13
|
+
def __enter__(self):
|
|
14
|
+
FancyCM.lvl += 1
|
|
15
|
+
|
|
16
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
17
|
+
FancyCM.lvl -= 1
|
|
18
|
+
if exc_type and FancyCM.lvl == 0:
|
|
19
|
+
print(f"Exception occurred: {cstr(exc_type.__name__):r} ({cstr(exc_value):y})")
|
|
20
|
+
tb.print_tb(traceback)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == '__main__':
|
|
25
|
+
|
|
26
|
+
with FancyCM():
|
|
27
|
+
print("This was printed inside the context manager")
|
|
28
|
+
with FancyCM():
|
|
29
|
+
print("This was printed inside the nested context manager. Now an error will occure.")
|
|
30
|
+
x = 1/0
|
|
31
|
+
print("This will not be printed.")
|
|
32
|
+
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import Literal
|
|
3
|
+
import re
|
|
4
|
+
ANSI_RE = re.compile(r'\033\[[0-9;]*m')
|
|
5
|
+
from .print_stack import in_notebook
|
|
6
|
+
|
|
7
|
+
class Cstr(str):
|
|
8
|
+
"""
|
|
9
|
+
Class inheritting for type string, with a few additional methods for coloring the string when printed to the console.
|
|
10
|
+
|
|
11
|
+
Methods:
|
|
12
|
+
green: Returns the string in green color
|
|
13
|
+
blue: Returns the string in blue color
|
|
14
|
+
red: Returns the string in red color
|
|
15
|
+
yellow: Returns the string in yellow color
|
|
16
|
+
bold: Returns the string in bold font
|
|
17
|
+
underline: Returns the string with underline
|
|
18
|
+
italic: Returns the string in italic font
|
|
19
|
+
strikethrough: Returns the string with strikethrough
|
|
20
|
+
highlight: Returns the string with highlighted background
|
|
21
|
+
|
|
22
|
+
Format:
|
|
23
|
+
print(f'{ColoredString("This is a colored string"):green}') # prints the string in green color
|
|
24
|
+
print(f'{ColoredString("This is a colored string"):g}') # prints the string in green color
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
_COLORS = {
|
|
29
|
+
'green': '\033[92m',
|
|
30
|
+
'blue': '\033[94m',
|
|
31
|
+
'red': '\033[91m',
|
|
32
|
+
'yellow': '\033[93m',
|
|
33
|
+
'magenta': '\033[95m',
|
|
34
|
+
'cyan': '\033[96m',
|
|
35
|
+
'reset': '\033[0m',
|
|
36
|
+
|
|
37
|
+
'bold': '\033[1m',
|
|
38
|
+
'underline': '\033[4m',
|
|
39
|
+
'italic': '\033[3m',
|
|
40
|
+
'strikethrough': '\033[9m',
|
|
41
|
+
'highlight': '\033[7m',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def __init__(self,string:str):
|
|
45
|
+
"""
|
|
46
|
+
Args:
|
|
47
|
+
string (str): The string to be printed in color (can also be anything that can be converted to a string using str() function)
|
|
48
|
+
"""
|
|
49
|
+
super().__init__() # how the hell does this work???
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
##############
|
|
53
|
+
### COLORS ###
|
|
54
|
+
##############
|
|
55
|
+
|
|
56
|
+
def green(self) -> 'Cstr':
|
|
57
|
+
return self.__class__(
|
|
58
|
+
self._COLORS["green"] + self + self._COLORS["reset"]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def blue(self) -> 'Cstr':
|
|
62
|
+
return self.__class__(
|
|
63
|
+
self._COLORS["blue"] + self + self._COLORS["reset"]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def red(self) -> 'Cstr':
|
|
67
|
+
return self.__class__(
|
|
68
|
+
self._COLORS["red"] + self + self._COLORS["reset"]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def yellow(self) -> 'Cstr':
|
|
72
|
+
return self.__class__(
|
|
73
|
+
self._COLORS["yellow"] + self + self._COLORS["reset"]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def magenta(self) -> 'Cstr':
|
|
77
|
+
return self.__class__(
|
|
78
|
+
self._COLORS["magenta"] + self + self._COLORS["reset"]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def cyan(self) -> 'Cstr':
|
|
82
|
+
return self.__class__(
|
|
83
|
+
self._COLORS["cyan"] + self + self._COLORS["reset"]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def white(self) -> 'Cstr':
|
|
87
|
+
return self
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
#############
|
|
91
|
+
### FONTS ###
|
|
92
|
+
#############
|
|
93
|
+
|
|
94
|
+
def bold(self) -> 'Cstr':
|
|
95
|
+
return self.__class__(
|
|
96
|
+
self._COLORS["bold"] + self + self._COLORS["reset"]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def underline(self) -> 'Cstr':
|
|
100
|
+
return self.__class__(
|
|
101
|
+
self._COLORS["underline"] + self + self._COLORS["reset"]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def italic(self) -> 'Cstr':
|
|
105
|
+
return self.__class__(
|
|
106
|
+
self._COLORS["italic"] + self + self._COLORS["reset"]
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def strikethrough(self) -> 'Cstr':
|
|
110
|
+
return self.__class__(
|
|
111
|
+
self._COLORS["strikethrough"] + self + self._COLORS["reset"]
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def highlight(self) -> 'Cstr':
|
|
115
|
+
return self.__class__(
|
|
116
|
+
self._COLORS["highlight"] + self + self._COLORS["reset"]
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
##############
|
|
121
|
+
### FORMAT ###
|
|
122
|
+
##############
|
|
123
|
+
|
|
124
|
+
def __format__(self, format_spec:str) -> 'Cstr':
|
|
125
|
+
if not format_spec:
|
|
126
|
+
return self
|
|
127
|
+
|
|
128
|
+
colors = [
|
|
129
|
+
'green', 'blue', 'red', 'yellow', 'magenta', 'cyan', 'white'
|
|
130
|
+
]
|
|
131
|
+
fonts = [
|
|
132
|
+
'bold', 'underline', 'italic', 'strikethrough', 'highlight'
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
color_spec = format_spec[0]
|
|
136
|
+
if len(format_spec) > 1:
|
|
137
|
+
font_spec = format_spec[1:]
|
|
138
|
+
else:
|
|
139
|
+
font_spec = ''
|
|
140
|
+
assert len(format_spec) <= 2, f"Invalid format specifier: {format_spec}. Must be one or two characters long."
|
|
141
|
+
|
|
142
|
+
allowed_color_specs = [c[0] for c in colors]
|
|
143
|
+
allowed_font_specs = [f[0] for f in fonts] + ['']
|
|
144
|
+
|
|
145
|
+
assert color_spec in allowed_color_specs, f"Invalid format specifier: {format_spec}. Must be one of {allowed_color_specs}."
|
|
146
|
+
assert font_spec in allowed_font_specs, f"Invalid format specifier: {format_spec}. Must be one of {allowed_font_specs}."
|
|
147
|
+
|
|
148
|
+
out = self
|
|
149
|
+
|
|
150
|
+
# 1. Add the color to the string
|
|
151
|
+
color = [c for c in colors if c.startswith(color_spec)][0] # there is only one anyway
|
|
152
|
+
out = getattr(out, color)()
|
|
153
|
+
|
|
154
|
+
# 2. Add the font to the string
|
|
155
|
+
if font_spec:
|
|
156
|
+
font = [f for f in fonts if f.startswith(font_spec)][0]
|
|
157
|
+
out = getattr(out, font)()
|
|
158
|
+
|
|
159
|
+
return out
|
|
160
|
+
|
|
161
|
+
def length(self) -> int:
|
|
162
|
+
"""
|
|
163
|
+
Returns the length of the string without ANSI escape codes.
|
|
164
|
+
"""
|
|
165
|
+
if not in_notebook:
|
|
166
|
+
return len(ANSI_RE.sub('', self))
|
|
167
|
+
else:
|
|
168
|
+
return len(self)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def cstr(obj:object, format_spec:str='') -> 'Cstr':
|
|
172
|
+
"""
|
|
173
|
+
Convert an object into a color-capable string (`Cstr`).
|
|
174
|
+
|
|
175
|
+
This function formats `obj` using Python's built-in ``format`` machinery
|
|
176
|
+
(e.g. for floats, integers, or custom ``__format__`` implementations),
|
|
177
|
+
and then wraps the resulting text into a :class:`Cstr` object.
|
|
178
|
+
The returned `Cstr` instance supports ANSI colorization, text styles
|
|
179
|
+
(bold, underline, italic, …), and compact format specifiers.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
obj : object
|
|
184
|
+
The object to convert to a colored string. Any object that can be
|
|
185
|
+
formatted via ``format(obj, format_spec)`` is accepted.
|
|
186
|
+
format_spec : str, optional
|
|
187
|
+
Standard Python format specification applied *before*
|
|
188
|
+
converting the output to a `Cstr`.
|
|
189
|
+
For example: ``'.2f'``, ``'05d'``, ``'>10s'``, etc.
|
|
190
|
+
|
|
191
|
+
Returns
|
|
192
|
+
-------
|
|
193
|
+
Cstr
|
|
194
|
+
A colored-string wrapper that supports methods such as
|
|
195
|
+
``.green()``, ``.red()``, ``.bold()``, ``.underline()``,
|
|
196
|
+
as well as compact format usage inside f-strings (e.g. ``:gb``).
|
|
197
|
+
|
|
198
|
+
Notes
|
|
199
|
+
-----
|
|
200
|
+
- Color and style transformations are applied *after* formatting.
|
|
201
|
+
- Inside f-strings, short format specifiers allow concise styling:
|
|
202
|
+
``g`` → green, ``r`` → red, ``b`` → blue,
|
|
203
|
+
combined with ``b`` for bold, ``u`` for underline, …
|
|
204
|
+
Examples: ``:gb`` (green + bold), ``:ru`` (red + underline).
|
|
205
|
+
|
|
206
|
+
Examples
|
|
207
|
+
--------
|
|
208
|
+
Basic usage:
|
|
209
|
+
|
|
210
|
+
>>> print(cstr("Hello").green())
|
|
211
|
+
Hello # in green
|
|
212
|
+
|
|
213
|
+
With numeric formatting:
|
|
214
|
+
|
|
215
|
+
>>> x = 3.14159
|
|
216
|
+
>>> print(cstr(x, '.2f').cyan())
|
|
217
|
+
3.14 # in cyan
|
|
218
|
+
|
|
219
|
+
Using short form styling inside an f-string:
|
|
220
|
+
|
|
221
|
+
>>> print(f"Value: {cstr('OK'):gb}")
|
|
222
|
+
Value: OK # green + bold
|
|
223
|
+
"""
|
|
224
|
+
return Cstr(format(obj, format_spec))
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
if __name__ == '__main__':
|
|
229
|
+
x = 3.1416
|
|
230
|
+
print(cstr(x, '.2f').green().bold())
|
|
231
|
+
print(f"This was the number PI in {cstr('green'):g} color.")
|
|
232
|
+
|
|
233
|
+
# testing the length method
|
|
234
|
+
s = "Hello, World!"
|
|
235
|
+
colored_s = cstr(s).red().bold().underline()
|
|
236
|
+
print(f"Original length: {len(s)}, Colored length: {colored_s.length()}, len(colored_s): {len(colored_s)}")
|
|
237
|
+
|