Skip to content

Instantly share code, notes, and snippets.

@GigglePocket
Created December 26, 2025 07:07
Show Gist options
  • Select an option

  • Save GigglePocket/541d9917d7508520044a1bb8aab980b8 to your computer and use it in GitHub Desktop.

Select an option

Save GigglePocket/541d9917d7508520044a1bb8aab980b8 to your computer and use it in GitHub Desktop.
Print function

#? Date Added: 2022/10/30 12:02 AM #? Date Updated: 2025/12/11 11:36 PM def print( *objects: tuple[Any] | Any, sep: str = " ", end: str = "\n", file: Optional[TextIOWrapper | BufferedWriter | str | Path]=None, flush: bool = False, silent: bool=False, box=False, escape_bracket=False, markdown: bool=False, color: Optional[str]=None, bgcolor: Optional[str]=None, bold: Optional[bool]=None, dim: Optional[bool]=None, italic: Optional[bool]=None, underline: Optional[bool]=None, strike: Optional[bool]=None, conceal: Optional[bool]=None, md_table: Optional[str]=None, md_kwargs: Optional[dict]=None, code: bool | int=False, code_kwargs: Optional[dict]=None, truncate_len: int=0, truncate_end: str="...", format_exceptions: bool=True, trace: bool=False, frame_offset: int=1, frame_exclude: Optional[list[FrameType]]=None, format_maladict: bool=True, delay: Optional[float | int]=None, separate: bool=False, rstrip_code: bool=True, pager: bool | Callable=False, dedent: bool=False, print_obj: Optional[Callable]=None, **kwargs, ): """ ## Description: - Print from rich.print with a plethora of additional features and options




 

## ***Parameters:***

>>> *objects: Any

- Objects to print
- The string representations of these objects are printed to the file argument separated by the 1 argument and followed by the `end` argument
- Objects can be any type but the string representation must be valid Unicode and contain no null characters (\\0)
- A trailing newline character is stripped from the end of the string if present
- If no objects are given, `print()` will just write end
<hr>
&nbsp;

>>> sep: str=" "

- The separator used between `*objects` when printing
<hr>
&nbsp;

>>> end: str="\\n"

- The string appended after the last value, defaulting to a newline
<hr>
&nbsp;

>>> file: str | Path | TextIOWrapper | BufferedWriter | None=None

- A file-like object (stream) to which the output is printed
- If `file` is `TextIOWrapper` or `BufferedWriter`, the output is written to the file object
- If `file` is `str` or `Path`, the output is written to the file named by `file` as a text file
- If `file` is `None`, the output is written to `sys.stdout`
<hr>
&nbsp;

>>> flush: bool=False

- If `True`, the output is flushed (forced to be written) immediately
- If `False`, the output is buffered (written when the buffer is full)
- The default buffer size is usually 8KB
<hr>
&nbsp;

>>> silent: bool=False

- If `True`, this function will return immediately without printing anything
<hr>
&nbsp;

>>> box: bool=False

- If `True`, puts `objects` in boxes
- If `False`, does not
<hr>
&nbsp;

>>> escape_bracket: bool=False

- If `True`, will escape all opening square brackets `[` within all strings to prevent `rich.print` from treating them as the beginning of an argument
<hr>
&nbsp;

>>> markdown: bool=False

- If `True`, will treat the input text as markdown and render it as such
<hr>
&nbsp;

>>> truncate_len: int=0

- If provided and not 0, this will truncate the output to the provided length minus the length of, and appending, `truncate_end`
<hr>
&nbsp;

>>> truncate_end: str="..."

- If `truncate_len` is provided and not 0, this will be appended to the truncated output
<hr>
&nbsp;

>>> format_exceptions: bool=True

- If `True`, and the first argument to `objects` is an exception, this will use the `errors.display_exception` function to display the error, as well as any other arguments passed to `objects`, and keyword arguments passed to this function
<hr>
&nbsp;

>>> trace: bool=False

- If `True`, a clickable trace to the file, line, and function/module will be included in the printed output
<hr>
&nbsp;

>>> frame_offset: int=1

- If `trace` is `True`, this will offset the frame by the provided number of frames
<hr>
&nbsp;

>>> frame_exclude: Optional[list[FrameType]]=None

- A list of one or more frames to compare to the frame at the chosen offset that, if matched, will trigger the previous frame, if possible, to be used instead
<hr>
&nbsp;

