Skip to content

Commit

Permalink
Update: styler & check_arg more readable names; __XXX__ custom stylin…
Browse files Browse the repository at this point in the history
…g; file model for file handler logging; New: color code string list
  • Loading branch information
StefanHeng committed Nov 25, 2024
1 parent 2f4dc17 commit a155024
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 33 deletions.
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages

VERSION = '0.42.12'
VERSION = '0.42.13'
DESCRIPTION = 'Machine Learning project startup utilities'
LONG_DESCRIPTION = 'My commonly used utilities for machine learning projects'

Expand All @@ -13,7 +13,7 @@
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
url='https://github.com/StefanHeng/stef-util',
download_url='https://github.com/StefanHeng/stef-util/archive/refs/tags/v0.42.12.tar.gz',
download_url='https://github.com/StefanHeng/stef-util/archive/refs/tags/v0.42.13.tar.gz',
packages=find_packages(),
include_package_data=True,
install_requires=[
Expand Down
10 changes: 5 additions & 5 deletions stefutil/concurrency.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from tqdm.contrib import concurrent as tqdm_concurrent

from stefutil.container import group_n
from stefutil.prettier import ca
from stefutil.prettier import check_arg as ca


__all__ = ['conc_map', 'batched_conc_map', 'conc_yield']
Expand Down Expand Up @@ -346,7 +346,7 @@ def _dummy_fn(xx: int = None):


if __name__ == '__main__':
from stefutil.prettier import rich_progress, rcl
from stefutil.prettier import rich_progress, rich_console_log

def try_concurrent_yield():
# this will gather all results and return
Expand All @@ -366,9 +366,9 @@ def check_conc_map():
n = 10
# for res in conc_map(fn=_work, args=range(n), with_tqdm=dict(total=n), n_worker=4, mode='process'):
# sic(res)
from stefutil.prettier import rcl
from stefutil.prettier import rich_console_log
for res in rich_progress(conc_map(fn=_work, args=range(n), mode='process'), total=n):
rcl(res)
rich_console_log(res)
# check_conc_map()

def check_conc_yield():
Expand Down Expand Up @@ -437,5 +437,5 @@ def check_conc_process_chunk():
i, _ = i
# logger.info(f'Process {s.i(i)} terminated')
lst.append(i)
rcl(lst)
rich_console_log(lst)
check_conc_process_chunk()
2 changes: 1 addition & 1 deletion stefutil/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from functools import reduce
from collections import OrderedDict

from stefutil.prettier import s
from stefutil.prettier import style as s
from stefutil.packaging import _use_dl


Expand Down
2 changes: 1 addition & 1 deletion stefutil/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from os.path import join as os_join
from typing import Callable, Union, Any

from stefutil.prettier import ca, date
from stefutil.prettier import check_arg as ca, date


__all__ = ['profile_runtime', 'RecurseLimit']
Expand Down
2 changes: 1 addition & 1 deletion stefutil/ml.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import math
from typing import Tuple, Dict, Union, Iterable, Any

from stefutil.prettier import fmt_num, fmt_sizeof, ca
from stefutil.prettier import fmt_num, fmt_sizeof, check_arg as ca
from stefutil.packaging import _use_ml, _use_dl


Expand Down
2 changes: 1 addition & 1 deletion stefutil/nlp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from tqdm import tqdm

from stefutil.container import group_n
from stefutil.prettier import get_logger, s, ca
from stefutil.prettier import get_logger, style as s, check_arg as ca
from stefutil.packaging import _use_dl


Expand Down
8 changes: 4 additions & 4 deletions stefutil/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from typing import List, Dict, Iterable, Callable, Any, Union
from dataclasses import dataclass

from stefutil.prettier import s, ca, get_logger, Timer
from stefutil.prettier import style as s, check_arg as ca, get_logger, Timer
from stefutil.container import df_col2cat_col
from stefutil.packaging import installed_packages, _use_plot, _use_ml

Expand All @@ -37,9 +37,9 @@


def set_plot_style():
plt.rc('figure', figsize=(16, 9))
plt.rc('figure.constrained_layout', use=True)
plt.rc('text.latex', preamble='\n'.join([
plt.rich_console('figure', figsize=(16, 9))
plt.rich_console('figure.constrained_layout', use=True)
plt.rich_console('text.latex', preamble='\n'.join([
r'\usepackage{nicefrac}',
r'\usepackage{helvet}',
r'\usepackage{sansmath}', # render math sans-serif
Expand Down
25 changes: 17 additions & 8 deletions stefutil/prettier/prettier_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@


__all__ = [
'MyIceCreamDebugger', 'sic',
'rc', 'rcl',
'MyIceCreamDebugger', 'icecream',
'rich_console', 'rich_console_log',
'_DEFAULT_ANSI_BACKEND', '_ANSI_REST_ALL',
'to_rich_markup',
'render_nested_ansi_pairs', 'PrettyStyler', 's', 'style',
'render_nested_ansi_pairs', 'PrettyStyler', 'style',
]


Expand All @@ -42,10 +42,10 @@ def output_width(self, value):


# syntactic sugar
sic = MyIceCreamDebugger()
sic = icecream = MyIceCreamDebugger()

rc = Console()
rcl = rc.log
rich_console = Console()
rich_console_log = rich_console.log


@dataclass
Expand Down Expand Up @@ -244,8 +244,8 @@ def __call__(
import rich.style
style_args['color'] = style_args.pop('fg')
style_args['bgcolor'] = style_args.pop('bg')
style = rich.style.Style(**style_args)
return style.render(text=str(x)) # explicitly convert to str for `False` and `None` styling
style_ = rich.style.Style(**style_args)
return style_.render(text=str(x)) # explicitly convert to str for `False` and `None` styling

elif self.backend == 'rich-markup':
if kwargs != dict():
Expand All @@ -270,6 +270,7 @@ class PrettyStyler:
False: dict(fg='Br', italic=True),
int: dict(fg='Bc'),
float: dict(fg='Bc'),
'keyword': dict(fg='Bm'),
str: dict(fg='Bg'),
'path': dict(fg='m')
}
Expand Down Expand Up @@ -336,6 +337,9 @@ def _get_default_style(x: Union[int, float, bool, str, None]):
elif isinstance(x, Path) \
or (isinstance(x, str) and (os.path.exists(x) or len(x) < 256 and x.count(os.sep) >= 2)): # heuristics to check for path
tp = 'path'
# consider `__XXX__` a special keyword
elif isinstance(x, str) and x.startswith('__') and x.endswith('__'):
tp = 'keyword'
else:
tp = type(x)
ret = d.get(tp, dict())
Expand Down Expand Up @@ -925,3 +929,8 @@ def check_pad():
}
print(s.i(d, align_keys=1, indent=1, pad=4))
# check_pad()

def check_style_keyword():
d = {'__correct__': 1, '__not_named_entity__': 2, '__wrong_boundary__': 3, '__wrong_type__': 4, 'incorrect ': 5}
print(s.i(d, color_keys=True))
check_style_keyword()
3 changes: 2 additions & 1 deletion stefutil/prettier/prettier_dl.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

from tqdm.auto import tqdm

from stefutil.prettier import s, ca
from stefutil.prettier.prettier_debug import style as s
from stefutil.prettier.prettier_log import check_arg as ca
from stefutil.packaging import _use_dl


Expand Down
103 changes: 97 additions & 6 deletions stefutil/prettier/prettier_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
import sys
import logging
import datetime
from typing import List, Dict, Union, Optional, Any, Callable
from typing import List, Dict, Union, Optional, Any, Callable, Iterable
from collections import OrderedDict

from stefutil.prettier.prettier import hex2rgb
from stefutil.prettier.prettier_debug import s, _DEFAULT_ANSI_BACKEND, _ANSI_REST_ALL
from stefutil.prettier.prettier_debug import style as s, _DEFAULT_ANSI_BACKEND, _ANSI_REST_ALL


__all__ = [
'MyTheme', 'MyFormatter',
'filter_ansi', 'CleanAnsiFileHandler', 'AnsiFileMap',
'LOG_STR2LOG_LEVEL', 'get_logging_handler', 'get_logger', 'add_log_handler', 'add_file_handler', 'drop_file_handler',

'CheckArg', 'ca',
'CheckArg', 'check_arg',

'now', 'date',
]
Expand Down Expand Up @@ -74,6 +74,87 @@ def date():
return now(for_path=True, fmt='short-date')


def _color_code_string(prompt: str = None) -> str:
"""
Color-code a prompt for semantic segmentation by a simple heuristic
"""
# first, split up the prompt into sections
ret = ''
sep = '\n\n'
segs = prompt.split(sep)
it = iter(segs)
prev = None
curr = next(it)
assert curr is not None
cs = ['b', 'm', 'r', 'y', 'g']
i_c = None
while curr is not None:
# iteratively check, does the difference between current and next segment indicate a new section?
# if prev is not None:
# declare different is current segment is pretty long, via either char count or line count
long_enough = False
n_lines = curr.count('\n')
n_chars = len(curr)
if n_chars > 250:
long_enough = True
elif n_chars > 150 and n_lines > 0:
long_enough = True
elif n_lines > 0 and all(len(c) > 60 for c in curr.split('\n')):
long_enough = True
elif '\n' not in curr and n_chars > 120:
long_enough = True
elif n_lines > 3:
long_enough = True
elif '---' in curr or 'Examples:' in curr:
long_enough = True
if prev is None:
i_c = 0
elif long_enough:
i_c = (i_c + 1) % len(cs)
print(long_enough)
ret += f'{s.i(curr, fg=cs[i_c])}{sep}'

prev = curr
curr = next(it, None)
return ret


def print_strings(strings: Union[Callable[[], str], List[str], Iterable[str]], n: int = None) -> List[str]:
"""
color codes a list of strings with heuristics to separate semantic sections
:param strings: a list of strings or a callable that returns a string
:param n: number of strings to print
"""
if isinstance(strings, list):
assert n is None
prompts = strings
n = len(prompts)
elif hasattr(strings, '__iter__'):
prompts = list(strings)
if n is not None:
n = min(n, len(prompts))
else:
n = n or 5
prompts = [strings() for _ in range(n)]

if any(not p for p in prompts):
prompts = [p for p in prompts if p] # filter out empty/None prompts
n = len(prompts)
assert n > 0
assert all(isinstance(p, str) for p in prompts) # sanity check

# prompts = [f'Prompt {i}:\n{pl.i(p)}' for i, p in enumerate(prompts, start=1)]
prompts = [f'String {i}:\n{_color_code_string(p)}' for i, p in enumerate(prompts, start=1)]
if n == 1:
print(prompts[0])
else:
for i in range(n):
sep = '\n\n\n' if i != n - 1 else ''
print(f'{prompts[i]}{sep}')
return prompts


class MyTheme:
"""
Theme based on `sty` and `Atom OneDark`
Expand Down Expand Up @@ -306,7 +387,7 @@ def append_ext(file_path: str) -> str:


def get_logging_handler(
kind: str = 'stdout', file_path: str = None, level: Union[str, int] = 'debug',
kind: str = 'stdout', file_path: str = None, level: Union[str, int] = 'debug', file_mode: str = 'a',
ansi_file_map: Callable[[str], str] = AnsiFileMap.append_ext
) -> Union[logging.Handler, List[logging.Handler]]:
"""
Expand All @@ -319,6 +400,7 @@ def get_logging_handler(
If `file+ansi`, both file write handlers w/ and w/o ANSI style filtering
:param file_path: File path for file logging.
:param level: Logging level for the handler.
:param file_mode: File mode for file logging.
:param ansi_file_map: Mapping function for the ANSI file handler:
Returns the mapped file path for ANSI given the original file path.
"""
Expand Down Expand Up @@ -353,7 +435,7 @@ def get_logging_handler(

# i.e., when `file-w/-ansi`, use the default file handler - no filter out for the ANSI chars
cls = CleanAnsiFileHandler if kind == 'file' else logging.FileHandler
handler = cls(file_path)
handler = cls(file_path, mode=file_mode)
set_level(handler, level=level)
handler.setFormatter(MyFormatter(with_color=kind in ['stdout', 'file-w/-ansi']))
handler.addFilter(HandlerFilter(handler_name=kind))
Expand Down Expand Up @@ -468,7 +550,7 @@ def cache_options(self, display_name: str, attr_name: str, options: List[str]):
setattr(self, attr_name, options)


ca = CheckArg()
ca = check_arg = CheckArg()
ca.cache_options( # See `stefutil::plot.py`
'Bar Plot Orientation', attr_name='bar_orient', options=['v', 'h', 'vertical', 'horizontal']
)
Expand Down Expand Up @@ -573,3 +655,12 @@ def check_filter_ansi():
print(txt)
print(filter_ansi(txt))
# check_filter_ansi()

def check_print_str():
lst = [
"a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string"
"\n\n\n\n\n\n\n\n\n a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string a long string\n\n\n\n\na long string\n\n\nasdsadasasd",
'hello world'
]
print_strings(lst)
check_print_str()
4 changes: 2 additions & 2 deletions stefutil/prettier/prettier_progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from tqdm.utils import FormatReplace, disp_len, disp_trim

from stefutil.os import rel_path
from stefutil.prettier.prettier_debug import s, rc
from stefutil.prettier.prettier_debug import style as s, rich_console


__all__ = [
Expand All @@ -21,7 +21,7 @@


def rich_status(desc: str = None, spinner: str = 'arrow3'):
return rc.status(status=desc, spinner=spinner)
return rich_console.status(status=desc, spinner=spinner)


_I = typing.TypeVar("_I", TextIO, BinaryIO)
Expand Down
2 changes: 1 addition & 1 deletion stefutil/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from stefutil.container import get
from stefutil.os import rel_path
from stefutil.prettier import now, ca, get_logger, s
from stefutil.prettier import now, check_arg as ca, get_logger, style as s


__all__ = ['SConfig', 'PathUtil']
Expand Down

0 comments on commit a155024

Please sign in to comment.