From 90c16ecc359055d426f4880881feccbae5997af5 Mon Sep 17 00:00:00 2001 From: Verdi March Date: Wed, 23 Feb 2022 18:31:05 +0800 Subject: [PATCH 1/6] my-nb-color: alternate setup --- {{cookiecutter.repo_name}}/src/my_nb_color.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/{{cookiecutter.repo_name}}/src/my_nb_color.py b/{{cookiecutter.repo_name}}/src/my_nb_color.py index ee3dc4e..5815e5f 100644 --- a/{{cookiecutter.repo_name}}/src/my_nb_color.py +++ b/{{cookiecutter.repo_name}}/src/my_nb_color.py @@ -29,16 +29,21 @@ print = pprint = oprint = print else: oprint = print # In-case plain old behavior is needed - rich.reconfigure(force_terminal=True, force_jupyter=False) + rich.reconfigure(force_terminal=True) rich.pretty.install() - print = cast(Callable, rich.get_console().out) - _pprint = rich.get_console().print + + _console = rich.console.Console(force_terminal=True, force_jupyter=False) + print = cast(Callable, _console.out) + _pprint = _console.print def pprint(*args, **kwargs): kwargs["soft_wrap"] = True _pprint(*args, **kwargs) - inspect = rich.inspect + def inspect(*args, **kwargs): + if "console" not in kwargs: + kwargs["console"] = _console + rich.inspect(*args, **kwargs) # Try to setup loguru. try: From be884587c974998740240db3cb4d8526a950f6c3 Mon Sep 17 00:00:00 2001 From: Verdi March Date: Wed, 23 Feb 2022 18:35:20 +0800 Subject: [PATCH 2/6] Linted my-nb-*.py --- {{cookiecutter.repo_name}}/notebooks/my_nb_path.py | 2 ++ {{cookiecutter.repo_name}}/src/my_nb_color.py | 1 + 2 files changed, 3 insertions(+) diff --git a/{{cookiecutter.repo_name}}/notebooks/my_nb_path.py b/{{cookiecutter.repo_name}}/notebooks/my_nb_path.py index 0571245..ac2ed1e 100644 --- a/{{cookiecutter.repo_name}}/notebooks/my_nb_path.py +++ b/{{cookiecutter.repo_name}}/notebooks/my_nb_path.py @@ -46,11 +46,13 @@ from pathlib import Path from typing import Union + def sys_path_append(o: Union[str, os.PathLike]) -> None: posix_path: str = o.as_posix() if isinstance(o, Path) else Path(o).as_posix() if posix_path not in sys.path: sys.path.insert(0, posix_path) + # Add GIT_ROOT/ and a few other subdirs _p = subprocess.run( ["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT diff --git a/{{cookiecutter.repo_name}}/src/my_nb_color.py b/{{cookiecutter.repo_name}}/src/my_nb_color.py index 5815e5f..dc65274 100644 --- a/{{cookiecutter.repo_name}}/src/my_nb_color.py +++ b/{{cookiecutter.repo_name}}/src/my_nb_color.py @@ -45,6 +45,7 @@ def inspect(*args, **kwargs): kwargs["console"] = _console rich.inspect(*args, **kwargs) + # Try to setup loguru. try: from loguru import logger From 1d346d696d2bae6da3c3efa4607c16ae6bc36f9d Mon Sep 17 00:00:00 2001 From: Verdi March Date: Thu, 24 Feb 2022 11:52:09 +0800 Subject: [PATCH 3/6] my_nb_color.py: opinionated rich configuration --- {{cookiecutter.repo_name}}/src/my_nb_color.py | 93 ++++++++++++++++--- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/{{cookiecutter.repo_name}}/src/my_nb_color.py b/{{cookiecutter.repo_name}}/src/my_nb_color.py index dc65274..e37cbf7 100644 --- a/{{cookiecutter.repo_name}}/src/my_nb_color.py +++ b/{{cookiecutter.repo_name}}/src/my_nb_color.py @@ -5,7 +5,7 @@ Basic usage by an ``.ipynb``: >>> # Colorize notebook outputs - >>> from my_nb_color import print, pprint, oprint + >>> from my_nb_color import print, pprint, oprint, inspect >>> >>> # Test-drive different behavior of print functionalities >>> d = {"A" * 200, "B" * 200} @@ -14,6 +14,13 @@ >>> oprint("Plain (i.e., Python's original):", d) >>> display(d) >>> + >>> import pandas as pd + >>> df = pd.DataFrame(dict(a=[1,2,3], b=[4,5,6])) + >>> display(d, df) + >>> df.plot(); + >>> + >>> inspect(df) + >>> >>> # Test-drive loguru >>> from loguru import logger >>> for f in (logger.debug, logger.info, logger.success, logger.error): @@ -29,22 +36,84 @@ print = pprint = oprint = print else: oprint = print # In-case plain old behavior is needed - rich.reconfigure(force_terminal=True) - rich.pretty.install() - _console = rich.console.Console(force_terminal=True, force_jupyter=False) + rich.reconfigure(force_terminal=True, force_jupyter=False) + _console = rich.get_console() + print = cast(Callable, _console.out) - _pprint = _console.print - def pprint(*args, **kwargs): - kwargs["soft_wrap"] = True - _pprint(*args, **kwargs) + def pprint(*args, soft_wrap=True, **kwargs): + """Call ``rich.console.Console(...).print(..., soft_wrap=True, ...)``.""" + _console.print(*args, soft_wrap=soft_wrap, **kwargs) + + class Inspect: + def __init__(self, console=_console): + self.console = _console + + def __call__(self, obj, *args, **kwargs): + """Call ``rich.inspect(..., console=, ...)``.""" + # Do not inspect wrappers, because *args & **kwargs are not useful for callers. + # + # Implementation notes: make sure the pattern is: + # + # if is : + # = + if self is obj: + obj = rich.inspect + elif pprint is obj: + obj = self.console.print + + rich.inspect(obj, *args, console=self.console, **kwargs) + + inspect = Inspect() + + def opinionated_rich_pretty_install(): + """Intercept any post-ipython renderings. + + Known cases fixed (as of rich-11.2.0): (i) prevent pandas dataframe rendered twice (as text + and as html), (ii) do not show ``
`` on matplotlib figures. + """ + from IPython.core.formatters import BaseFormatter + + class RichFormatterWrapper(BaseFormatter): + # See: rich.pretty.install._ipy_display_hook() + reprs = [ + "_repr_html_", + "_repr_markdown_", + "_repr_json_", + "_repr_latex_", + "_repr_jpeg_", + "_repr_png_", + "_repr_svg_", + "_repr_mimebundle_", + ] + + def __init__(self, rich_formatter): + self.rich_formatter = rich_formatter + + def __call__(self, value, *args, **kwargs): + for repr_name in self.reprs: + try: + repr_method = getattr(value, repr_name) + repr_method() + except ( + AttributeError, # value object has does not have the repr attribute + Exception, # any other error + ) as e: + continue + else: + return + else: + # None of the ipython methods work, hence let rich takes over + self.rich_formatter(value, *args, **kwargs) - def inspect(*args, **kwargs): - if "console" not in kwargs: - kwargs["console"] = _console - rich.inspect(*args, **kwargs) + rich.pretty.install() + ipy_formatters = get_ipython().display_formatter.formatters + rich_formatter = ipy_formatters["text/plain"] + if rich_formatter.__module__ == "rich.pretty": + ipy_formatters["text/plain"] = RichFormatterWrapper(rich_formatter) + opinionated_rich_pretty_install() # Try to setup loguru. try: From bccc770bd90ca009976a1567b53f53f9aaf3218e Mon Sep 17 00:00:00 2001 From: Verdi March Date: Thu, 24 Feb 2022 11:53:24 +0800 Subject: [PATCH 4/6] my_nb_color: added inspect examples --- {{cookiecutter.repo_name}}/src/my_nb_color.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/{{cookiecutter.repo_name}}/src/my_nb_color.py b/{{cookiecutter.repo_name}}/src/my_nb_color.py index e37cbf7..c9a5f4a 100644 --- a/{{cookiecutter.repo_name}}/src/my_nb_color.py +++ b/{{cookiecutter.repo_name}}/src/my_nb_color.py @@ -20,6 +20,9 @@ >>> df.plot(); >>> >>> inspect(df) + >>> inspect(print) + >>> inspect(pprint) + >>> inspect(inspect) >>> >>> # Test-drive loguru >>> from loguru import logger From 68c1e5cf4ce4146aa9df384ed362d9ccb97d0bff Mon Sep 17 00:00:00 2001 From: Verdi March Date: Thu, 24 Feb 2022 12:18:20 +0800 Subject: [PATCH 5/6] my_nb_color: added fallback for inspect() --- {{cookiecutter.repo_name}}/src/my_nb_color.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/{{cookiecutter.repo_name}}/src/my_nb_color.py b/{{cookiecutter.repo_name}}/src/my_nb_color.py index c9a5f4a..86e3ddf 100644 --- a/{{cookiecutter.repo_name}}/src/my_nb_color.py +++ b/{{cookiecutter.repo_name}}/src/my_nb_color.py @@ -30,6 +30,7 @@ >>> f("Hello World!") """ import sys +import warnings from typing import Callable, cast # Try to setup rich. @@ -37,6 +38,10 @@ import rich except ModuleNotFoundError: print = pprint = oprint = print + + def inspect(*args, **kwargs): + warnings.warn(f"{__name__}.inspect() requires rich.") + else: oprint = print # In-case plain old behavior is needed From 832d72fe4e23ac48f8fc6f6cf23132a559a4cfc0 Mon Sep 17 00:00:00 2001 From: Verdi March Date: Thu, 24 Feb 2022 12:26:16 +0800 Subject: [PATCH 6/6] skeleton.ipynb: renamed {rprint => pprint} --- {{cookiecutter.repo_name}}/notebooks/skeleton.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.repo_name}}/notebooks/skeleton.ipynb b/{{cookiecutter.repo_name}}/notebooks/skeleton.ipynb index 4ec4031..d825dc2 100644 --- a/{{cookiecutter.repo_name}}/notebooks/skeleton.ipynb +++ b/{{cookiecutter.repo_name}}/notebooks/skeleton.ipynb @@ -31,7 +31,7 @@ "\n", "# Make sure my_nb_path is imported first (and when isort is used, it needs to be told).\n", "import my_nb_path # isort: skip\n", - "from my_nb_color import print, rprint" + "from my_nb_color import print, pprint, inspect" ] }, { @@ -205,7 +205,7 @@ "source": [ "d = {\"A\" * 200, \"B\" * 200}\n", "print(\"Colored:\", d)\n", - "rprint(\"Colored and wrapped:\", d)\n", + "pprint(\"Colored and wrapped:\", d)\n", "display(d)\n", "\n", "for f in (logger.debug, logger.info, logger.success, logger.error):\n",