>>> format_maladict: bool=True

- If `True`, and the first argument to `objects` is a `MalaDict`, it will be converted to a regular dictionary before printing, allowing it to be pretty printed
<hr>
&nbsp;

>>> delay: Optional[float | int]=None

- If provided, this will delay this number of seconds after printing
- Does not work with other options that return after execution

<hr>
&nbsp;

>>> separate: bool=False

- If `True`, will print each object separately, (calls itself recursively)
- Useful for ensuring that rich formatting will be applied to each object
<hr>
&nbsp;

>>> rstrip_code: bool=True

- If `True` and `code` is a truthy value, the code will be stripped of trailing whitespace before being printed, or written to file
- This addresses mysterious extra blank lines introduced in the when passing the highlighted code to rich's print method
<hr>
&nbsp;

>>> pager: bool | Callable=False

- If `True`, will use rich's built in pager to display the output
- This should be passed a boolean value; Callable is for internal use only
- Paginated output has no style, syntax highlighting, or color
<hr>
&nbsp;

>>> dedent: bool=False

- If `True`, this removes the leading whitespace on the first non-blank line to zero, and then removes the same amount of whitespace from all other lines that contain at least that amount of leading whitespace
    - This process will be applied to every item in `*objects`
<hr>
&nbsp;

>>> print_obj: Optional[Callable]=None

- If provided, this callable will be used to print each object in `*objects` instead of the default rich print method
- This can be `rich.console.log` or any function that accepts a single argument to print
- If provided, any keyword arguments passed not accepted by this function will be ignored
<hr>
&nbsp;

>>> **kwargs

- Any additional keyword arguments to be passed to print object used by this function
- This is useful if the `print_obj` function passed requires additional arguments not accepted by this function explicitly
- If `log_locals` is provided here and `True`, and no `print_obj` was provided, this will print the locals to the console before printing the objects and performing any other operations specified
    - **NOTE:** The value of `frame_offset` + 1 will be passed to the `Console.log` method, and will determine which frame's locals are printed
<hr>
<hr>
&nbsp;

### ***Markdown Parameters:***

>>> color: str(any(rgb_color_code)) or None

- If provided, will color the text with the provided color code
<hr>
&nbsp;

>>> bgcolor: str(any(rgb_color_code)) or None

- If provided, will set the background color of the text to the provided color code
<hr>
&nbsp;

>>> bold: bool(either) or None

- If provided, will set the text to bold if `True`, or not bold if `False`
<hr>
&nbsp;

>>> dim: bool(either) or None

- If provided, will set the text to dim if `True`, or not dim if `False`
<hr>
&nbsp;

>>> italic: bool(either) or None

<hr>
&nbsp;

>>> underline: bool(either) or None

<hr>
&nbsp;

>>> strike: bool(either) or None

<hr>
&nbsp;

>>> conceal: bool(either) or None

<hr>
&nbsp;

>>> md_table: str(any) or None

- If provided, will render the markdown as a table using `markdown_to_table` with the provided `md_kwargs`
- For more information, see the `md_kwargs` parameter and the Markdown To Table Description section
<hr>
&nbsp;

>>> md_kwargs: dict(any) or None

- If provided, will pass the kwargs to markdown_to_table
- These must be provided as a named argument to the `print` function if provided
- These do not apply to any other markdown rendering, or to any other part of this function
<hr>
&nbsp;

>>> code: bool | int=False

- If this is either `1`, `2`, or `True` will use one of 2 functions to highlight the `*objects`, both of which accept the base argument `objects` passed to print, and a language argument (passed to `code_kwargs`):
    - If `1`:
        - Uses the `highlight_code` function (the standard pygments + rich code highlighting)
        - Uses the "rrt" theme by default
        - Accepts many arguments, all of which you can view in the documentation for `malacode.code_print.print_code.highlight_code`
        - The code passed to this MUST be a string lest a TypeError be raised
    - If `2` or `True`:
        - Uses the `highlight_code2` function (my own implementation)
        - Uses the "rrt" theme as the base, but features custom tokens so 85% of the objects aren't colored the same
        - Accepts 5 additional arguments in `code_kwargs`:
            - `language: str | None="python",`
            - `style: str="rrt",`
            - `include_blank: bool = `True`,`
            - `max_blank: int | None = 2,`
            - `indentation_factor: int | None = 8,`
        - The code passed to this can be any code type whatsoever. A list, a string, a dictionary, your mom, it will highlight it, and using your custom token colors too!
        - You can read more about this in the documentation for `malacode.code_print.print_code.highlight_code2`
