bluenote10 / yachalk

🖍️ Terminal string styling done right
MIT License
161 stars 3 forks source link
ansi ansi-escape-codes chalk color console python terminal



Chalk


Terminal string styling done right

This is a feature-complete clone of the awesome Chalk (JavaScript) library.

All credits go to Sindre Sorhus.

PyPI version Build Status codecov Code style: black mypy license




Highlights

Install

$ pip install yachalk

The only requirement is a modern Python (3.6+).

Usage

from yachalk import chalk

print(chalk.blue("Hello world!"))

Chalk comes with an easy to use composable API where you just chain and nest the styles you want.

from yachalk import chalk

# Combine styled and normal strings
print(chalk.blue("Hello") + " World" + chalk.red("!"))

# Compose multiple styles using the chainable API
print(chalk.blue.bg_red.bold("Hello world!"))

# Use within f-strings
num_results = 12345
print(f"Found {chalk.bold(num_results)} results.")

# Pass in multiple arguments
print(chalk.blue("Hello", "World!"))

# Nest styles...
print(chalk.red(f"Hello {chalk.underline.bg_blue('world')}!"))

# Nest styles of the same type even (color, underline, background)
print(chalk.green(
    "I am a green line " +
    chalk.blue.underline.bold("with a blue substring") +
    " that becomes green again!"
))

# Use RGB or HEX colors
print(chalk.rgb(123, 45, 67).underline("Underlined reddish color"))
print(chalk.hex("#DEADED").bold("Bold gray!"))

Easily define and re-use your own themes:

from yachalk import chalk

error = chalk.bold.red
warning = chalk.hex("#FFA500")

print(error("Error!"))
print(warning("Warning!"))

Prior art: Why yet another chalk clone?

The Python ecosystem has a large number libraries for terminal styling/coloring. However, after working with Chalk in JavaScript for a while, I always missed to have the same convenience in Python. Inspired by Chalk, I wanted to have a terminal styling library satisfying the following design criteria:

I started collecting existing libraries in a feature matrix, but since I keep finding more and more libraries, I've given up on filling it completely 😉. Nonetheless, feel free to open an issue if it contains an error or misses an important solution.

comparison

Links to related projects:

API

In general there is no need to remember the API, because it is written in a way that it fully auto-completes in common IDEs:

auto_complete

chalk.<style>[.<style>...](string, [string...], sep=" ")

Example: chalk.red.bold.underline("Hello", "world")

Chain styles and call the last one as a method with a string argument. Order doesn't matter, and later styles take precedent in case of a conflict. This simply means that chalk.red.yellow.green is equivalent to chalk.green.

Multiple arguments will be separated by a space, but the separator can also be passed in as keyword argument sep="...".

chalk.set_color_mode(mode: ColorMode)

Set the color mode manually. ColorMode is an enum with the value:

See color mode control for more details.

chalk.disable_all_ansi()

Shorthand for chalk.set_color_mode(ColorMode.AllOff).

chalk.enable_basic_colors()

Shorthand for chalk.set_color_mode(ColorMode.Basic16).

chalk.enable_extended_colors()

Shorthand for chalk.set_color_mode(ColorMode.Extended256).

chalk.enable_full_colors()

Shorthand for chalk.set_color_mode(ColorMode.FullTrueColor).

chalk.get_color_mode() -> ColorMode

Return current color mode.

Styles

Modifiers

Colors

Background colors

256 and Truecolor color support

Chalk supports 256 colors and Truecolor (16 million colors) on supported terminal apps.

Colors are downsampled from 16 million RGB values to an ANSI color format that is supported by the terminal emulator (or by setting a specific ColorMode manually). For example, Chalk configured to run at level ColorMode.Basic16 will downsample an RGB value of #FF0000 (red) to 31 (ANSI escape for red).

Examples:

Background versions of these models are prefixed with bg_:

Color mode control

The imported symbol chalk is a singleton that is initialized with the color mode resulting from the auto-detection. This means that if you run on a terminal that has e.g. only basic 16 colors support, you can still use the full RGB/HEX API, but the resulting colors would be approximated by the available colors. If the terminal doesn't support any ANSI escape codes, the resulting strings would be completely free of any ANSI codes.

If you would like to take manual control of the color mode, you have three options.

1. Use environment variables

Chalk has introduced the convention to use the FORCE_COLOR environment variable as an override in the auto-detection. The semantics are:

This can be a convenient solution in CI/cloud-based contexts.

2. Set the color mode manually on the chalk instance

If you don't care about auto-detection, you might as well set your desired color mode unconditionally.

The chalk singleton supports setting the color mode via chalk.disable_all_ansi, chalk.enable_..._colors, or chalk.set_color_mode.

A reasonable place to configure the singleton is e.g. at the beginning of a main function, similar to where logging is configured.

3. Use your own chalk instance

For advanced use cases that e.g. require to dynamically switch the color mode in a multi-threaded context, you can opt-out of the convenience of using a singleton, and use a custom chalk instances where desired. In general chalk is just an instance of ChalkFactory, which takes the color mode as input in its constructor.

from yachalk import ChalkFactory

def some_function():
    # create your own chalk instance with explicit mode control
    chalk = ChalkFactory(ColorMode.FullTrueColor)

    # ...

    colored_messages.append(chalk.red("An error occurred"))

Or if you'd like to use your own mode-detection logic, you could create the chalk singleton yourself in one of your modules

# e.g. in my_chalk.py
from yachalk import ChalkFactory

def custom_color_mode_detection() -> ColorMode:
    # ...
    return ColorMode.Basic16

chalk = ChalkFactory(custom_color_mode_detection())

and replace usages of from yachalk import chalk with from my_chalk import chalk.