Skip to content

Commit

Permalink
Merge branch 'master' into progress_track_show_elapsed_on_finish
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan authored Mar 4, 2023
2 parents f50385d + bb1a56b commit 196bbc7
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 127 deletions.
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [13.3.2] - Unreleased

### Fixed

- Reversed `pre` and `code` tags in base HTML format https://github.com/Textualize/rich/pull/2642
- Fix syntax error when building with nuitka https://github.com/Textualize/rich/pull/2635
- Fixed pretty printing of empty dataclass https://github.com/Textualize/rich/issues/2819
- Use `Console(stderr=True)` in `rich.traceback.install` to support io redirection.
- Fixes superfluous spaces in html output https://github.com/Textualize/rich/issues/2832
- Fixed duplicate output in Jupyter https://github.com/Textualize/rich/pulls/2804
- Filter ANSI character-encoding-change codes in `Text.from_ansi` parser
- Fixes traceback failing when a frame filename is unreadable https://github.com/Textualize/rich/issues/2821

### Added

Expand Down
3 changes: 3 additions & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ The following people have contributed to the development of Rich:
- [Dave Pearson](https://github.com/davep/)
- [Avi Perl](https://github.com/avi-perl)
- [Laurent Peuch](https://github.com/psycojoker)
- [Ronny Pfannschmidt](https://github.com/RonnyPfannschmidt/)
- [Olivier Philippon](https://github.com/DrBenton)
- [Kylian Point](https://github.com/p0lux)
- [Kyle Pollina](https://github.com/kylepollina)
- [Sebastián Ramírez](https://github.com/tiangolo)
- [Felipe Guedes](https://github.com/guedesfelipe)
- [Min RK](https://github.com/minrk)
- [Clément Robert](https://github.com/neutrinoceros)
- [Brian Rutledge](https://github.com/bhrutledge)
- [Tushar Sadhwani](https://github.com/tusharsadhwani)
Expand All @@ -63,3 +65,4 @@ The following people have contributed to the development of Rich:
- [Zhe Huang](https://github.com/onlyacat)
- [Ke Sun](https://github.com/ksun212)
- [Qiming Xu](https://github.com/xqm32)
- [James Addison](https://github.com/jayaddison)
24 changes: 12 additions & 12 deletions README.pt-br.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ O Rich pode imprimir [tables](https://rich.readthedocs.io/en/latest/tables.html)

A animação acima foi gerada com o arquivo [table_movie.py](https://github.com/textualize/rich/blob/master/examples/table_movie.py) da pasta de exemplos.

Veja um exemplo mais simple:
Veja um exemplo mais simples:

```python
from rich.console import Console
Expand Down Expand Up @@ -239,9 +239,9 @@ Que gera o seguinte resultado:

![table](https://github.com/textualize/rich/raw/master/imgs/table.png)

Observe que o markup é renderizado da mesma for que em `print()` e `log()`. De fato, tudo que é renderizável pelo Rich pode ser incluído nos cabeçalhos ou linhas (até mesmo outras tabelas).
Observe que o markup é renderizado da mesma que em `print()` e `log()`. Na verdade, tudo que é renderizável pelo Rich pode ser incluído nos cabeçalhos ou linhas (até mesmo outras tabelas).

A classe `Table` é inteligente o suficiente para ajustar o tamanho das colunas para caber na largura do terminal, quebrando o texto em novas linhas quando necessário. Veja a seguir o mesmo exemplo, só que desta vez com um terminal menor do que o tamanho original da tabela:
A classe `Table` é inteligente o suficiente para ajustar o tamanho das colunas para caber na largura do terminal, quebrando o texto em novas linhas quando necessário. Veja o mesmo exemplo a seguir, só que desta vez com um terminal menor do que o tamanho original da tabela:

![table2](https://github.com/textualize/rich/raw/master/imgs/table2.png)

Expand All @@ -250,7 +250,7 @@ A classe `Table` é inteligente o suficiente para ajustar o tamanho das colunas
<details>
<summary>Barra de Progresso</summary>

O Rich consegue renderizar de forma eficiente multiplas barras de [progresso](https://rich.readthedocs.io/en/latest/progress.html) que podem ser usadas para rastrear o estado de processos longos.
O Rich consegue renderizar de forma eficiente múltiplas [barras de progresso](https://rich.readthedocs.io/en/latest/progress.html) que podem ser usadas para rastrear o estado de processos longos.

Uma forma simples de usar é passando o iterável para a função `track` e iterar normalmente sobre o retorno. Veja o exemplo a seguir:

Expand All @@ -261,22 +261,22 @@ for step in track(range(100)):
do_step(step)
```

Adicionar multiplas barras de progresso também é simples. Veja outro exemplo que existe na documentação:
Adicionar múltiplas barras de progresso também é simples. Veja outro exemplo que existe na documentação:

![progress](https://github.com/textualize/rich/raw/master/imgs/progress.gif)

As colunas podem ser configuradas pra mostrar qualquer detalho necessário. As colunas nativas incluem a porcentagem completa, tamanho de arquivo, velocidade do arquivo e tempo restante. O exemplo a seguir mostra o progresso de um download:

![progress](https://github.com/textualize/rich/raw/master/imgs/downloader.gif)

Para testar isso no seu terminal, use o arquivo [examples/downloader.py](https://github.com/textualize/rich/blob/master/examples/downloader.py) para fazer o download de multiplas URLs simultaneamente, exibindo o progresso de cada download.
Para testar isso no seu terminal, use o arquivo [examples/downloader.py](https://github.com/textualize/rich/blob/master/examples/downloader.py) para fazer o download de múltiplas URLs simultaneamente, exibindo o progresso de cada download.

</details>

<details>
<summary>Status</summary>

Em casos em que é dificil de calcular o progresso da tarefa, você pode usar o método [status](https://rich.readthedocs.io/en/latest/reference/console.html#rich.console.Console.status) que exibe uma animação de um "spinner" e a mensagem. A animação não impede em nada o uso do `console`. Veja o exemplo a seguir:
Em casos em que é dificil calcular o progresso da tarefa, você pode usar o método [status](https://rich.readthedocs.io/en/latest/reference/console.html#rich.console.Console.status) que exibe uma animação de um "spinner" e a mensagem. A animação não impede em nada o uso do `console`. Veja o exemplo a seguir:

```python
from time import sleep
Expand Down Expand Up @@ -311,7 +311,7 @@ O comando acima deve exibir o seguinte no seu terminal:
<details>
<summary>Árvore</summary>

O Rich pode renderizar [árvores](https://rich.readthedocs.io/en/latest/tree.html) com linhas de identação. Uma árvore é a forma ideal de exibir uma extrutura de arquivos ou qualquer outra apresentação hierárquica de dados.
O Rich pode renderizar [árvores](https://rich.readthedocs.io/en/latest/tree.html) com linhas de identação. Uma árvore é a forma ideal de exibir uma estrutura de arquivos ou qualquer outra apresentação hierárquica de dados.

Os titulos dos itens da árvore podem ser textos simples ou qualquer coisa que o Rich pode renderizar. Execute o comando a seguir para uma demonstração:

Expand Down Expand Up @@ -343,7 +343,7 @@ directory = os.listdir(sys.argv[1])
print(Columns(directory))
```

O screenshot a seguir é do resultado do [exemplo de colunas](https://github.com/textualize/rich/blob/master/examples/columns.py) formatando em colunas os dados extraidos de uma API:
O screenshot a seguir é do resultado do [exemplo de colunas](https://github.com/textualize/rich/blob/master/examples/columns.py) formatando em colunas os dados extraídos de uma API:

![columns](https://github.com/textualize/rich/raw/master/imgs/columns.png)

Expand All @@ -354,7 +354,7 @@ O screenshot a seguir é do resultado do [exemplo de colunas](https://github.com

O Rich pode renderizar [markdown](https://rich.readthedocs.io/en/latest/markdown.html) e faz um bom trabalho de conversão do formato para o terminal.

Para renderizar markdowm, importe a classe `Markdown` e instancie com a string que contem o código markdown. Depois, imprima o objeto no console. Por exemplo:
Para renderizar markdowm, importe a classe `Markdown` e instancie com a string que contém o código markdown. Depois, imprima o objeto no console. Por exemplo:

```python
from rich.console import Console
Expand Down Expand Up @@ -418,13 +418,13 @@ Veja o resultado disso no OSX (resultados semelhantes no Linux):

</details>

Todos os renderizaveis do Rich usam o [Protocolo do Console](https://rich.readthedocs.io/en/latest/protocol.html), que você pode usar para implementar o seu próprio conteúdo Rich.
Todos os renderizáveis do Rich usam o [Protocolo do Console](https://rich.readthedocs.io/en/latest/protocol.html), que você pode usar para implementar o seu próprio conteúdo Rich.

# Rich para empresas

Disponível como parte da assinatura Tidelift.

Os mantenedores do Rich e milhares de outros pacotes estão trabalhando com o Tidelift para disponibilizar suporte comercial e manutenção de projetos de código aberto usados nas suas aplicações. Economise tempo, reduza riscos e melhore a saúde do código enquanto paga os mantenedores dos pacotes exatos que você usa. [Mais detalhes.](https://tidelift.com/subscription/pkg/pypi-rich?utm_source=pypi-rich&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
Os mantenedores do Rich e milhares de outros pacotes estão trabalhando com o Tidelift para disponibilizar suporte comercial e manutenção de projetos de código aberto usados nas suas aplicações. Economize tempo, reduza riscos e melhore a qualidade do código enquanto paga os mantenedores dos pacotes exatos que você usa. [Mais detalhes.](https://tidelift.com/subscription/pkg/pypi-rich?utm_source=pypi-rich&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

# Projetos usando Rich

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ include = ["rich/py.typed"]
[tool.poetry.dependencies]
python = ">=3.7.0"
typing-extensions = { version = ">=4.0.0, <5.0", python = "<3.9" }
pygments = "^2.14.0"
pygments = "^2.13.0"
ipywidgets = { version = ">=7.5.1,<9", optional = true }
markdown-it-py = "^2.2.0"

Expand Down
4 changes: 1 addition & 3 deletions rich/_export_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
</head>
<html>
<body>
<pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">
<code>{code}</code>
</pre>
<pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><code>{code}</code></pre>
</body>
</html>
"""
Expand Down
3 changes: 3 additions & 0 deletions rich/ansi.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ def _ansi_tokenize(ansi_text: str) -> Iterable[_AnsiToken]:
if start > position:
yield _AnsiToken(ansi_text[position:start])
if sgr:
if sgr == "(":
position = end + 1
continue
if sgr.endswith("m"):
yield _AnsiToken("", sgr[1:-1], osc)
else:
Expand Down
14 changes: 9 additions & 5 deletions rich/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -711,11 +711,6 @@ def __init__(
self._force_terminal = None
if force_terminal is not None:
self._force_terminal = force_terminal
else:
# If FORCE_COLOR env var has any value at all, we force terminal.
force_color = self._environ.get("FORCE_COLOR")
if force_color is not None:
self._force_terminal = True

self._file = file
self.quiet = quiet
Expand Down Expand Up @@ -949,6 +944,15 @@ def is_terminal(self) -> bool:
# Return False for Idle which claims to be a tty but can't handle ansi codes
return False

if self.is_jupyter:
# return False for Jupyter, which may have FORCE_COLOR set
return False

# If FORCE_COLOR env var has any value at all, we assume a terminal.
force_color = self._environ.get("FORCE_COLOR")
if force_color is not None:
self._force_terminal = True

isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None)
try:
return False if isatty is None else isatty()
Expand Down
90 changes: 27 additions & 63 deletions rich/pretty.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,6 @@
)


JUPYTER_CLASSES_TO_NOT_RENDER = {
# Matplotlib "Artists" manage their own rendering in a Jupyter notebook, and we should not try to render them too.
# "Typically, all [Matplotlib] visible elements in a figure are subclasses of Artist."
"matplotlib.artist.Artist",
}


def _is_attr_object(obj: Any) -> bool:
"""Check if an object was created with attrs module."""
return _has_attrs and _attr_module.has(type(obj))
Expand Down Expand Up @@ -122,69 +115,40 @@ def _ipy_display_hook(
max_string: Optional[int] = None,
max_depth: Optional[int] = None,
expand_all: bool = False,
) -> None:
) -> Union[str, None]:
# needed here to prevent circular import:
from ._inspect import is_object_one_of_types
from .console import ConsoleRenderable

# always skip rich generated jupyter renderables or None values
if _safe_isinstance(value, JupyterRenderable) or value is None:
return
return None

console = console or get_console()
if console.is_jupyter:
# Delegate rendering to IPython if the object (and IPython) supports it
# https://ipython.readthedocs.io/en/stable/config/integrating.html#rich-display
ipython_repr_methods = [
"_repr_html_",
"_repr_markdown_",
"_repr_json_",
"_repr_latex_",
"_repr_jpeg_",
"_repr_png_",
"_repr_svg_",
"_repr_mimebundle_",
]
for repr_method in ipython_repr_methods:
method = getattr(value, repr_method, None)
if inspect.ismethod(method):
# Calling the method ourselves isn't ideal. The interface for the `_repr_*_` methods
# specifies that if they return None, then they should not be rendered
# by the notebook.
try:
repr_result = method()
except Exception:
continue # If the method raises, treat it as if it doesn't exist, try any others
if repr_result is not None:
return # Delegate rendering to IPython

# When in a Jupyter notebook let's avoid the display of some specific classes,
# as they result in the rendering of useless and noisy lines such as `<Figure size 432x288 with 1 Axes>`.
# What does this do?
# --> if the class has "matplotlib.artist.Artist" in its hierarchy for example, we don't render it.
if is_object_one_of_types(value, JUPYTER_CLASSES_TO_NOT_RENDER):
return

# certain renderables should start on a new line
if _safe_isinstance(value, ConsoleRenderable):
console.line()

console.print(
value
if _safe_isinstance(value, RichRenderable)
else Pretty(
value,
overflow=overflow,
indent_guides=indent_guides,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
expand_all=expand_all,
margin=12,
),
crop=crop,
new_line_start=True,
)

with console.capture() as capture:
# certain renderables should start on a new line
if _safe_isinstance(value, ConsoleRenderable):
console.line()
console.print(
value
if _safe_isinstance(value, RichRenderable)
else Pretty(
value,
overflow=overflow,
indent_guides=indent_guides,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
expand_all=expand_all,
margin=12,
),
crop=crop,
new_line_start=True,
end="",
)
# strip trailing newline, not usually part of a text repr
# I'm not sure if this should be prevented at a lower level
return capture.get().rstrip("\n")


def _safe_isinstance(
Expand Down
7 changes: 6 additions & 1 deletion rich/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,10 @@ def tokens_to_spans() -> Iterable[Tuple[str, Optional[Style]]]:

# Skip over tokens until line start
while line_no < _line_start:
_token_type, token = next(tokens)
try:
_token_type, token = next(tokens)
except StopIteration:
break
yield (token, None)
if token.endswith("\n"):
line_no += 1
Expand Down Expand Up @@ -671,6 +674,8 @@ def _get_syntax(
line_offset = max(0, start_line - 1)
lines: Union[List[Text], Lines] = text.split("\n", allow_blank=ends_on_nl)
if self.line_range:
if line_offset > len(lines):
return
lines = lines[line_offset:end_line]

if self.indent_guides and not options.ascii_only:
Expand Down
6 changes: 1 addition & 5 deletions rich/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ class Span(NamedTuple):
"""Style associated with the span."""

def __repr__(self) -> str:
return (
f"Span({self.start}, {self.end}, {self.style!r})"
if (isinstance(self.style, Style) and self.style._meta)
else f"Span({self.start}, {self.end}, {self.style!r})"
)
return f"Span({self.start}, {self.end}, {self.style!r})"

def __bool__(self) -> bool:
return self.end > self.start
Expand Down
15 changes: 13 additions & 2 deletions rich/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def install(
Callable: The previous exception handler that was replaced.
"""
traceback_console = Console(file=sys.stderr) if console is None else console
traceback_console = Console(stderr=True) if console is None else console

locals_hide_sunder = (
True
Expand Down Expand Up @@ -342,6 +342,7 @@ def from_exception(
locals_hide_dunder=locals_hide_dunder,
locals_hide_sunder=locals_hide_sunder,
)

return cls(
rich_traceback,
width=width,
Expand Down Expand Up @@ -663,7 +664,13 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
style="pygments.text",
)
else:
text = Text.assemble("in ", (frame.name, "pygments.function"))
text = Text.assemble(
"in ",
(frame.name, "pygments.function"),
(":", "pygments.text"),
(str(frame.lineno), "pygments.number"),
style="pygments.text",
)
if not frame.filename.startswith("<") and not first:
yield ""
yield text
Expand All @@ -673,6 +680,10 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
if not suppressed:
try:
code = read_code(frame.filename)
if not code:
# code may be an empty string if the file doesn't exist, OR
# if the traceback filename is generated dynamically
continue
lexer_name = self._guess_lexer(frame.filename, code)
syntax = Syntax(
code,
Expand Down
Loading

0 comments on commit 196bbc7

Please sign in to comment.