Files
apache--tvm/python/tvm/script/highlight.py
T
wehub-resource-sync 26446540fa
Lint / lint (push) Has been cancelled
CI / MacOS (push) Has been cancelled
CI / Windows (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

312 lines
9.7 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ruff: noqa: F821, RUF012
"""Highlight printed TVM script."""
import functools
import os
import shutil
import subprocess
import sys
import warnings
from typing import Any, Union
def cprint(
printable: Any | str,
style: str | None = None,
black_format: bool = False,
) -> None:
"""Print TVMScript string with Pygments highlight and Black auto-formatting.
Parameters
----------
printable : Union[IRModule, PrimFunc, str]
The TVMScript to be printed
style : str, optional
Pygmentize printing style, auto-detected if None.
black_format: bool
If true, use the formatter Black to format the TVMScript
Notes
-----
The style parameter follows the Pygments style names or Style objects. Three
built-in styles are extended: "light", "dark" and "ansi". By default, "light"
will be used for notebook environment and terminal style will be "ansi" for
better style consistency. As an fallback when the optional Pygment library is
not installed, plain text will be printed with a one-time warning to suggest
installing the Pygment library. Other Pygment styles can be found in
https://pygments.org/styles/
The default pygmentize style can also be set with the environment
variable "TVM_PYGMENTIZE_STYLE".
"""
if hasattr(printable, "script") and callable(getattr(printable, "script")):
printable = printable.script()
elif not isinstance(printable, str):
raise TypeError(
f"Only can print strings or objects with `script` method, but got: {type(printable)}"
)
if black_format:
printable = _format(printable)
is_in_notebook = "ipykernel" in sys.modules # in notebook env (support html display).
style = _get_pygments_style(style, is_in_notebook)
if style is None:
print(printable)
return
# pylint: disable=import-outside-toplevel
from pygments import highlight
from pygments.formatters import HtmlFormatter, Terminal256Formatter
from pygments.lexers.python import Python3Lexer
if is_in_notebook:
from IPython import display # pylint: disable=import-outside-toplevel
formatter = HtmlFormatter(style=style)
formatter.noclasses = True # inline styles
html = highlight(printable, Python3Lexer(), formatter)
display.display(display.HTML(html))
else:
print(highlight(printable, Python3Lexer(), Terminal256Formatter(style=style)))
@functools.lru_cache
def _get_formatter(formatter: str | None = None):
def get_ruff_formatter():
if shutil.which("ruff") is None:
return None
def formatter(code_str):
proc = subprocess.Popen(
["ruff", "format", "--stdin-filename=TVMScript"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
encoding="utf-8",
)
stdout, _stderr = proc.communicate(code_str)
return stdout
return formatter
def get_black_formatter():
try:
# pylint: disable=import-outside-toplevel
import black
except ImportError:
return None
def formatter(code_str):
return black.format_str(code_str, mode=black.FileMode())
return formatter
def get_fallback_formatter():
def formatter(code_str):
with warnings.catch_warnings():
warnings.simplefilter("once", UserWarning)
ruff_install_cmd = sys.executable + " -m pip install ruff"
black_install_cmd = (
sys.executable + ' -m pip install "black==22.3.0" --upgrade --user'
)
warnings.warn(
f"Neither the 'ruff' formatter nor the 'black' formatter is available. "
f"To print formatted TVM script, please a formatter. \n"
f"To install ruff: {ruff_install_cmd}\n"
f"To install black: {black_install_cmd}",
category=UserWarning,
)
return code_str
return formatter
# formatter = "black"
if formatter is None:
options = [get_ruff_formatter, get_black_formatter]
elif formatter == "ruff":
options = [get_ruff_formatter]
elif formatter == "black":
options = [get_black_formatter]
else:
raise ValueError(f"Unknown formatter: {formatter}")
for option in options:
func = option()
if func is not None:
return func
return get_fallback_formatter()
def _format(code_str: str, formatter: str | None = None) -> str:
"""Format a code string using Black.
Parameters
----------
code_str: str
The string containing Python/TVMScript code to format
formatter: Optional[str]
The formatter to use. Can specify `ruff`, `black`, or
auto-select by passing `None`.
Returns
-------
formatted: str
The formatted Python/TVMScript code
"""
return _get_formatter(formatter)(code_str)
def _get_pygments_style(
style: str | None, is_in_notebook: bool
) -> Union["pygments.style.Style", str] | None:
"""Select a pygments style to use
Parameters
----------
style: str
The style specifier to use. If None, auto-select a style.
is_in_notebook: bool
Whether python is currently running in a jupyter notebook.
Used for automatic selection.
Returns
-------
style: Optional[Union['pygments.style.Style',str]]
If pygments is installed, the style object or string, suitable
for use as the "style" argument to pygments formatters. If
pygments is not installed, returns None.
"""
try:
# pylint: disable=import-outside-toplevel
import pygments
from packaging import version
from pygments.style import Style
from pygments.token import Comment, Keyword, Name, Number, Operator, String
if version.parse(pygments.__version__) < version.parse("2.4.0"):
raise ImportError("Required Pygments version >= 2.4.0 but got " + pygments.__version__)
except ImportError as err:
if err.name == "packaging":
name = "packaging"
elif err.name == "pygments":
name = "Pygments>=2.4.0"
else:
raise ValueError(f'Package "{err.name}" should not be used')
with warnings.catch_warnings():
warnings.simplefilter("once", UserWarning)
install_cmd = sys.executable + f' -m pip install "{name}" --upgrade --user'
warnings.warn(
str(err)
+ "\n"
+ f"To print highlighted TVM script, please install {name}:\n"
+ install_cmd,
category=UserWarning,
)
return None
class JupyterLight(Style):
"""A Jupyter-Notebook-like Pygments style configuration (aka. "light")"""
background_color = ""
styles = {
Keyword: "bold #008000",
Keyword.Type: "nobold #008000",
Name.Function: "#0000FF",
Name.Class: "bold #0000FF",
Name.Decorator: "#AA22FF",
String: "#BA2121",
Number: "#008000",
Operator: "bold #AA22FF",
Operator.Word: "bold #008000",
Comment: "italic #007979",
}
class VSCDark(Style):
"""A VSCode-Dark-like Pygments style configuration (aka. "dark")"""
background_color = ""
styles = {
Keyword: "bold #c586c0",
Keyword.Type: "#82aaff",
Keyword.Namespace: "#4ec9b0",
Name.Class: "bold #569cd6",
Name.Function: "bold #dcdcaa",
Name.Decorator: "italic #fe4ef3",
String: "#ce9178",
Number: "#b5cea8",
Operator: "#bbbbbb",
Operator.Word: "#569cd6",
Comment: "italic #6a9956",
}
class AnsiTerminalDefault(Style):
"""The default style for terminal display with ANSI colors (aka. "ansi")"""
background_color = ""
styles = {
Keyword: "bold ansigreen",
Keyword.Type: "nobold ansigreen",
Name.Class: "bold ansiblue",
Name.Function: "bold ansiblue",
Name.Decorator: "italic ansibrightmagenta",
String: "ansiyellow",
Number: "ansibrightgreen",
Operator: "bold ansimagenta",
Operator.Word: "bold ansigreen",
Comment: "italic ansibrightblack",
}
if style == "light":
return JupyterLight
elif style == "dark":
return VSCDark
elif style == "ansi":
return AnsiTerminalDefault
if style is not None:
return style
style_from_environment = os.environ.get("TVM_PYGMENTIZE_STYLE", "").strip()
if style_from_environment:
return style_from_environment
if is_in_notebook:
return JupyterLight
return AnsiTerminalDefault