# Rich: the README says Python 3.8, the package needs 3.9

> Rich is Textualize's MIT licensed library for colour, tables, progress bars, markdown and syntax highlighting in the terminal, and it is still the least invasive way to make a CLI readable. Two details decide whether it fits: the newest release, v15.0.0, dropped Python 3.8 while the README still promises it, and styling lives in a bbcode-like markup that will eat square brackets in text you did not write.

**Textualize/rich** — GitHub describes it as Rich is a Python library for rich text and beautiful formatting in the terminal.. The repository metadata lists Python as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

- Repository: https://github.com/Textualize/rich
- Website: https://rich.readthedocs.io/en/latest/
- Stars: 57,451 · Forks: 2,381
- Language: Python
- License: MIT
- Published: 2026-08-13 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/textualize-rich

## The README promises Python 3.8, pyproject requires 3.9

Read the compatibility paragraph and the packaging metadata together, because they disagree. The README says Rich requires Python 3.8 or later, and the newest release is titled v15.0.0, The So Long 3.8 Release, dated 2026-04-12. In pyproject.toml the floor is python = ">=3.9.0" and the classifiers start at Programming Language :: Python :: 3.9 and run to 3.14, with Development Status :: 5 - Production/Stable. So a team still on 3.8 reads a README that promises support, installs the newest version, and meets a resolver that refuses it. Pin an older release deliberately or move the interpreter, and do not rely on the README's compatibility line when the constraint that actually blocks the install lives in the package metadata.

## The test suite runs with TERM=unknown on purpose

The Makefile shows how this project tests itself, and the first line is the interesting one:

```bash
test:
	TERM=unknown pytest --cov-report term-missing --cov=rich tests/ -vv
```

Forcing TERM=unknown means the suite never runs against a colour-capable terminal, so the no-colour path is the one under test on every run, and the coverage report is scoped to the rich package with missing lines called out. The remaining targets are equally plain: black --check . for formatting, mypy -p rich --no-incremental for types with an HTML report variant, and a docs target that cds into docs and runs make html. Performance has its own apparatus, with asv.conf.json, an asvhashfile and a benchmarks/ directory, though none of the Makefile targets shown run it.

## setup.py is a shim, and poetry does the real build

There is a setup.py at the top level and it does not build anything. Its own comment says it is a shim to hopefully allow Github to detect the package and that the build is done with poetry, and the only argument it passes is the name. Anything that tries to install this project through setuptools therefore has no package list to work from, which is one more reason the documented route is the published wheel. The real metadata is Poetry's: name rich, version 15.0.0, author Will McGugan, MIT, built with poetry-core>=1.0.0 through the poetry.core.masonry.api backend. Two details there matter downstream. include = ["rich/py.typed"] ships the marker file that makes the package typed for consumers, and the mypy configuration runs strict over the rich directory with three extra error codes enabled.

## Styling lives in the string, so square brackets are not yours

Coarse styling is a keyword argument, console.print("Hello", "World!", style="bold red"), and fine styling is markup embedded in the text itself, similar in syntax to bbcode: console.print("Where there is a [bold cyan]Will[/bold cyan] there [u]is[/u] a [i]way[/i]."). That second form is the pleasant one to write and the one to be careful with. Any string containing square brackets goes through the same parser, so text that came from a user, a log file or an API response can lose characters or pick up styling, and the README does not describe a way to turn markup off for a single call. The same paragraph contains the other behaviour worth internalising: unlike the builtin print, Rich word-wraps your text to fit the terminal width, which changes the shape of every line you emit.

## console.log adds a time column and the call site

The log method is the debugging one. It has an interface similar to print(), and it renders a column for the current time and the file and line which made the call, syntax highlights Python structures and repr strings by default, and pretty prints a collection so it fits the space available. Pass log_locals=True and it adds a table of the local variables at the call site, which is how the README's example inspects a dict of context next to a list of JSON-RPC style records. For anything using the standard library, there is also a builtin Handler class that formats and colourises logging module output. The limit is worth stating plainly: this is output built for a terminal, so a service that needs to ship its logs to a collector still logs through the logging module, and uses the handler only for the colouring.