- If `True`, will treat the input text as code and, using `highlight_code` with the provided `code_kwargs`, render it as such
<hr>
&nbsp;

>>> code_kwargs: dict[str, str] or None

- If provided, and code is `1`, `2`, or, `True`, will pass the kwargs to `highlight_code`, or `highlight_code2`
- These do not apply to any other code rendering, or to any other part of this function
<hr>
&nbsp;

#### Markdown To Table Description:
- Takes a markdown table in standard format, converts it to a rich table, and returns it fit for printing
<hr>
&nbsp;

#### Markdown To Table Parameters:

>>> md_table: str

- Any markdown table in standard format
- If the justification line is included (`|:---:|---:| etc.`):
    - Its values will determine the justification of the columns only
    - If the the `column_positions` argument is provided, it will override the justification values in the markdown table
    - The line can have an arbitrary number of hyphens

<hr>
&nbsp;

#### \*\*kwargs (print[md_kwargs]):

>>> header_styles: list[str] default: ["bold #00bfff", ...]

- A list of styles to be applied to the headers of the table (e.g. "bold italic #00ff00")
- The length of this list must be equal to the number of columns in the table
<hr>
&nbsp;

>>> header_positions: list[str] default: ["center", ...]

- A list of justification values for the headers of the table ("right", "left", "center")
- The length of this list must be equal to the number of columns in the table
<hr>
&nbsp;

>>> column_styles: list[str] default: ["italic #00ff00", ...]

- A list of styles to be applied to the columns of the table (e.g. "bold italic #00ff00")
- The length of this list must be equal to the number of columns in the table
<hr>
&nbsp;

>>> column_positions: list[str] default: ["left", ...]

- A list of justification values for the columns of the table ("right", "left", "center")
- If provided, this list will override any justification line in the markdown table
<hr>
&nbsp;

>>> max_cutoff: int default: 104

- The maximum cutoff for the length of the header
- If the max_length (the greatest length of all strings within a given column) is greater than this value, the max_cutoff is used instead
- This is used because text wrapping, along with the current zoom level of the terminal on which it's displayed makes it impossible to calculate the exact length of the cell text to pad and position the headers exactly
<hr>
&nbsp;

>>> box: str default: "HEAVY_ROUNDED_HEAD"

- The box style to be used for the table
- Results in the application of one of the related Box objects from the rich.box module
- The following options are available:
    - "ASCII_DOUBLE_HEAD"
    - "ASCII"
    - "ASCII2"
    - "DOUBLE_EDGE"
    - "DOUBLE"
    - "HEAVY_EDGE"
    - "HEAVY_HEAD"
    - "HEAVY_ROUNDED_HEAD" (custom)
    - "HEAVY"
    - "HORIZONTALS"
    - "MARKDOWN"
    - "MINIMAL_DOUBLE_HEAD"
    - "MINIMAL_HEAVY_HEAD"
    - "MINIMAL"
    - "ROUNDED"
    - "SIMPLE_HEAD"
    - "SIMPLE_HEAVY"
    - "SIMPLE"
    - "SQUARE_DOUBLE_HEAD"
    - "SQUARE"
<hr>
&nbsp;

#### Highlight Code Description:
- Generates, and prints a `rich.syntax.Syntax` object (a printable syntax highlighted code block) with the given `code_kwargs`
<hr>
&nbsp;

### ***\*objects (print[objects]):***
- It is recommended to provide only a single string to the *objects parameter
- In the event that several strings are provided, they will be joined by the `sep` parameter and passed to `highlight_code` as a single string
<hr>
&nbsp;

### ***\*kwargs (print[code_kwargs]) (optional):***
- If provided, these should all be passed as a single dictionary to the `code_kwargs` argument

>>> lexer: (Lexer | str)

- Most likely a string representing the language of the code (e.g. "python" or "javascript")
- To look at documentation that doesn't really inform you on how to use the more involved features of lexers (see https://pygments.org/docs/lexers/)
<hr>
&nbsp;

>>> theme: str | SyntaxTheme = "rrt"

