bear-utils 0.9.2__py3-none-any.whl → 0.9.3__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.
- bear_utils/_internal/_version.py +1 -1
- bear_utils/_internal/cli.py +20 -6
- bear_utils/_internal/debug.py +15 -2
- bear_utils/cli/_get_version.py +60 -59
- bear_utils/constants/_exit_code.py +1 -1
- bear_utils/constants/_meta.py +78 -16
- bear_utils/extras/__init__.py +6 -0
- bear_utils/extras/_tools.py +11 -119
- bear_utils/extras/_zapper.py +399 -0
- bear_utils/graphics/font/_utils.py +188 -0
- bear_utils/graphics/font/block_font.py +59 -7
- {bear_utils-0.9.2.dist-info → bear_utils-0.9.3.dist-info}/METADATA +2 -1
- {bear_utils-0.9.2.dist-info → bear_utils-0.9.3.dist-info}/RECORD +14 -13
- bear_utils-0.9.2.dist-info/entry_points.txt +0 -3
- {bear_utils-0.9.2.dist-info → bear_utils-0.9.3.dist-info}/WHEEL +0 -0
bear_utils/_internal/_version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
version = "0.9.
|
1
|
+
version = "0.9.3"
|
bear_utils/_internal/cli.py
CHANGED
@@ -54,7 +54,7 @@ def get_version() -> ExitCode:
|
|
54
54
|
return ExitCode.SUCCESS
|
55
55
|
|
56
56
|
|
57
|
-
def bump_version() -> ExitCode:
|
57
|
+
def bump_version(args: list[str] | None = None) -> ExitCode:
|
58
58
|
"""CLI command to bump the version of the package."""
|
59
59
|
parser = ArgumentParser(description="Bump the version of the package.")
|
60
60
|
parser.add_argument(
|
@@ -63,21 +63,24 @@ def bump_version() -> ExitCode:
|
|
63
63
|
choices=VALID_BUMP_TYPES,
|
64
64
|
help=f"Type of version bump: {', '.join(VALID_BUMP_TYPES)}",
|
65
65
|
)
|
66
|
-
|
67
|
-
|
68
|
-
return cli_bump(bump_args)
|
66
|
+
_args: Namespace = parser.parse_args(args or sys.argv[1:])
|
67
|
+
return cli_bump([_args.bump_type, debug.__PACKAGE_NAME__, _version])
|
69
68
|
|
70
69
|
|
71
70
|
def get_parser() -> ArgumentParser:
|
72
71
|
name = debug._get_name()
|
73
72
|
parser = ArgumentParser(description=name.capitalize(), prog=name, exit_on_error=False)
|
74
73
|
parser.add_argument("-V", "--version", action=_Version, help="Print the version of the package")
|
74
|
+
subparser = parser.add_subparsers(dest="command", required=False, help="Available commands")
|
75
|
+
subparser.add_parser("get-version", help="Get the current version of the package")
|
76
|
+
bump = subparser.add_parser("bump-version", help="Bump the version of the package")
|
77
|
+
bump.add_argument("bump_type", type=str, choices=VALID_BUMP_TYPES, help="major, minor, or patch")
|
75
78
|
parser.add_argument("--about", action=_About, help="Print information about the package")
|
76
79
|
parser.add_argument("--debug_info", action=_DebugInfo, help="Print debug information")
|
77
80
|
return parser
|
78
81
|
|
79
82
|
|
80
|
-
def main(args: list[str] | None = None) ->
|
83
|
+
def main(args: list[str] | None = None) -> ExitCode:
|
81
84
|
"""Main entry point for the CLI.
|
82
85
|
|
83
86
|
This function is called when the CLI is executed. It can be used to
|
@@ -94,7 +97,18 @@ def main(args: list[str] | None = None) -> int:
|
|
94
97
|
try:
|
95
98
|
parser: ArgumentParser = get_parser()
|
96
99
|
opts: Namespace = parser.parse_args(args)
|
97
|
-
|
100
|
+
command = opts.command
|
101
|
+
if command is None:
|
102
|
+
parser.print_help()
|
103
|
+
return ExitCode.SUCCESS
|
104
|
+
if command == "get-version":
|
105
|
+
return get_version()
|
106
|
+
if command == "bump-version":
|
107
|
+
if not hasattr(opts, "bump_type"):
|
108
|
+
print("Error: 'bump-version' command requires a 'bump_type' argument.", file=sys.stderr)
|
109
|
+
return ExitCode.FAILURE
|
110
|
+
bump_type = opts.bump_type
|
111
|
+
return bump_version([bump_type])
|
98
112
|
except Exception as e:
|
99
113
|
print(f"Error initializing CLI: {e}", file=sys.stderr)
|
100
114
|
return ExitCode.FAILURE
|
bear_utils/_internal/debug.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from dataclasses import dataclass
|
3
|
+
from dataclasses import dataclass, field
|
4
4
|
import importlib.metadata
|
5
5
|
from importlib.metadata import PackageNotFoundError, metadata, version
|
6
6
|
import os
|
@@ -8,6 +8,7 @@ import platform
|
|
8
8
|
import sys
|
9
9
|
|
10
10
|
from bear_utils._internal._version import version as _version
|
11
|
+
from bear_utils.cli._get_version import Version
|
11
12
|
|
12
13
|
__PACKAGE_NAME__ = "bear-utils"
|
13
14
|
|
@@ -20,9 +21,20 @@ class _Package:
|
|
20
21
|
"""Package name."""
|
21
22
|
version: str = _version
|
22
23
|
"""Package version."""
|
24
|
+
_version: Version = field(default_factory=lambda: Version.from_string(_version))
|
23
25
|
description: str = "No description available."
|
24
26
|
"""Package description."""
|
25
27
|
|
28
|
+
def __post_init__(self) -> None:
|
29
|
+
"""Post-initialization to ensure version is a string."""
|
30
|
+
if not isinstance(self.version, str) or "0.0.0" in self.version:
|
31
|
+
self.version = version(self.name) if self.name else "0.0.0"
|
32
|
+
if not self.description:
|
33
|
+
try:
|
34
|
+
self.description = metadata(self.name)["Summary"]
|
35
|
+
except PackageNotFoundError:
|
36
|
+
self.description = "No description available."
|
37
|
+
|
26
38
|
def __str__(self) -> str:
|
27
39
|
"""String representation of the package information."""
|
28
40
|
return f"{self.name} v{self.version}: {self.description}"
|
@@ -158,4 +170,5 @@ def _print_debug_info() -> None:
|
|
158
170
|
|
159
171
|
|
160
172
|
if __name__ == "__main__":
|
161
|
-
_print_debug_info()
|
173
|
+
# _print_debug_info()
|
174
|
+
print(_get_package_info())
|
bear_utils/cli/_get_version.py
CHANGED
@@ -10,42 +10,40 @@ from typing import Literal, Self
|
|
10
10
|
from pydantic import BaseModel
|
11
11
|
|
12
12
|
from bear_utils.constants import ExitCode
|
13
|
-
from bear_utils.constants._meta import
|
13
|
+
from bear_utils.constants._meta import IntValue as Value, RichIntEnum
|
14
|
+
from bear_utils.extras import zap_as
|
14
15
|
|
15
16
|
|
16
|
-
class
|
17
|
+
class VerParts(RichIntEnum):
|
17
18
|
"""Enumeration for version parts."""
|
18
19
|
|
19
|
-
MAJOR = Value(
|
20
|
-
MINOR = Value(
|
21
|
-
PATCH = Value(
|
20
|
+
MAJOR = Value(0, "major")
|
21
|
+
MINOR = Value(1, "minor")
|
22
|
+
PATCH = Value(2, "patch")
|
22
23
|
|
23
24
|
@classmethod
|
24
25
|
def choices(cls) -> list[str]:
|
25
26
|
"""Return a list of valid version parts."""
|
26
|
-
return [version_part.
|
27
|
+
return [version_part.text for version_part in cls]
|
27
28
|
|
28
29
|
@classmethod
|
29
|
-
def
|
30
|
-
"""Return the number of
|
30
|
+
def parts(cls) -> int:
|
31
|
+
"""Return the total number of version parts."""
|
31
32
|
return len(cls.choices())
|
32
33
|
|
33
34
|
|
34
|
-
VALID_BUMP_TYPES: list[str] =
|
35
|
-
|
36
|
-
MAJOR = VersionParts.MAJOR.str()
|
37
|
-
MINOR = VersionParts.MINOR.str()
|
38
|
-
PATCH = VersionParts.PATCH.str()
|
35
|
+
VALID_BUMP_TYPES: list[str] = VerParts.choices()
|
36
|
+
ALL_PARTS: int = VerParts.parts()
|
39
37
|
|
40
38
|
|
41
39
|
class Version(BaseModel):
|
42
40
|
"""Model to represent a version string."""
|
43
41
|
|
44
|
-
major: int
|
42
|
+
major: int = 0
|
45
43
|
"""Major version number."""
|
46
|
-
minor: int
|
44
|
+
minor: int = 0
|
47
45
|
"""Minor version number."""
|
48
|
-
patch: int
|
46
|
+
patch: int = 0
|
49
47
|
"""Patch version number."""
|
50
48
|
|
51
49
|
@classmethod
|
@@ -61,10 +59,17 @@ class Version(BaseModel):
|
|
61
59
|
Raises:
|
62
60
|
ValueError: If the version string is not in the correct format.
|
63
61
|
"""
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
try:
|
63
|
+
major, minor, patch = zap_as("-+", version_str, 3, replace=".", func=int)
|
64
|
+
return cls(major=int(major), minor=int(minor), patch=int(patch))
|
65
|
+
except ValueError as e:
|
66
|
+
raise ValueError(
|
67
|
+
f"Invalid version string format: {version_str}. Expected integers for major, minor, and patch."
|
68
|
+
) from e
|
69
|
+
|
70
|
+
def increment(self, attr_name: str) -> None:
|
71
|
+
"""Increment the specified part of the version."""
|
72
|
+
setattr(self, attr_name, getattr(self, attr_name) + 1)
|
68
73
|
|
69
74
|
@property
|
70
75
|
def version_string(self) -> str:
|
@@ -75,30 +80,22 @@ class Version(BaseModel):
|
|
75
80
|
"""
|
76
81
|
return f"{self.major}.{self.minor}.{self.patch}"
|
77
82
|
|
78
|
-
def
|
79
|
-
"""
|
83
|
+
def default(self, part: str) -> None:
|
84
|
+
"""Clear the specified part of the version.
|
80
85
|
|
81
86
|
Args:
|
82
|
-
|
83
|
-
|
84
|
-
Returns:
|
85
|
-
A new version string.
|
86
|
-
|
87
|
-
Raises:
|
88
|
-
ValueError: If the bump_type is unsupported.
|
87
|
+
part: The part of the version to clear.
|
89
88
|
"""
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
self.
|
100
|
-
case _:
|
101
|
-
raise ValueError(f"Unsupported bump type: {bump_type}")
|
89
|
+
if hasattr(self, part):
|
90
|
+
setattr(self, part, 0)
|
91
|
+
|
92
|
+
def new_version(self, bump_type: str) -> Version:
|
93
|
+
"""Return a new version string based on the bump type."""
|
94
|
+
bump_part: VerParts = VerParts.get(bump_type, default=VerParts.PATCH)
|
95
|
+
self.increment(bump_part.text)
|
96
|
+
for part in VerParts:
|
97
|
+
if part.value > bump_part.value:
|
98
|
+
self.default(part.text)
|
102
99
|
return self
|
103
100
|
|
104
101
|
@classmethod
|
@@ -135,6 +132,24 @@ def _bump_version(version: str, bump_type: Literal["major", "minor", "patch"]) -
|
|
135
132
|
return ver.new_version(bump_type)
|
136
133
|
|
137
134
|
|
135
|
+
def _get_version(package_name: str) -> str:
|
136
|
+
"""Get the version of the specified package.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
package_name: The name of the package to get the version for.
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
A Version instance representing the current version of the package.
|
143
|
+
|
144
|
+
Raises:
|
145
|
+
PackageNotFoundError: If the package is not found.
|
146
|
+
"""
|
147
|
+
record = StringIO()
|
148
|
+
with redirect_stdout(record):
|
149
|
+
cli_get_version([package_name])
|
150
|
+
return record.getvalue().strip()
|
151
|
+
|
152
|
+
|
138
153
|
def cli_get_version(args: list[str] | None = None) -> ExitCode:
|
139
154
|
"""Get the version of the current package.
|
140
155
|
|
@@ -159,24 +174,6 @@ def cli_get_version(args: list[str] | None = None) -> ExitCode:
|
|
159
174
|
return ExitCode.SUCCESS
|
160
175
|
|
161
176
|
|
162
|
-
def _get_version(package_name: str) -> str:
|
163
|
-
"""Get the version of the specified package.
|
164
|
-
|
165
|
-
Args:
|
166
|
-
package_name: The name of the package to get the version for.
|
167
|
-
|
168
|
-
Returns:
|
169
|
-
A Version instance representing the current version of the package.
|
170
|
-
|
171
|
-
Raises:
|
172
|
-
PackageNotFoundError: If the package is not found.
|
173
|
-
"""
|
174
|
-
record = StringIO()
|
175
|
-
with redirect_stdout(record):
|
176
|
-
cli_get_version([package_name])
|
177
|
-
return record.getvalue().strip()
|
178
|
-
|
179
|
-
|
180
177
|
def cli_bump(args: list[str] | None = None) -> ExitCode:
|
181
178
|
if args is None:
|
182
179
|
args = sys.argv[1:]
|
@@ -204,3 +201,7 @@ def cli_bump(args: list[str] | None = None) -> ExitCode:
|
|
204
201
|
except Exception as e:
|
205
202
|
print(f"Unexpected error: {e}")
|
206
203
|
return ExitCode.FAILURE
|
204
|
+
|
205
|
+
|
206
|
+
if __name__ == "__main__":
|
207
|
+
cli_bump(["patch", "bear-utils", "0.9.2-fart.build-alpha"])
|
bear_utils/constants/_meta.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
from contextlib import suppress
|
1
2
|
from dataclasses import dataclass
|
2
3
|
from enum import IntEnum, StrEnum
|
3
|
-
from typing import Any, Self, TextIO
|
4
|
+
from typing import Any, Self, TextIO, overload
|
4
5
|
|
5
6
|
|
6
7
|
@dataclass(frozen=True)
|
@@ -9,6 +10,7 @@ class IntValue:
|
|
9
10
|
|
10
11
|
value: int
|
11
12
|
text: str
|
13
|
+
default: int = 0
|
12
14
|
|
13
15
|
|
14
16
|
@dataclass(frozen=True)
|
@@ -17,23 +19,65 @@ class StrValue:
|
|
17
19
|
|
18
20
|
value: str
|
19
21
|
text: str
|
22
|
+
default: str = ""
|
20
23
|
|
21
24
|
|
22
25
|
class RichStrEnum(StrEnum):
|
23
26
|
"""Base class for StrEnums with rich metadata."""
|
24
27
|
|
25
28
|
text: str
|
29
|
+
default: str
|
26
30
|
|
27
31
|
def __new__(cls, value: StrValue) -> Self:
|
28
|
-
_value: str = value.value
|
29
32
|
text: str = value.text
|
30
|
-
obj: Self = str.__new__(cls,
|
31
|
-
obj._value_ =
|
33
|
+
obj: Self = str.__new__(cls, value.value)
|
34
|
+
obj._value_ = value.value
|
32
35
|
obj.text = text
|
36
|
+
obj.default = value.default
|
33
37
|
return obj
|
34
38
|
|
39
|
+
@classmethod
|
40
|
+
def keys(cls) -> list[str]:
|
41
|
+
"""Return a list of all enum member names."""
|
42
|
+
return [item.name for item in cls]
|
43
|
+
|
44
|
+
@overload
|
45
|
+
@classmethod
|
46
|
+
def get(cls, value: str | Self, default: Self) -> Self: ...
|
47
|
+
|
48
|
+
@overload
|
49
|
+
@classmethod
|
50
|
+
def get(cls, value: str | Self, default: None = None) -> None: ...
|
51
|
+
|
52
|
+
@classmethod
|
53
|
+
def get(cls, value: str | Self, default: Self | None = None) -> Self | None:
|
54
|
+
"""Try to get an enum member by its value or name."""
|
55
|
+
if isinstance(value, cls):
|
56
|
+
return value
|
57
|
+
with suppress(ValueError):
|
58
|
+
if isinstance(value, str):
|
59
|
+
return cls.from_text(value)
|
60
|
+
return default
|
61
|
+
|
62
|
+
@classmethod
|
63
|
+
def from_text(cls, text: str) -> Self:
|
64
|
+
"""Convert a string text to its corresponding enum member."""
|
65
|
+
for item in cls:
|
66
|
+
if item.text == text:
|
67
|
+
return item
|
68
|
+
raise ValueError(f"Text {text} not found in {cls.__name__}")
|
69
|
+
|
70
|
+
@classmethod
|
71
|
+
def from_name(cls, name: str) -> Self:
|
72
|
+
"""Convert a string name to its corresponding enum member."""
|
73
|
+
try:
|
74
|
+
return cls[name.upper()]
|
75
|
+
except KeyError as e:
|
76
|
+
raise ValueError(f"Name {name} not found in {cls.__name__}") from e
|
77
|
+
|
35
78
|
def __str__(self) -> str:
|
36
|
-
|
79
|
+
"""Return a string representation of the enum."""
|
80
|
+
return self.value
|
37
81
|
|
38
82
|
def str(self) -> str:
|
39
83
|
"""Return the string value of the enum."""
|
@@ -44,13 +88,14 @@ class RichIntEnum(IntEnum):
|
|
44
88
|
"""Base class for IntEnums with rich metadata."""
|
45
89
|
|
46
90
|
text: str
|
91
|
+
default: int
|
47
92
|
|
48
93
|
def __new__(cls, value: IntValue) -> Self:
|
49
|
-
_value: int = value.value
|
50
94
|
text: str = value.text
|
51
|
-
obj: Self = int.__new__(cls,
|
52
|
-
obj._value_ =
|
95
|
+
obj: Self = int.__new__(cls, value.value)
|
96
|
+
obj._value_ = value.value
|
53
97
|
obj.text = text
|
98
|
+
obj.default = value.default
|
54
99
|
return obj
|
55
100
|
|
56
101
|
def __int__(self) -> int:
|
@@ -58,18 +103,33 @@ class RichIntEnum(IntEnum):
|
|
58
103
|
return self.value
|
59
104
|
|
60
105
|
def __str__(self) -> str:
|
106
|
+
"""Return a string representation of the enum."""
|
61
107
|
return f"{self.name} ({self.value}): {self.text}"
|
62
108
|
|
63
109
|
@classmethod
|
64
|
-
def
|
110
|
+
def keys(cls) -> list[str]:
|
111
|
+
"""Return a list of all enum member names."""
|
112
|
+
return [item.name for item in cls]
|
113
|
+
|
114
|
+
@overload
|
115
|
+
@classmethod
|
116
|
+
def get(cls, value: str | int | Self, default: Self) -> Self: ...
|
117
|
+
|
118
|
+
@overload
|
119
|
+
@classmethod
|
120
|
+
def get(cls, value: str | int | Self, default: None = None) -> None: ...
|
121
|
+
|
122
|
+
@classmethod
|
123
|
+
def get(cls, value: str | int | Self | Any, default: Self | None = None) -> Self | None:
|
65
124
|
"""Try to get an enum member by its value, name, or text."""
|
66
125
|
if isinstance(value, cls):
|
67
126
|
return value
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
127
|
+
with suppress(ValueError):
|
128
|
+
if isinstance(value, int):
|
129
|
+
return cls.from_int(value)
|
130
|
+
if isinstance(value, str):
|
131
|
+
return cls.from_name(value)
|
132
|
+
return default
|
73
133
|
|
74
134
|
@classmethod
|
75
135
|
def from_name(cls, name: str) -> Self:
|
@@ -81,6 +141,7 @@ class RichIntEnum(IntEnum):
|
|
81
141
|
|
82
142
|
@classmethod
|
83
143
|
def from_int(cls, code: int) -> Self:
|
144
|
+
"""Convert an integer to its corresponding enum member."""
|
84
145
|
for item in cls:
|
85
146
|
if item.value == code:
|
86
147
|
return item
|
@@ -96,11 +157,11 @@ class RichIntEnum(IntEnum):
|
|
96
157
|
|
97
158
|
|
98
159
|
class MockTextIO(TextIO):
|
99
|
-
"""A mock TextIO class that
|
160
|
+
"""A mock TextIO class that captures written output for testing purposes."""
|
100
161
|
|
101
162
|
def __init__(self) -> None:
|
102
163
|
"""Initialize the mock TextIO."""
|
103
|
-
self._buffer = []
|
164
|
+
self._buffer: list[str] = []
|
104
165
|
|
105
166
|
def write(self, _s: str, *_) -> None: # type: ignore[override]
|
106
167
|
"""Mock write method that appends to the buffer."""
|
@@ -130,6 +191,7 @@ class NullFile(TextIO):
|
|
130
191
|
"""Flush the null file (no operation)."""
|
131
192
|
|
132
193
|
def __enter__(self) -> Self:
|
194
|
+
"""Enter context manager and return self."""
|
133
195
|
return self
|
134
196
|
|
135
197
|
def __exit__(self, *_: object) -> None:
|
bear_utils/extras/__init__.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
from singleton_base import SingletonBase
|
4
4
|
|
5
5
|
from ._tools import ClipboardManager, ascii_header, clear_clipboard, copy_to_clipboard, paste_from_clipboard
|
6
|
+
from ._zapper import zap, zap_as, zap_as_multi, zap_get, zap_multi
|
6
7
|
from .platform_utils import OS, get_platform, is_linux, is_macos, is_windows
|
7
8
|
from .wrappers.add_methods import add_comparison_methods
|
8
9
|
|
@@ -19,4 +20,9 @@ __all__ = [
|
|
19
20
|
"is_macos",
|
20
21
|
"is_windows",
|
21
22
|
"paste_from_clipboard",
|
23
|
+
"zap",
|
24
|
+
"zap_as",
|
25
|
+
"zap_as_multi",
|
26
|
+
"zap_get",
|
27
|
+
"zap_multi",
|
22
28
|
]
|
bear_utils/extras/_tools.py
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
import asyncio
|
2
2
|
from asyncio.subprocess import PIPE
|
3
3
|
from collections import deque
|
4
|
-
from functools import cached_property
|
5
4
|
import shutil
|
6
5
|
from typing import TYPE_CHECKING
|
7
6
|
|
8
|
-
from rich.console import Console
|
9
|
-
|
10
7
|
from bear_utils.cli.shell._base_command import BaseShellCommand as ShellCommand
|
11
8
|
from bear_utils.cli.shell._base_shell import AsyncShellSession
|
12
9
|
from bear_utils.extras.platform_utils import OS, get_platform
|
10
|
+
from bear_utils.graphics.font._utils import ascii_header
|
13
11
|
|
14
12
|
if TYPE_CHECKING:
|
15
13
|
from subprocess import CompletedProcess
|
@@ -175,119 +173,13 @@ async def clear_clipboard_async() -> int:
|
|
175
173
|
return await clipboard_manager.clear()
|
176
174
|
|
177
175
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
right_sep: str = "<",
|
189
|
-
bottom_sep: str = "#",
|
190
|
-
length: int = 60,
|
191
|
-
s1: str = "bold red",
|
192
|
-
s2: str = "bold blue",
|
193
|
-
return_txt: bool = False,
|
194
|
-
) -> str:
|
195
|
-
"""Generate a header string with customizable separators for each line.
|
196
|
-
|
197
|
-
Args:
|
198
|
-
title: The title text to display
|
199
|
-
top_sep: Character(s) for the top separator line
|
200
|
-
left_sep: Character(s) for the left side of title line
|
201
|
-
right_sep: Character(s) for the right side of title line
|
202
|
-
bottom_sep: Character(s) for the bottom separator line
|
203
|
-
length: Total width of each line
|
204
|
-
s1: Style for the title text
|
205
|
-
s2: Style for the entire header block
|
206
|
-
return_txt: If True, return the text instead of printing
|
207
|
-
"""
|
208
|
-
# Top line: all top_sep characters
|
209
|
-
top_line: str = top_sep * length
|
210
|
-
|
211
|
-
# Bottom line: all bottom_sep characters
|
212
|
-
bottom_line: str = bottom_sep * length
|
213
|
-
|
214
|
-
# Title line: left_sep chars + title + right_sep chars
|
215
|
-
title_with_spaces = f" {title} "
|
216
|
-
styled_title = f"[{s1}]{title}[/{s1}]"
|
217
|
-
|
218
|
-
# Calculate padding needed on each side
|
219
|
-
title_length = len(title_with_spaces)
|
220
|
-
remaining_space = length - title_length
|
221
|
-
left_padding = remaining_space // 2
|
222
|
-
right_padding = remaining_space - left_padding
|
223
|
-
|
224
|
-
# Build the title line with different left and right separators
|
225
|
-
title_line = (
|
226
|
-
(left_sep * left_padding) + title_with_spaces.replace(title, styled_title) + (right_sep * right_padding)
|
227
|
-
)
|
228
|
-
|
229
|
-
# Assemble the complete header
|
230
|
-
output_text: str = f"\n{top_line}\n{title_line}\n{bottom_line}\n"
|
231
|
-
|
232
|
-
if not return_txt:
|
233
|
-
self.local_console.print(output_text, style=s2)
|
234
|
-
return output_text
|
235
|
-
|
236
|
-
|
237
|
-
def ascii_header(
|
238
|
-
title: str,
|
239
|
-
top_sep: str = "#",
|
240
|
-
left_sep: str = ">",
|
241
|
-
right_sep: str = "<",
|
242
|
-
bottom_sep: str = "#",
|
243
|
-
length: int = 60,
|
244
|
-
style1: str = "bold red",
|
245
|
-
style2: str = "bold blue",
|
246
|
-
print_out: bool = True,
|
247
|
-
) -> str:
|
248
|
-
"""Generate a header string for visual tests.
|
249
|
-
|
250
|
-
Args:
|
251
|
-
title (str): The title to display in the header.
|
252
|
-
top_sep (str): The character to use for the top separator line. Defaults to '#'.
|
253
|
-
left_sep (str): The character to use for the left side of title line. Defaults to '>'.
|
254
|
-
right_sep (str): The character to use for the right side of title line. Defaults to '<'.
|
255
|
-
bottom_sep (str): The character to use for the bottom separator line. Defaults to '#'.
|
256
|
-
length (int): The total length of the header line. Defaults to 60.
|
257
|
-
style1 (str): The style for the title text. Defaults to 'bold red'.
|
258
|
-
style2 (str): The style for the separator text. Defaults to 'bold blue'.
|
259
|
-
print_out (bool): Whether to print the header or just return it. Defaults to True.
|
260
|
-
"""
|
261
|
-
text_helper = TextHelper()
|
262
|
-
if print_out:
|
263
|
-
text_helper.print_header(
|
264
|
-
title=title,
|
265
|
-
top_sep=top_sep,
|
266
|
-
left_sep=left_sep,
|
267
|
-
right_sep=right_sep,
|
268
|
-
bottom_sep=bottom_sep,
|
269
|
-
length=length,
|
270
|
-
s1=style1,
|
271
|
-
s2=style2,
|
272
|
-
return_txt=False,
|
273
|
-
)
|
274
|
-
return ""
|
275
|
-
return text_helper.print_header(
|
276
|
-
title=title,
|
277
|
-
top_sep=top_sep,
|
278
|
-
left_sep=left_sep,
|
279
|
-
right_sep=right_sep,
|
280
|
-
bottom_sep=bottom_sep,
|
281
|
-
length=length,
|
282
|
-
s1=style1,
|
283
|
-
s2=style2,
|
284
|
-
return_txt=True,
|
285
|
-
)
|
286
|
-
|
287
|
-
|
288
|
-
if __name__ == "__main__":
|
289
|
-
# Example usage of the TextHelper
|
290
|
-
text_helper = TextHelper()
|
291
|
-
text_helper.print_header("My Title", top_sep="#", bottom_sep="#")
|
292
|
-
text_helper.print_header("My Title", top_sep="=", left_sep=">", right_sep="<", bottom_sep="=")
|
293
|
-
text_helper.print_header("My Title", top_sep="-", left_sep="[", right_sep="]", bottom_sep="-")
|
176
|
+
__all__ = [
|
177
|
+
"ClipboardManager",
|
178
|
+
"ascii_header",
|
179
|
+
"clear_clipboard",
|
180
|
+
"clear_clipboard_async",
|
181
|
+
"copy_to_clipboard",
|
182
|
+
"copy_to_clipboard_async",
|
183
|
+
"paste_from_clipboard",
|
184
|
+
"paste_from_clipboard_async",
|
185
|
+
]
|