Rich 高亮机制完全指南:自动语法高亮、自定义 Highlighter 与内置高亮器详解
【免费下载链接】richRich is a Python library for rich text and beautiful formatting in the terminal.项目地址: https://gitcode.com/gh_mirrors/ri/rich
Rich 是一款用于在终端中生成富文本与精美格式的 Python 库,而文本高亮是它最直观、最常用的能力之一:当你用print或log输出内容时,Rich 会自动识别文本中的数字、字符串、布尔值、None、文件路径、URL、UUID 等模式并施加颜色样式,让日志和调试输出一目了然。本篇指南以 docs/source/highlighting.rst 为骨架,结合 rich/highlighter.py、rich/console.py、rich/text.py 等源码实现,系统讲解高亮的开关控制、自定义高亮器的两种写法(正则驱动与逐字符控制)、以及仓库内置的多个高亮器。读完后你将能熟练地为自己的终端输出定制专属高亮规则。
一、默认高亮:Rich 自动识别哪些模式
Rich 在渲染字符串时会自动对文本做高亮处理。以Console.print输出为例,默认情况下以下模式都会被识别并着色:
- 数字:整数、浮点数、科学计数法、十六进制数;
- 字符串:单引号 / 双引号 / 三引号包裹的文本;
- 集合与括号:
[]、{}、()等括号符号; - 布尔值与 None:
True、False、None; - 路径与文件名:如
/foo/bar/baz.py; - URL:
https://、http://、ws://、file://等协议开头的链接; - UUID:标准 8-4-4-4-12 十六进制格式;
- 以及 IPv4、IPv6、MAC 地址(EUI-48 / EUI-64)、函数调用、省略号等更"小众"的模式。
这些规则集中定义在默认高亮器ReprHighlighter中,见 rich/highlighter.py。它把模式分成若干命名组,例如number、str、path、filename、url、uuid、ipv4、ipv6、bool_true、none等,再配合 rich/default_styles.py 中的默认样式表完成着色:
| 样式名 | 默认效果 |
|---|---|
repr.number | 青色(cyan)加粗 |
repr.str | 绿色(green) |
repr.bool_true | 亮绿(bright_green)斜体 |
repr.bool_false | 亮红(bright_red)斜体 |
repr.none | 洋红(magenta)斜体 |
repr.url | 亮蓝(bright_blue)下划线 |
repr.uuid | 亮黄(bright_yellow) |
repr.path/repr.filename | 洋红 / 亮洋红 |
repr.ipv4/repr.ipv6 | 亮绿加粗 |
二、高亮的开关控制:全局与局部
高亮默认开启,但你可以通过三种粒度进行控制。
2.1 在 print / log 上按次关闭
在Console.print或Console.log上设置highlight=False,即可对本次调用禁用高亮:
from rich.console import Console console = Console() console.print("https://example.org is a URL") # URL 会被高亮 console.print("https://example.org is a URL", highlight=False) # 本次不高亮2.2 在 Console 构造函数上全局关闭
在Console(...)构造函数中设置highlight=False,则所有输出默认都不再高亮。查看 rich/console.py 的构造函数签名可以看到,highlight默认值为True,highlighter默认为ReprHighlighter():
from rich.console import Console console = Console(highlight=False) console.print("2024-01-01 08:00:00") # 时间字符串不会被高亮2.3 全局关闭后按需开启
highlight参数在 print / log 上的取值遵循"局部优先、未指定则回落全局"的逻辑。在 render_str 的实现 中可以看到判断方式:
highlight_enabled = highlight or (highlight is None and self._highlight)也就是说,当传入highlight=True时强制开启;传入False时强制关闭;传入None(默认)时继承 Console 构造函数的全局设置。因此即使你在构造函数上关闭了高亮,仍然可以在个别 print / log 调用中传highlight=True来选择性开启。
from rich.console import Console console = Console(highlight=False) # 全局关闭 console.print("Send funds to money@example.org", highlight=True) # 本条强制开启2.4 彻底关闭:NullHighlighter
除了用布尔开关,你还可以把高亮器显式替换为 NullHighlighter。它的highlight方法什么都不做("Nothing to do"),其文档字符串说明它"用于彻底禁用高亮":
from rich.console import Console from rich.highlighter import NullHighlighter console = Console(highlighter=NullHighlighter())在源码内部,rich/console.py 还维护了一个模块级单例_null_highlighter,当构造 Console 时未显式传入highlighter或传入为空时使用;默认情况下则使用ReprHighlighter()(见 rich/console.py)。
三、自定义高亮器(一):RegexHighlighter 正则驱动
如果默认高亮无法满足需求,最便捷的方式是继承 RegexHighlighter:它接收一组正则表达式,凡是匹配的文本都会被施加样式。原文档给出了一个识别邮箱地址的经典例子,这也是仓库 examples/highlighter.py 中的完整示例:
from rich.console import Console from rich.highlighter import RegexHighlighter from rich.theme import Theme class EmailHighlighter(RegexHighlighter): """Apply style to anything that looks like an email.""" base_style = "example." highlights = [r"(?P<email>[\w-]+@([\w-]+\.)+[\w-]+)"] theme = Theme({"example.email": "bold magenta"}) console = Console(highlighter=EmailHighlighter(), theme=theme) console.print("Send funds to money@example.org")3.1 highlights 与 base_style 的配合机制
RegexHighlighter只需声明两个类变量:
highlights:一个正则表达式列表。每个正则表达式的命名组((?P<name>...))会被翻译为样式名;base_style:一个前缀字符串。任何匹配组的样式名都会被加上此前缀。
上例中,正则的命名组为email,base_style为"example.",于是匹配到的邮箱文本最终应用样式"example.email",而该样式恰好定义在自定义Theme中("bold magenta",加粗洋红)。
从源码看,RegexHighlighter.highlight的实现非常精简,见 rich/highlighter.py:
def highlight(self, text: Text) -> None: highlight_regex = text.highlight_regex for re_highlight in self.highlights: highlight_regex(re_highlight, style_prefix=self.base_style)它遍历highlights中的每个正则,委托给Text.highlight_regex。真正的样式落地发生在 rich/text.py:highlight_regex会在纯文本上执行finditer匹配,然后把每个命名组的起止位置转换为Span(start, end, f"{style_prefix}{name}")追加到文本的 span 列表中。也就是说,命名组名 + base_style 前缀 = 最终样式名,这是整个 RegexHighlighter 机制的核心约定。
3.2 挂载到 Console 与局部调用
把高亮器挂在Console(highlighter=...)上,之后所有 print 输出(在开启高亮的前提下)都会经过它。另一种更细粒度的用法是把高亮器实例当作可调用对象,手动处理某段文本后再交给 console 输出:
from rich.console import Console from rich.theme import Theme # 复用上一节的 EmailHighlighter 定义 console = Console(theme=theme) highlight_emails = EmailHighlighter() console.print(highlight_emails("Send funds to money@example.org"))这是因为 Highlighter.call接受str或Text:若传入字符串会先包装为Text,若传入Text则会复制一份再就地高亮(避免污染原对象),最后返回带样式的高亮文本。传入其他类型会抛出TypeError。
四、自定义高亮器(二):继承 Highlighter 抽象基类
RegexHighlighter虽强,但终究受限于"正则 + 命名组"这一模式。如果你想完全自定义高亮逻辑,可以直接继承抽象基类 Highlighter。它只要求实现一个方法:
class Highlighter(ABC): def __call__(self, text): ... # 已实现:处理 str/Text 输入 @abstractmethod def highlight(self, text: Text) -> None: """Apply highlighting in place to text."""highlight接收一个 Text 对象并就地修改。原文档给出了一个"彩虹高亮器"的例子——给每个字符随机分配一种颜色:
from random import randint from rich import print from rich.highlighter import Highlighter class RainbowHighlighter(Highlighter): def highlight(self, text): for index in range(len(text)): text.stylize(f"color({randint(16, 255)})", index, index + 1) rainbow = RainbowHighlighter() print(rainbow("I must not fear. Fear is the mind-killer."))这里用到的Text.stylize(style, start, end)在 rich/text.py 中实现:它把Span(start, end, style)追加到文本的 span 列表,并支持负索引与越界保护。f"color({randint(16, 255)})"会解析为 16–255 号调色板颜色,从而让每个字符呈现不同颜色。这个例子展示了自定义高亮器的本质——在 Text 对象上按任意规则附加 span,样式系统会负责最终的渲染。
五、内置高亮器一览
rich.highlighter模块中预置了以下高亮器,可直接导入使用。
5.1 ReprHighlighter(默认)
ReprHighlighter的文档描述为"高亮__repr__方法典型产出的文本",它是 Console 的默认高亮器。其规则覆盖范围最广,包含:
- 标签结构(
<tag_name>...</tag_name>); - 属性名与属性值(
key=value); - 括号
[]{}(); - IPv4 / IPv6 / EUI-48 / EUI-64 地址;
- UUID;
- 函数调用(
name(); True/False/None;- 复数、普通数字(含十六进制);
- 路径与文件名;
- 字符串字面量;
- URL。
5.2 JSONHighlighter
JSONHighlighter(见 rich/highlighter.py)用于高亮 JSON 文本,基础样式前缀为"json.",覆盖括号、true/false/null、数字与字符串。它还在父类正则高亮的基础上额外重写了highlight:通过re.finditer扫描字符串字面量,若其后(跳过空白)紧跟着冒号:,则判定该字符串为 JSON 键,追加json.key样式。默认样式表在 rich/default_styles.py 中定义:
| 样式名 | 默认效果 |
|---|---|
json.brace | 加粗 |
json.bool_true | 亮绿斜体 |
json.bool_false | 亮红斜体 |
json.null | 洋红斜体 |
json.number | 青色加粗 |
json.str | 绿色 |
json.key | 蓝色加粗 |
5.3 ISO8601Highlighter
ISO8601Highlighter(见 rich/highlighter.py)专用于高亮 ISO 8601 日期时间字符串,样式前缀为"iso8601."。它的规则非常细致,覆盖了年月、日期、星期、时间、时区以及带时区的日期时间、XML Schema 的date/time/dateTime类型等十余种变体。默认样式为iso8601.date(蓝色)、iso8601.time(洋红)、iso8601.timezone(黄色),见 rich/default_styles.py。
5.4 NullHighlighter
见上文 2.4 节,用于彻底禁用高亮。
六、源码视角:高亮在渲染链路中如何生效
理解整条调用链有助于你在复杂场景中定位高亮行为。以console.print("...")为例:
Console.print收集渲染对象,把highlight参数传入渲染选项(见 rich/console.py);- 字符串最终进入
Console.render_str,其中计算highlight_enabled(局部参数优先,否则取构造函数默认值); - 若启用高亮,取
highlighter or self.highlighter,调用_highlighter(str(rich_text)),得到高亮后的Text再拷贝样式回原文本(rich/console.py); RegexHighlighter内部调用Text.highlight_regex,把每个正则命名组的区间转为Span;- 渲染阶段,
Text的 span 叠加出最终样式(参考 rich/text.py 的get_style_at_offset:按字符偏移合并所有覆盖该位置的 span 样式)。
log方法同样接收highlight参数(默认None,回落全局设置),见 rich/console.py,因此在日志输出上也可以独立控制高亮。
七、配套示例与测试
- 示例:examples/highlighter.py 提供了与本文 3.1 节完全一致的
EmailHighlighter完整可运行代码;examples/log.py、examples/table.py 等示例也能直观看到默认高亮在真实输出中的效果。 - 测试:tests/test_highlighter.py 覆盖了自定义高亮器对非法类型抛错(
test_wrong_type)、highlight_regex的 span 产出(test_highlight_regex)、JSON 高亮在有无缩进及纯字符串场景下的行为(test_highlight_json_*),以及 ISO 8601 高亮的正则匹配(test_highlight_iso8601_regex)。阅读这些测试可以快速理解高亮器的输入输出契约。 - 样式表:rich/default_styles.py 集中定义了
repr.*、json.*、iso8601.*三组高亮默认样式,修改或补充样式时可在此查找参考。 - 主题机制:自定义高亮器常与
Theme配合使用,主题的完整说明见 docs/source/theme.rst。
八、小结:高亮器的选型建议
| 需求 | 推荐方案 |
|---|---|
| 开箱即用的默认高亮 | 直接使用 Console 默认的ReprHighlighter |
| 高亮某种特定模式(邮箱、ID、日志关键字等) | 继承RegexHighlighter,用命名组 +base_style+ 自定义Theme |
| 高亮 JSON / ISO 8601 时间字符串 | 直接用内置的JSONHighlighter/ISO8601Highlighter |
| 完全自定义的高亮算法 | 继承Highlighter,重写highlight(text),在Text上按需stylize |
| 需要关闭高亮 | highlight=False(print/log 或 Console 构造函数),或挂载NullHighlighter |
掌握高亮机制后,无论是日志着色、数据展示还是 REPL 风格的输出,你都能用最少的代码让终端文本变得层次分明、易于阅读。
【免费下载链接】richRich is a Python library for rich text and beautiful formatting in the terminal.项目地址: https://gitcode.com/gh_mirrors/ri/rich
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考