- Color theme, aka Pygments style (see the `code_theme_demo` function to see the most tolerable ones in action)
- Or, view this list:
    - dracula
        - Mild purple background, good contrast, decent colors: 7/10
    - fruity
        - Dark, nearly black background, good contrast, decent colors: 8/10
    - github-dark
        - Dark, not quite black background, decent contrast, good colors: 8/10
    - gruvbox-dark
        - Medium grey background, decent contrast, good colors: 8/10
    - inkpot
        - Dark purple background, ok contrast, ok, colors: 6/10
    - lightbulb
        - Medium purple-grey background, good contrast, ok colors: 7/10
    - material
        - Medium/light grey background, ok contrast, ok colors: 6/10
    - monokai
        -  Dark brown/grey background, decent contrast, ok colors: 7/10
    - native
        - Dark/dim brown/grey background, ok contrast, decent colors: 7/10
    - nord-darker
        - Medium dark blue/grey background, passible contrast, ok colors: 5/10
    - nord
        - The same as nord-darker, but with lighter background and thus, poorer contrast: 4/10
    - one-dark
        - Medium dark blue/grey background, ok contrast, very colorful (no white variables): 6/10
    - paraiso-dark
        - Very purple background, poor contrast with statement keywords, decent colors: 5/10
    - rrt
        - Very black background, very strong contrast, good colors: 9/10
    - stata-dark
        - Medium dark grey background, nearly passable contrast, poor color variation: 4/10
    - vim
        - Second darkest background, good contrast, decent colors: 7/10
    - zenburn
        - Medium grey background (the lightest), ok contrast, boring colors: 4/10
    <hr>
    &nbsp;

>>> dedent: bool=False

- Enable stripping of initial whitespace
<hr>
&nbsp;

>>> line_numbers: bool=True

- Enable rendering of line numbers
<hr>
&nbsp;

>>> start_line: int=1

- Starting number for line numbers
<hr>
&nbsp;

>>> line_range: (Tuple[int | None, int | None])

- If given should be a tuple of the start and end line to render.
- A value of None in the tuple indicates the range is open in that direction
<hr>
&nbsp;

>>> highlight_lines: (Set[int])

- A set of line numbers to highlight
<hr>
&nbsp;

>>> code_width: int=None

- Width of the code to render (not including line numbers)
- If None this will use all available width
<hr>
&nbsp;

>>> tab_size: int=4

- The size of the tabs
<hr>
&nbsp;

>>> word_wrap: bool=False

- Enables word wrapping
<hr>
&nbsp;

>>> background_color: str | None=None

- The background color, or None to use theme color
- Takes the same formats that rich print does, so you can use hex codes, color names, etc.
<hr>
&nbsp;

>>> indent_guides: bool=True

- Shows indent guides (i.e. vertical lines where indent levels change)
<hr>
&nbsp;

>>> padding PaddingDimensions (int)=0

- The padding to apply around the syntax
- 0 is no padding
<hr>
<hr>
<hr>
&nbsp;

