From 83ab616e13ffa4d5505e5cc23d462a5ce3627566 Mon Sep 17 00:00:00 2001 From: Yusur Princeps Date: Fri, 19 Sep 2025 15:39:44 +0200 Subject: [PATCH] add chalk --- CHANGELOG.md | 5 +-- src/suou/__init__.py | 3 +- src/suou/asgi.py | 12 +++++++ src/suou/color.py | 79 ++++++++++++++++++++++++++++++++++++++++++++ src/suou/luck.py | 8 +++-- src/suou/waiter.py | 1 + 6 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 src/suou/color.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 80f1123..c1fee43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ ## 0.7.0 "The Lucky Update" -+ Add RNG/random selection overloads such as `luck()`, `rng_overload()`. -+ Add 7 new throwable exceptions. ++ Add RNG/random selection overloads such as `luck()`, `rng_overload()` ++ Add 7 new throwable exceptions ++ Add color utilities: `chalk` module ## 0.6.1 diff --git a/src/suou/__init__.py b/src/suou/__init__.py index 9f80088..a33a879 100644 --- a/src/suou/__init__.py +++ b/src/suou/__init__.py @@ -35,6 +35,7 @@ from .strtools import PrefixIdentifier from .validators import matches from .redact import redact_url_password from .http import WantsContentType +from .color import chalk __version__ = "0.7.0-dev37" @@ -46,7 +47,7 @@ __all__ = ( 'StringCase', 'TimedDict', 'TomlI18n', 'UserSigner', 'Wanted', 'WantsContentType', 'addattr', 'additem', 'age_and_days', 'alru_cache', 'b2048decode', 'b2048encode', 'b32ldecode', 'b32lencode', 'b64encode', 'b64decode', 'cb32encode', - 'cb32decode', 'count_ones', 'dei_args', 'deprecated', 'ilex', 'join_bits', + 'cb32decode', 'chalk', 'count_ones', 'dei_args', 'deprecated', 'ilex', 'join_bits', 'jsonencode', 'kwargs_prefix', 'lex', 'ltuple', 'makelist', 'mask_shift', 'matches', 'mod_ceil', 'mod_floor', 'none_pass', 'not_implemented', 'redact_url_password', 'rtuple', 'split_bits', 'ssv_list', 'symbol_table', diff --git a/src/suou/asgi.py b/src/suou/asgi.py index 3f3fd70..7229920 100644 --- a/src/suou/asgi.py +++ b/src/suou/asgi.py @@ -1,5 +1,17 @@ """ +ASGI stuff +--- + +Copyright (c) 2025 Sakuragasaki46. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +See LICENSE for the specific language governing permissions and +limitations under the License. + +This software is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ from typing import Any, Awaitable, Callable, MutableMapping, ParamSpec, Protocol diff --git a/src/suou/color.py b/src/suou/color.py new file mode 100644 index 0000000..31ea031 --- /dev/null +++ b/src/suou/color.py @@ -0,0 +1,79 @@ +""" +Colors for coding artists + +NEW 0.7.0 + +--- + +Copyright (c) 2025 Sakuragasaki46. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +See LICENSE for the specific language governing permissions and +limitations under the License. + +This software is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +""" + + +from functools import lru_cache + + +class Chalk: + """ + ANSI escape codes for terminal colors, similar to JavaScript's `chalk` library. + + Best used with Python 3.12+ that allows arbitrary nesting of f-strings. + + Yes, I am aware colorama exists. + + UNTESTED + + NEW 0.7.0 + """ + CSI = '\x1b[' + RED = CSI + "31m" + GREEN = CSI + "32m" + YELLOW = CSI + "33m" + BLUE = CSI + "34m" + CYAN = CSI + "36m" + PURPLE = CSI + "35m" + GREY = CSI + "90m" + END_COLOR = CSI + "39m" + BOLD = CSI + "1m" + END_BOLD = CSI + "22m" + FAINT = CSI + "2m" + def __init__(self, flags = (), ends = ()): + self._flags = tuple(flags) + self._ends = tuple(ends) + @lru_cache() + def _wrap(self, beg, end): + return Chalk(self._flags + (beg,), self._ends + (end,)) + def __call__(self, s: str) -> str: + return ''.join(self._flags) + s + ''.join(reversed(self._ends)) + def red(self): + return self._wrap(self.RED, self.END_COLOR) + def green(self): + return self._wrap(self.GREEN, self.END_COLOR) + def blue(self): + return self._wrap(self.BLUE, self.END_COLOR) + def yellow(self): + return self._wrap(self.YELLOW, self.END_COLOR) + def cyan(self): + return self._wrap(self.CYAN, self.END_COLOR) + def purple(self): + return self._wrap(self.PURPLE, self.END_COLOR) + def grey(self): + return self._wrap(self.GREY, self.END_COLOR) + gray = grey + marine = blue + def bold(self): + return self._wrap(self.BOLD, self.END_BOLD) + def faint(self): + return self._wrap(self.FAINT, self.END_BOLD) + + +## TODO make it lazy? +chalk = Chalk() diff --git a/src/suou/luck.py b/src/suou/luck.py index c22b552..7b8192e 100644 --- a/src/suou/luck.py +++ b/src/suou/luck.py @@ -1,5 +1,7 @@ """ -Fortune and esoterism helpers. +Fortune' RNG and esoterism. + +NEW 0.7.0 --- @@ -108,5 +110,5 @@ def rng_overload(prev_func: RngCallable[_T, _U] | int | None, /, *, weight: int return decorator - - \ No newline at end of file +# This module is experimental and therefore not re-exported into __init__ +__all__ = ('lucky', 'rng_overload') \ No newline at end of file diff --git a/src/suou/waiter.py b/src/suou/waiter.py index 74d2d0e..a210f45 100644 --- a/src/suou/waiter.py +++ b/src/suou/waiter.py @@ -54,4 +54,5 @@ def ko(status: int, /, content = None, **ka): return PlainTextResponse(content, status_code=status, **ka) return content +# This module is experimental and therefore not re-exported into __init__ __all__ = ('ko', 'ok', 'Waiter') \ No newline at end of file