## pretty.install() changes the interpreter, not just your script

Two entry points are about exploration rather than output. pretty.install() puts Rich into the Python REPL, after which any data structure you echo is pretty printed and highlighted, which is the fastest way to look at a nested object without writing a loop. rich.inspect does the same as a function, producing a report on any Python object such as a class, an instance or a builtin, and inspect(my_list, methods=True) adds the object's methods to the report. The trade is output size: the methods flag walks attributes, so inspecting something with a large surface produces more than you wanted to read. Both are session-level conveniences, and pretty.install() in particular changes the interpreter you are sitting at rather than the program you ship.

## True colour needs Windows Terminal, a classic console gets 16

Compatibility is stated plainly: Rich works with Linux, macOS and Windows, true colour and emoji work with the new Windows Terminal, and a classic terminal is limited to 16 colours. That boundary decides where a deployment lands. A build agent, a serial console or a legacy terminal emulator will not render the palette the design assumes, so screenshots from your laptop will not match production output, and the difference is invisible until someone looks at both. Emoji are inserted by putting the name between two colons. The README also states that Rich works with Jupyter notebooks with no additional configuration, while pyproject.toml carries ipywidgets as an optional extra under jupyter, so there is a Jupyter dependency path even though the notebook experience is described as configuration free.

## Eighteen README translations and a generated FAQ

The repository is wider than the library. Alongside README.md there are eighteen translated versions, from README.cn.md and README.zh-tw.md through README.de.md, README.de-ch.md for Swiss German, README.es.md, README.fr.md, README.hi.md, README.id.md, README.it.md, README.ja.md, README.kr.md, README.pl.md, README.pt-br.md, README.ru.md, README.sv.md and README.tr.md, plus README.fa.md for Persian. The FAQ is generated rather than hand-written, with FAQ.md at the top level and a .faq/ directory driven by faq.yml and questions/. Governance files sit alongside them, AI_POLICY.md, SECURITY.md, CODE_OF_CONDUCT.md, CONTRIBUTORS.md and CONTRIBUTING.md. The examples/ directory is the fastest way to learn the API, with two dozen scripts covering bars, columns, progress, downloader, exception, export, highlighter, jobs, layout, link, listdir and a printed calendar.

## Conclusion

Adopt Rich for a command line tool, a log stream or a debugging session when word-wrapped output, syntax highlighting and progress display matter more than a dependency-free script. Do not adopt it for Python 3.8 targets, because the newest release v15.0.0 from 2026-04-12 requires 3.9 while the README still says 3.8, and do not pass user-supplied strings to Console.print with markup on unless you have checked how they parse. Verify first what your terminal actually supports, since a classic Windows console gives you 16 colours rather than true colour.

## FAQ

### What is Python rich?

Rich is a MIT licensed Python library from Textualize for rich text and formatting in the terminal. It renders tables, progress bars, markdown, syntax highlighted source code and tracebacks, and it installs with python -m pip install rich and can be smoke tested with python -m rich.

### What does the rich console do?

Console is the object you construct for full control over terminal content. Its print method has an interface intentionally similar to the builtin print, except that Rich word-wraps your text to the terminal width, and you can set a style for a whole line with the style keyword or use markup for finer control.

### What is rich text in Python?

In Rich it is markup embedded in the string, similar in syntax to bbcode, so console.print("Where there is a [bold cyan]Will[/bold cyan] there [u]is[/u] a [i]way[/i].") turns style tags into terminal styling. The library also ships a rich print with the same signature as the builtin print, and pretty.install() to apply the same rendering in the REPL.

## Sources

- [Official documentation](https://rich.readthedocs.io/en/latest/)
- [Official README](https://github.com/Textualize/rich#readme)
- [Project repository](https://github.com/Textualize/rich)
- [Release notes](https://github.com/Textualize/rich/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/textualize-rich