"""

if silent:
    return

print_obj_provided = False
kwargs = {"end": end, "file": file, "flush": flush, "sep": sep, **kwargs}

if separate:
    from malacode.python_parser import get_arguments
    kwarguments = get_arguments().nargs
    kwarguments.pop("separate")
    for i, obj in enumerate(objects):
        if i == 1:
            kwarguments.pop("trace")
        print(obj, **kwarguments, separate=False)
    return

#^ Standard Library Imports
from sys import stdout
from builtins import print as bprint

#^ Third Party Imports
import rich.markdown
from rich.panel import Panel
from rich.console import Console

#@ If pager is a callable, this is a recursive call and we assign it to rprint
if callable(pager):
    rprint = pager
    print_obj_provided = True

#@ If a print_obj is provided...
elif print_obj:
    from malacode.python_parser import get_params

    #@ Filter kwargs to only those accepted by the provided print_obj and assign the object to rprint
    kwargs = {k: v for k, v in kwargs.items() if k in get_params(print_obj)}
    rprint = print_obj
    print_obj_provided = True
else:
    from rich import print as rprint
from rich.errors import NotRenderableError
from rich.style import Style

#^ Local Imports
from malacode.typings.m_types import STDOutType

#^ Import Setups
Markdown = rich.markdown.Markdown

filetype: bool = isinstance(file, (Path, str))
stdout: STDOutType
stdout.reconfigure(encoding="utf-8")

#@ If log_locals is True and no print_obj is provided, log the local variables using rich's Console.log
(kwargs.pop("log_locals", None) and not print_obj_provided) and Console().log(log_locals=True, _stack_offset=frame_offset + 1)

not objects and (objects := ("",))

if dedent:
    from malacode.text.text import dedent as dedent_text
    objects = tuple(dedent_text(str(i)) for i in objects)

#@ If pager is True, use rich's console pager to print the objects
if pager is True:
    from malacode.python_parser import get_arguments, get_params
    kwarguments = get_arguments().nargs
    kwarguments.pop("separate")

    console = Console()
    params = get_params(console.print)

    def console_wrapper(*objects, **kwargs):
        filtered = dict(filter(lambda item: item[0] in params, kwargs.items()))
        console.print(*objects, **filtered)

    kwarguments.pager = console_wrapper

    with console.pager(styles=True, links=True):
        print(*objects, **kwarguments)

    return

#@ If the first item is a MalaDict object and it should be formatted
if str(type(objects[0])) == "<class 'malacode.dict.MalaDict'>" and format_maladict:
    objs: MalaDict = objects[0]
    #@ If the MalaDict is allowed to have duplicates, convert it's data's top level object to a list
    #@ Otherwise, convert its data to a dictionary object
    objects = (objs._duplicate_data.tolist(), *objects[1:]) if objs.allow_duplicates else (objs.to_dict(), *objects[1:])

#^ Exception Display
if all([len(objects) >= 1, isinstance(objects[0], Exception), format_exceptions]):
    from malacode.errors import display_exception
    from malacode.python_parser import get_arguments

    #@ Get all keyward arguments passed to this function
    kwargs = get_arguments().nargs

    display_exception(objects[0], *objects[1:], **kwargs) #type:ignore #nosec #noqa
    return

#@ Escapes opening square brackets in strings to prevent rich from treating them as the beginning of an argument
escape_bracket and (objects := (i.replace(r"[", r"\[") if isinstance(i, str) else i for i in objects)) #type:ignore #nosec

#@ Truncates the output if the length is greater than the provided length
if truncate_len:
    from io import StringIO
    strio = StringIO()

    unique_char = "╛"
    rprint(unique_char.join([str(i) for i in objects]), **kwargs)
    if len(strio.getvalue()) > truncate_len:
        t_strobjects = strio.getvalue()[:truncate_len-len(truncate_end)] + truncate_end
        objects = tuple(t_strobjects.split(unique_char))

#@ Include a colored clickable line of the filepath, line number, and function name before the output
if trace:
    trace = frame_and_format(offset=frame_offset, exclude=frame_exclude)
    objects = (trace, *objects)

if md_table:
    from .markdown_table import markdown_to_table
    rprint(markdown_to_table(md_table, **md_kwargs), **kwargs)
    return

if code:

    from malacode.code_print.print_code import highlight_code, highlight_code2

    #@ Using string keys instead of literals to prevent matching 1 or 2 as they are both truthy
    code_map = {"1": highlight_code, "2": highlight_code2, "True": highlight_code2}

    #@ Converting to string to match the string keys
    code_highlighter = code_map[str(code)]
    hl_code = code_highlighter(end.join([*objects]), **code_kwargs if code_kwargs else {})
    (rstrip_code and hasattr(hl_code, "rstrip")) and (hl_code := hl_code.rstrip())
    rprint(hl_code, **kwargs)
    return

if markdown:
    style = Style(
        color=color,
        bgcolor=bgcolor,
        bold=bold,
        dim=dim,
        italic=italic,
        underline=underline,
        strike=strike,
        conceal=conceal,
    )

    objects = (Markdown(i, style=style) for i in objects)

if box:
    try:
        if filetype:
            with open(str(file), "w", encoding="utf-8") as file:
                rprint(Panel.fit(*objects), **kwargs)
        else:
            rprint(Panel.fit(*objects), **kwargs)

    except (NotRenderableError, TypeError):
        if filetype:
            with open(str(file), "w", encoding="utf-8") as file:
                rprint(*objects, **kwargs)
        else:
            rprint(*objects, **kwargs)
else:
    if filetype:
        with open(str(file), "w", encoding="utf-8") as file:
            rprint(*objects, **kwargs)
    else:
        rprint(*objects, **kwargs)
        if delay:
            from time import sleep
            sleep(delay)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment