diff --git a/.github/workflows/readmechanged.yml b/.github/workflows/readmechanged.yml new file mode 100644 index 000000000..e4729f6a7 --- /dev/null +++ b/.github/workflows/readmechanged.yml @@ -0,0 +1,24 @@ +name: README.md Changed + +on: + push: + branches: + - master + paths: + - 'README.md' + +jobs: + send_notification: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Send notification to README Authors + env: + GITHUB_TOKEN: ${{ secrets.GHP_README_WORKFLOW }} + GIT_SHA: ${{ github.sha }} + run: | + COMMIT=$(git rev-parse --short "$GIT_SHA") + AUTHORS='@willmcgugan @oleksis @Adilius' + BODY="๐Ÿค“ $AUTHORS README.md changed ๐Ÿ“. Check the [commit $COMMIT](https://github.com/willmcgugan/rich/commit/$GIT_SHA) ๐Ÿ‘€" + DISCUSSIONID='MDEwOkRpc2N1c3Npb24zMzI2NzM0' + gh api graphql -H 'GraphQL-Features: discussions_api' -f body="$BODY" -F discussionId="$DISCUSSIONID" -f query='mutation($body: String!, $discussionId: ID!){addDiscussionComment(input:{body: $body , discussionId: $discussionId}){comment{id}}}' diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f0ddfec..a385cf19d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ 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). +## [10.2.0] - 2021-05-12 + +### Added + +- Added syntax for call, i.e. "Foo(bar)" +- Added Console.measure as a convenient alias for Measurement.get +- Added support for pretty printing attrs objects +- Added mappingproxy to pretty print +- Added UserDict and UserList support to pretty printer + +### Changed + +- Changed colorama init to set strip=False +- Changed highlighter for False, True, None to not match in the middle of a word. i.e. NoneType is no longer highlighted as None + +### Fixed + +- Fixed initial blank lines removed from Syntax https://github.com/willmcgugan/rich/issues/1214 + ## [10.1.0] - 2020-04-03 ### Fixed @@ -29,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved layout.tree - Changed default theme color for repr.number to cyan - `__rich_measure__` signature changed to accept ConsoleOptions rather than max_width +- `text` parameter to rich.spinner.Spinner changed to RenderableType ### Added @@ -48,6 +68,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed incorrect measurement of Text with new lines and whitespace https://github.com/willmcgugan/rich/issues/1133 - Made type annotations consistent for various `total` keyword arguments in `rich.progress` and rich.`progress_bar` - Disabled Progress no longer displays itself when starting https://github.com/willmcgugan/rich/pull/1125 +- Animations no longer reset when updating rich.status.Status ## [9.13.0] - 2021-03-06 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e27d5659..2cd9ccaa4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,7 +50,7 @@ make typecheck Or if you don't have `make`: ``` -mypy -p rich --ignore-missing-imports --warn-unreachable +mypy -p rich --config-file= --ignore-missing-imports --no-implicit-optional --warn-unreachable ``` Please add type annotations for all new code. diff --git a/Makefile b/Makefile index 6bed4f767..9fed13906 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,9 @@ format-check: format: black . typecheck: - mypy -p rich --ignore-missing-imports --warn-unreachable + mypy -p rich --strict typecheck-report: - mypy -p rich --ignore-missing-imports --warn-unreachable --html-report mypy_report + mypy -p rich --strict --html-report mypy_report .PHONY: docs docs: cd docs; make html diff --git a/README.es.md b/README.es.md index a433067d5..d5f668a55 100644 --- a/README.es.md +++ b/README.es.md @@ -1,10 +1,13 @@ -# Rich - +[![Downloads](https://pepy.tech/badge/rich/month)](https://pepy.tech/project/rich) [![PyPI version](https://badge.fury.io/py/rich.svg)](https://badge.fury.io/py/rich) [![codecov](https://codecov.io/gh/willmcgugan/rich/branch/master/graph/badge.svg)](https://codecov.io/gh/willmcgugan/rich) [![Rich blog](https://img.shields.io/badge/blog-rich%20news-yellowgreen)](https://www.willmcgugan.com/tag/rich/) [![Twitter Follow](https://img.shields.io/twitter/follow/willmcgugan.svg?style=social)](https://twitter.com/willmcgugan) +![Logo](https://github.com/willmcgugan/rich/raw/master/imgs/logo.svg) + +[ไธญๆ–‡ readme](https://github.com/willmcgugan/rich/blob/master/README.cn.md) โ€ข [Lengua espaรฑola readme](https://github.com/willmcgugan/rich/blob/master/README.es.md) โ€ข [Deutsche readme](https://github.com/willmcgugan/rich/blob/master/README.de.md) โ€ข [Lรคs pรฅ svenska](https://github.com/willmcgugan/rich/blob/master/README.sv.md) โ€ข [ๆ—ฅๆœฌ่ชž readme](https://github.com/willmcgugan/rich/blob/master/README.ja.md) โ€ข [ํ•œ๊ตญ์–ด readme](https://github.com/willmcgugan/rich/blob/master/README.kr.md) + Rich es un paquete de Python para texto _enriquecido_ y un hermoso formato en la terminal. La [API Rich](https://rich.readthedocs.io/en/latest/) facilita la adiciรณn de color y estilo a la salida del terminal. Rich tambiรฉn puede representar tablas bonitas, barras de progreso, markdown, cรณdigo fuente resaltado por sintaxis, trazas y mรกs โ€” listo para usar. @@ -398,7 +401,15 @@ Asรญ es como se ve en OSX (similar en Linux): -## Proyecto usando Rich +Todos los renderizables enriquecidos utilizan el [Console Protocol](https://rich.readthedocs.io/en/latest/protocol.html), que tambiรฉn puede utilizar para implementar su propio contenido Rich. + +# Rich para empresas + +Disponible como parte de la suscripciรณn a Tidelift. + +Los mantenedores de Rich y miles de otros paquetes estรกn trabajando con Tidelift para brindar soporte comercial y mantenimiento para los paquetes de cรณdigo abierto que usa para construir sus aplicaciones. Ahorre tiempo, reduzca el riesgo y mejore el estado del cรณdigo, mientras paga a los mantenedores de los paquetes exactos que utiliza. [Mรกs informaciรณn](https://tidelift.com/subscription/pkg/pypi-rich?utm_source=pypi-rich&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + +# Proyecto usando Rich Aquรญ hay algunos proyectos que usan Rich: diff --git a/README.kr.md b/README.kr.md new file mode 100644 index 000000000..19d8f5b04 --- /dev/null +++ b/README.kr.md @@ -0,0 +1,447 @@ +[![Downloads](https://pepy.tech/badge/rich/month)](https://pepy.tech/project/rich) +[![PyPI version](https://badge.fury.io/py/rich.svg)](https://badge.fury.io/py/rich) +[![codecov](https://codecov.io/gh/willmcgugan/rich/branch/master/graph/badge.svg)](https://codecov.io/gh/willmcgugan/rich) +[![Rich blog](https://img.shields.io/badge/blog-rich%20news-yellowgreen)](https://www.willmcgugan.com/tag/rich/) +[![Twitter Follow](https://img.shields.io/twitter/follow/willmcgugan.svg?style=social)](https://twitter.com/willmcgugan) + +![Logo](https://github.com/willmcgugan/rich/raw/master/imgs/logo.svg) + +[ไธญๆ–‡ readme](https://github.com/willmcgugan/rich/blob/master/README.cn.md) โ€ข [Lengua espaรฑola readme](https://github.com/willmcgugan/rich/blob/master/README.es.md) โ€ข [Deutsche readme](https://github.com/willmcgugan/rich/blob/master/README.de.md) โ€ข [Lรคs pรฅ svenska](https://github.com/willmcgugan/rich/blob/master/README.sv.md) โ€ข [ๆ—ฅๆœฌ่ชž readme ](https://github.com/willmcgugan/rich/blob/master/README.ja.md) โ€ข [ํ•œ๊ตญ์–ด readme](https://github.com/willmcgugan/rich/blob/master/README.kr.md) + +Rich๋Š” ํ„ฐ๋ฏธ๋„์—์„œ _ํ’๋ถ€ํ•œ(rich)_ ํ…์ŠคํŠธ์™€ ์•„๋ฆ„๋‹ค์šด ์„œ์‹์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•œ ํŒŒ์ด์ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. + +[Rich API](https://rich.readthedocs.io/en/latest/)๋Š” ํ„ฐ๋ฏธ๋„ ์ถœ๋ ฅ์— ์ƒ‰๊น”๊ณผ ์Šคํƒ€์ผ์„ ์ž…ํžˆ๊ธฐ ์‰ฝ๊ฒŒ ๋„์™€์ค๋‹ˆ๋‹ค. ๋˜ํ•œ Rich๋Š” ๋ณ„๋‹ค๋ฅธ ์„ค์ • ์—†์ด ํ‘œ, ์ง„ํ–‰ ๋ฐ”, ๋งˆํฌ๋‹ค์šด, ์†Œ์Šค์ฝ”๋“œ ๊ตฌ๋ฌธ ๊ฐ•์กฐ, tracebacks ๋“ฑ์„ ์˜ˆ์˜๊ฒŒ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +![Features](https://github.com/willmcgugan/rich/raw/master/imgs/features.png) + +Rich์— ๋Œ€ํ•œ ๋™์˜์ƒ ์„ค๋ช…์„ ๋ณด์‹œ๋ ค๋ฉด [@fishnets88](https://twitter.com/fishnets88)์˜ [calmcode.io](https://calmcode.io/rich/introduction.html)๋ฅผ ํ™•์ธ ๋ฐ”๋ž๋‹ˆ๋‹ค. + +[์‚ฌ๋žŒ๋“ค์˜ Rich์— ๋Œ€ํ•œ ์˜๊ฒฌ](https://www.willmcgugan.com/blog/pages/post/rich-tweets/)์„ ํ™•์ธํ•ด๋ณด์„ธ์š”. + +## ํ˜ธํ™˜์„ฑ + +Rich๋Š” ๋ฆฌ๋ˆ…์Šค, OSX, ์œˆ๋„์šฐ์—์„œ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ํŠธ๋ฃจ ์ปฌ๋Ÿฌ / ์ด๋ชจ์ง€๋Š” ์ƒˆ๋กœ์šด ์œˆ๋„์šฐ ํ„ฐ๋ฏธ๋„์—์„œ ๋™์ž‘ํ•˜์ง€๋งŒ ๊ตฌํ˜• ํ„ฐ๋ฏธ๋„์—์„œ๋Š” 16๊ฐ€์ง€ ์ƒ‰์œผ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. Rich๋Š” ํŒŒ์ด์ฌ 3.6.1 ๋ฒ„์ „ ํ˜น์€ ๊ทธ ์ดํ›„ ๋ฒ„์ „์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. + +Rich๋Š” [Jupyter notebooks](https://jupyter.org/)์—์„œ ๋ณ„๋„์˜ ์„ค์ •์—†์ด ๋ฐ”๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. + +## ์„ค์น˜ + +`pip` ๋˜๋Š” ์ข‹์•„ํ•˜๋Š” Pypi ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €๋กœ ์„ค์น˜ํ•˜์„ธ์š”. + +``` +pip install rich +``` + +์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ํ„ฐ๋ฏธ๋„์—์„œ Rich ์ถœ๋ ฅ์„ ํ…Œ์ŠคํŠธํ•ด๋ณด์„ธ์š”. + +``` +python -m rich +``` + +## Rich Print + +๊ฐ„๋‹จํ•˜๊ฒŒ ๋‹น์‹ ์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— richํ•œ ์ถœ๋ ฅ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด, ํŒŒ์ด์ฌ ๋‚ด์žฅ ํ•จ์ˆ˜์™€ signature๊ฐ€ ๊ฐ™์€ [rich print](https://rich.readthedocs.io/en/latest/introduction.html#quick-start) ๋ฉ”์„œ๋“œ๋ฅผ import ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +๋”ฐ๋ผํ•ด๋ณด์„ธ์š”: + +```python +from rich import print + +print("Hello, [bold magenta]World[/bold magenta]!", ":vampire:", locals()) +``` + +![Hello World](https://github.com/willmcgugan/rich/raw/master/imgs/print.png) + +## Rich REPL + +Rich๋Š” ํŒŒ์ด์ฌ REPL์—๋„ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ผ๋„ ์˜ˆ์˜๊ฒŒ ์ถœ๋ ฅํ•˜๊ฑฐ๋‚˜ ๊ฐ•์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +```python +>>> from rich import pretty +>>> pretty.install() +``` + +![REPL](https://github.com/willmcgugan/rich/raw/master/imgs/repl.png) + +## ์ฝ˜์†” ์‚ฌ์šฉํ•˜๊ธฐ + +rich ํ„ฐ๋ฏธ๋„์„ ๋”์šฑ ์ž˜ ํ™œ์šฉํ•˜๋ ค๋ฉด, import ํ•œ ๋’ค [Console](https://rich.readthedocs.io/en/latest/reference/console.html#rich.console.Console) ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”. + +```python +from rich.console import Console + +console = Console() +``` + +์ฝ˜์†” ๊ฐ์ฒด์—๋Š” `print` ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š”๋ฐ, ๋‚ด๋ถ€์ ์œผ๋กœ ๋‚ด์žฅ `print` ํ•จ์ˆ˜์™€ ์œ ์‚ฌํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +```python +console.print("Hello", "World!") +``` + +์˜ˆ์ƒ๋Œ€๋กœ `"Hello World!"`์ด ํ„ฐ๋ฏธ๋„์— ์ถœ๋ ฅ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด์žฅ `print` ํ•จ์ˆ˜์™€ ๋‹ฌ๋ฆฌ, Rich๋Š” ํ„ฐ๋ฏธ๋„ ํญ์— ๋งž์ถฐ ์ž๋™ ์ค„๋ฐ”๊ฟˆ(word-wrap)์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์— ์œ ์˜ํ•˜์„ธ์š”. + +์ถœ๋ ฅ์— ์ƒ‰๊น”๊ณผ ์Šคํƒ€์ผ์„ ์ž…ํžˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋ช‡๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. `style` ํ‚ค์›Œ๋“œ ์ „๋‹ฌ์ธ์ž๋ฅผ ์ถ”๊ฐ€ํ•ด ์ „์ฒด ์ถœ๋ ฅ์— ๋Œ€ํ•ด ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + +```python +console.print("Hello", "World!", style="bold red") +``` + +๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค: + +![Hello World](https://github.com/willmcgugan/rich/raw/master/imgs/hello_world.png) + +ํ…์ŠคํŠธ ํ•œ ์ค„์„ ํ•œ ๋ฒˆ์— ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์Šต๋‹ˆ๋‹ค. ๋”์šฑ ์„ธ์„ธํ•˜๊ฒŒ ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด, Rich๋Š” [bbcode](https://en.wikipedia.org/wiki/BBCode)์™€ ๊ตฌ๋ฌธ์ด ๋น„์Šทํ•œ ๋ณ„๋„์˜ ๋งˆํฌ์—…์„ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. + +```python +console.print("Where there is a [bold cyan]Will[/bold cyan] there [u]is[/u] a [i]way[/i].") +``` + +![Console Markup](https://github.com/willmcgugan/rich/raw/master/imgs/where_there_is_a_will.png) + +Console ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•ด ์ ์€ ๋…ธ๋ ฅ์œผ๋กœ ๋ณต์žกํ•œ ์ถœ๋ ฅ์„ ์†์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ [Console API](https://rich.readthedocs.io/en/latest/console.html) ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”. + +## Rich Inspect + +Rich๋Š” class๋‚˜ instance, builtin ๊ฐ™์€ ํŒŒ์ด์ฌ ๊ฐ์ฒด์˜ ๋ ˆํฌํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” [inspect](https://rich.readthedocs.io/en/latest/reference/init.html?highlight=inspect#rich.inspect) ํ•จ์ˆ˜๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค + +```python +>>> my_list = ["foo", "bar"] +>>> from rich import inspect +>>> inspect(my_list, methods=True) +``` + +![Log](https://github.com/willmcgugan/rich/raw/master/imgs/inspect.png) + +์ž์„ธํ•œ ๋‚ด์šฉ์€ [inspect docs](https://rich.readthedocs.io/en/latest/reference/init.html#rich.inspect) ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”. + +# Rich Library + +Rich๋Š” CLI์—์„œ ์šฐ์•„ํ•˜๊ฒŒ ์ถœ๋ ฅํ•˜๊ฑฐ๋‚˜ ์ฝ”๋“œ ๋””๋ฒ„๊น…์„ ๋•๋„๋ก ๋‹ค์–‘ํ•œ ๋นŒํŠธ์ธ _๋ Œ๋”๋ง์„_ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +์ž์„ธํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•˜๋ ค๋ฉด ์ œ๋ชฉ์„ ๋ˆŒ๋Ÿฌ์ฃผ์„ธ์š”: + +
+Log + +Console ๊ฐ์ฒด๋Š” `print()`์™€ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์œ ์‚ฌํ•œ `log()` ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. `Log()`๋Š” ํ˜ธ์ถœ์ด ์ด๋ฃจ์–ด์ง„ ํŒŒ์ผ๊ณผ ๋ผ์ธ, ํ˜„์žฌ ์‹œ๊ฐ„๋„ ๊ฐ™์ด ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ Rich๋Š” ํŒŒ์ด์ฌ ๊ตฌ์กฐ์ฒด์™€ repr string์— ๋Œ€ํ•ด ์‹ ํƒ์Šค ํ•˜์ด๋ผ์ดํŒ…์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‹น์‹ ์ด collection(์˜ˆ๋ฅผ ๋“ค์–ด dict๋‚˜ list)์„ ๋กœ๊น…ํ•œ๋‹ค๋ฉด, Rich๋Š” ํ‘œํ˜„ ๊ฐ€๋Šฅํ•œ ๊ณต๊ฐ„์— ๋งž์ถฐ ์˜ˆ์˜๊ฒŒ ์ถœ๋ ฅํ•ด์ค๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ๋“ค์— ๋Œ€ํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค: + +```python +from rich.console import Console +console = Console() + +test_data = [ + {"jsonrpc": "2.0", "method": "sum", "params": [None, 1, 2, 4, False, True], "id": "1",}, + {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}, + {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": "2"}, +] + +def test_log(): + enabled = False + context = { + "foo": "bar", + } + movies = ["Deadpool", "Rise of the Skywalker"] + console.log("Hello from", console, "!") + console.log(test_data, log_locals=True) + + +test_log() +``` + +์œ„ ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + +![Log](https://github.com/willmcgugan/rich/raw/master/imgs/log.png) + +`log_locals` ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด log ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ ๊ณณ์˜ ๋กœ์ปฌ ๋ณ€์ˆ˜๋“ค์„ ํ‘œ๋กœ ๋ณด์—ฌ์ค€๋‹ค๋Š” ๊ฒƒ๋„ ์•Œ์•„๋‘์„ธ์š”. + +๋กœ๊ทธ ๋ฉ”์„œ๋“œ๋Š” ์„œ๋ฒ„์ฒ˜๋Ÿผ ์˜ค๋žซ๋™์•ˆ ์‹คํ–‰๋˜๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ„ฐ๋ฏธ๋„๋กœ ๋กœ๊น…ํ• ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋””๋ฒ„๊น… ํ•  ๋•Œ๋„ ๋งค์šฐ ์ข‹์Šต๋‹ˆ๋‹ค. + +
+
+Logging Handler + +๋˜ํ•œ ๋‚ด์žฅ๋œ [Handler class](https://rich.readthedocs.io/en/latest/logging.html)๋ฅผ ์‚ฌ์šฉํ•ด ํŒŒ์ด์ฌ์˜ ๋กœ๊น… ๋ชจ๋“ˆ์˜ ์ถœ๋ ฅ์„ ํ˜•ํƒœ๋ฅผ ๊พธ๋ฏธ๊ฑฐ๋‚˜ ์ƒ‰์„ ์ž…ํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +![Logging](https://github.com/willmcgugan/rich/raw/master/imgs/logging.png) + +
+ +
+Emoji(์ด๋ชจ์ง€) + +์ฝ˜์†” ์ถœ๋ ฅ์— ์ด๋ชจ์ง€๋ฅผ ๋„ฃ์œผ๋ ค๋ฉด ๋‘ ์ฝœ๋ก (:) ์‚ฌ์ด์— ์ด๋ชจ์ง€ ์ด๋ฆ„์„ ๋„ฃ์–ด์ฃผ์„ธ์š”. ๋‹ค์Œ์€ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +```python +>>> console.print(":smiley: :vampire: :pile_of_poo: :thumbs_up: :raccoon:") +๐Ÿ˜ƒ ๐Ÿง› ๐Ÿ’ฉ ๐Ÿ‘ ๐Ÿฆ +``` + +๋ถ€๋”” ์ด ๊ธฐ๋Šฅ์„ ์ž˜ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”. + +
+ +
+Tables(ํ‘œ) + +Rich๋Š” ์œ ๋‹ˆ์ฝ”๋“œ ๋ฐ•์Šค ๋ฌธ์ž์™€ ํ•จ๊ป˜ [ํ‘œ](https://rich.readthedocs.io/en/latest/tables.html)๋ฅผ ์ž์œ ๋กญ๊ฒŒ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ์ž๋ฆฌ, ์Šคํƒ€์ผ, ์…€ ์ •๋ ฌ ๋“ฑ์„ ์ •๋ง ๋‹ค์–‘ํ•˜๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +![table movie](https://github.com/willmcgugan/rich/raw/master/imgs/table_movie.gif) + +์œ„์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ example ๋””๋ ‰ํ† ๋ฆฌ์˜ [table_movie.py](https://github.com/willmcgugan/rich/blob/master/examples/table_movie.py)๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +๋” ๊ฐ„๋‹จํ•œ ํ‘œ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +```python +from rich.console import Console +from rich.table import Table + +console = Console() + +table = Table(show_header=True, header_style="bold magenta") +table.add_column("Date", style="dim", width=12) +table.add_column("Title") +table.add_column("Production Budget", justify="right") +table.add_column("Box Office", justify="right") +table.add_row( + "Dev 20, 2019", "Star Wars: The Rise of Skywalker", "$275,000,000", "$375,126,118" +) +table.add_row( + "May 25, 2018", + "[red]Solo[/red]: A Star Wars Story", + "$275,000,000", + "$393,151,347", +) +table.add_row( + "Dec 15, 2017", + "Star Wars Ep. VIII: The Last Jedi", + "$262,000,000", + "[bold]$1,332,539,889[/bold]", +) + +console.print(table) +``` + +์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค: + +![table](https://github.com/willmcgugan/rich/raw/master/imgs/table.png) + +์ฝ˜์†” ์ถœ๋ ฅ์€ `print()`๋‚˜ `log()`์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ Œ๋”๋ง ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์ฃผ์˜ํ•˜์„ธ์š”. ์‚ฌ์‹ค, Rich๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ๋ฌด์—‡์ด๋“  headers / rows (์‹ฌ์ง€์–ด ๋‹ค๋ฅธ ํ‘œ๋“ค๋„)์— ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +`Table` ํด๋ž˜์Šค๋Š” ํ„ฐ๋ฏธ๋„์˜ ํญ์— ๋งž์ถฐ ํ•„์š”ํ•œ ๋งŒํผ ์ค„์„ ๋‚ด๋ฆฌ๊ณ  ์—ด ๊ธธ์ด๋ฅผ ์Šค์Šค๋กœ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ํ‘œ๋ณด๋‹ค ์ž‘์€ ํ„ฐ๋ฏธ๋„์—์„œ ๋งŒ๋“ค์–ด์ง„ ํ‘œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค: + +![table2](https://github.com/willmcgugan/rich/raw/master/imgs/table2.png) + +
+ +
+Progress Bars(์ง„ํ–‰ ๋ฐ”) + +Rich๋Š” ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…๋“ค์„ ์œ„ํ•ด ๊นœ๋นก์ž„ ์—†๋Š” [์ง„ํ–‰](https://rich.readthedocs.io/en/latest/progress.html) ๋ฐ”๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ์„ ์œ„ํ•ด์„  ์•„๋ฌด sequence๋‚˜ `track` ํ•จ์ˆ˜๋กœ ๊ฐ์‹ธ๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜๋ณตํ•ด์ฃผ์„ธ์š”. ๋‹ค์Œ์€ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +```python +from rich.progress import track + +for step in track(range(100)): + do_step(step) +``` + +์—ฌ๋Ÿฌ๊ฐœ์˜ ์ง„ํ–‰ ๋ฐ”๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๋„ ์–ด๋ ต์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ๊ณต์‹๋ฌธ์„œ์—์„œ ๋”ฐ์˜จ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค: + +![progress](https://github.com/willmcgugan/rich/raw/master/imgs/progress.gif) + +์นผ๋Ÿผ๋“ค์€ ์ˆ˜์ •ํ•ด ์›ํ•˜๋Š” ์„ธ๋ถ€์ •๋ณด๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์œผ๋กœ ๋‚ด์žฅ๋œ ์นผ๋Ÿผ๋“ค์€ ์™„๋ฃŒ ํผ์„ผํ‹ฐ์ง€, ํŒŒ์ผ ํฌ๊ธฐ, ํŒŒ์ผ ์†๋„, ๋‚จ์€ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ๋‹ค์šด๋กœ๋“œ ์ง„ํ–‰์„ ๋ณด์—ฌ์ฃผ๋Š” ๋‹ค๋ฅธ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +![progress](https://github.com/willmcgugan/rich/raw/master/imgs/downloader.gif) + +์ง์ ‘ ํ•ด๋ณด์‹œ๋ ค๋ฉด, ์ง„ํ–‰ ๋ฐ”์™€ ํ•จ๊ป˜ ์—ฌ๋Ÿฌ๊ฐœ์˜ URL๋“ค์„ ๋™์‹œ์— ๋‹ค์šด๋กœ๋“œ ๋ฐ›๋Š” ์˜ˆ์ œ์ธ [examples/downloader.py](https://github.com/willmcgugan/rich/blob/master/examples/downloader.py)๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”. + +
+ +
+Status(์ƒํƒœ) + +์ง„ํ–‰ ์ƒํ™ฉ์„ ๊ณ„์‚ฐํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฒฝ์šฐ, [์ƒํƒœ](https://rich.readthedocs.io/en/latest/reference/console.html#rich.console.Console.status) ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” '์Šคํ”ผ๋„ˆ' ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ๋ฉ”์„ธ์ง€๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ๋‹น์‹ ์ด ์ฝ˜์†”์„ ์ •์ƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +```python +from time import sleep +from rich.console import Console + +console = Console() +tasks = [f"task {n}" for n in range(1, 11)] + +with console.status("[bold green]Working on tasks...") as status: + while tasks: + task = tasks.pop(0) + sleep(1) + console.log(f"{task} complete") +``` + +์ด ์˜ˆ์ œ๋Š” ํ„ฐ๋ฏธ๋„์— ์•„๋ž˜์™€ ๊ฐ™์ด ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. + +![status](https://github.com/willmcgugan/rich/raw/master/imgs/status.gif) + +์Šคํ”ผ๋„ˆ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ [cli-spinners](https://www.npmjs.com/package/cli-spinners)์—์„œ ๋นŒ๋ ค์™”์Šต๋‹ˆ๋‹ค. `spinner` ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ ํƒํ•ด์„œ ํŠน์ • ์Šคํ”ผ๋„ˆ๋ฅผ ์„ ํƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ๊ฐ’์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋Š” ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: + +``` +python -m rich.spinner +``` + +์œ„์˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค: + +![spinners](https://github.com/willmcgugan/rich/raw/master/imgs/spinners.gif) + +
+ +
+Tree(ํŠธ๋ฆฌ) + +Rich๋Š” ๊ฐ€์ด๋“œ๋ผ์ธ๊ณผ ํ•จ๊ป˜ [ํŠธ๋ฆฌ](https://rich.readthedocs.io/en/latest/tree.html)๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ ๊ตฌ์กฐ๋‚˜, ๊ณ„์ธต์  ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š”๋ฐ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. + +ํŠธ๋ฆฌ์˜ ๋ผ๋ฒจ์€ ๊ฐ„๋‹จํ•œ ํ…์ŠคํŠธ๋‚˜ Rich๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ๋ชจ๋“ ์ง€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์˜ ์˜ˆ์‹œ๋ฅผ ๋”ฐ๋ผํ•ด๋ณด์„ธ์š”: + +``` +python -m rich.tree +``` + +์ด๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค: + +![markdown](https://github.com/willmcgugan/rich/raw/master/imgs/tree.png) + +๋ฆฌ๋ˆ…์Šค์˜ `tree` ๋ช…๋ น์–ด์ฒ˜๋Ÿผ ์•„๋ฌด ๋””๋ ‰ํ† ๋ฆฌ์˜ ํŠธ๋ฆฌ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์Šคํฌ๋ฆฝํŠธ ์˜ˆ์ œ๋ฅผ ๋ณด์‹œ๋ ค๋ฉด [tree.py](https://github.com/willmcgugan/rich/blob/master/examples/tree.py)๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”. + +
+ +
+Columns(์นผ๋Ÿผ) + +Rich๋Š” ๋‚ด์šฉ์„ ๊ฐ™๊ฑฐ๋‚˜ ์ ์ ˆํ•œ ํญ์œผ๋กœ ๊น”๋”ํ•˜๊ฒŒ [์นผ๋Ÿผ](https://rich.readthedocs.io/en/latest/columns.html)์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์ œ๋Š” ์ข…๋ ฌ๋กœ ๋””๋ ‰ํ† ๋ฆฌ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” (MacOS / Linux)์˜ `ls` ๋ช…๋ น์–ด์˜ ๊ธฐ๋ณธ์ ์ธ ํด๋ก ์ž…๋‹ˆ๋‹ค: + +```python +import os +import sys + +from rich import print +from rich.columns import Columns + +directory = os.listdir(sys.argv[1]) +print(Columns(directory)) +``` + +์•„๋ž˜ ์Šคํฌ๋ฆฐ์ƒท์€ API์—์„œ ๋ฝ‘์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ข…๋ ฌ๋กœ ํ‘œํ˜„ํ•˜๋Š” [์นผ๋Ÿผ ์˜ˆ์ œ](https://github.com/willmcgugan/rich/blob/master/examples/columns.py)์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค: + +![columns](https://github.com/willmcgugan/rich/raw/master/imgs/columns.png) + +
+ +
+Markdown(๋งˆํฌ๋‹ค์šด) + +Rich๋Š” [๋งˆํฌ๋‹ค์šด](https://rich.readthedocs.io/en/latest/markdown.html)์„ ํ‘œํ˜„ํ•˜๊ฑฐ๋‚˜ ํ˜•ํƒœ๋ฅผ ํ„ฐ๋ฏธ๋„์— ๋งž์ถ”์–ด ์ ์ ˆํžˆ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +๋งˆํฌ๋‹ค์šด์„ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” `Markdown` ํด๋ž˜์Šค๋ฅผ importํ•˜๊ณ  ๋งˆํฌ๋‹ค์šด์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ๋ฌธ์ž์—ด์„ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”. ๋‹ค์Œ์€ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค: + +```python +from rich.console import Console +from rich.markdown import Markdown + +console = Console() +with open("README.md") as readme: + markdown = Markdown(readme.read()) +console.print(markdown) +``` + +์œ„ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค: + +![markdown](https://github.com/willmcgugan/rich/raw/master/imgs/markdown.png) + +
+ +
+Syntax Highlighting(๊ตฌ๋ฌธ ๊ฐ•์กฐ) + +Rich๋Š” [๊ตฌ๋ฌธ ๊ฐ•์กฐ](https://rich.readthedocs.io/en/latest/syntax.html) ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด [pygments](https://pygments.org/) ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ๋ฒ•์€ ๋งˆํฌ๋‹ค์šด๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. `Syntax` ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ฝ˜์†”์— ์ถœ๋ ฅํ•˜์„ธ์š”. ์˜ˆ์ œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: + +```python +from rich.console import Console +from rich.syntax import Syntax + +my_code = ''' +def iter_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]: + """Iterate and generate a tuple with a flag for first and last value.""" + iter_values = iter(values) + try: + previous_value = next(iter_values) + except StopIteration: + return + first = True + for value in iter_values: + yield first, False, previous_value + first = False + previous_value = value + yield first, True, previous_value +''' +syntax = Syntax(my_code, "python", theme="monokai", line_numbers=True) +console = Console() +console.print(syntax) +``` + +์œ„ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค: + +![syntax](https://github.com/willmcgugan/rich/raw/master/imgs/syntax.png) + +
+ +
+Tracebacks + +Rich๋Š” [์˜ˆ์œ tracebacks](https://rich.readthedocs.io/en/latest/traceback.html)์„ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ฝ๊ธฐ๋„ ๋” ์‰ฝ๊ณ  ์ผ๋ฐ˜์ ์ธ ํŒŒ์ด์ฌ tracebacks ๋ณด๋‹ค ๋” ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. uncaught exceptions๊ฐ€ Rich๋กœ ์ถœ๋ ฅ๋˜๋„๋ก Rich๋ฅผ ๊ธฐ๋ณธ Traceback ํ•ธ๋“ค๋Ÿฌ๋กœ ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +OSX์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค (๋ฆฌ๋ˆ…์Šค๋„ ์œ ์‚ฌํ•จ): + +![traceback](https://github.com/willmcgugan/rich/raw/master/imgs/traceback.png) + +
+ +๋ชจ๋“  Rich๋กœ ํ‘œํ˜„ ๊ฐ€๋Šฅํ•œ ๊ฒƒ๋“ค์€ [Console Protocol](https://rich.readthedocs.io/en/latest/protocol.html)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•ด์„œ ์ž์‹ ์˜ Rich ์ปจํ…์ธ ๋ฅผ ๋ Œ๋”๋งํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. + +# ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ๋ฅผ ์œ„ํ•œ Rich + +Tidelift ๊ตฌ๋…์˜ ์ผํ™˜์œผ๋กœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. + +Rich๋ฅผ ํฌํ•จํ•œ ์ˆ˜์ฒœ๊ฐ€์ง€ ๋‹ค๋ฅธ ํŒจํ‚ค์ง€๋“ค์˜ ๋ฉ”์ธํ…Œ์ด๋„ˆ๋“ค์€ ๋‹น์‹ ์ด ์•ฑ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์˜คํ”ˆ์†Œ์Šค ํŒจํ‚ค์ง€์˜ ์ƒ์—…์ ์ธ ์ง€์›๊ณผ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์œ„ํ•ด Tidelift์™€ ํ•จ๊ป˜ ์ผํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ์‚ฌ์šฉํ•˜๋Š” ํŒจํ‚ค์ง€์˜ ๋ฉ”์ธํ…Œ์ด๋„ˆ์—๊ฒŒ ๋น„์šฉ์„ ์ง€๋ถˆํ•˜๋Š” ๋Œ€์‹  ์‹œ๊ฐ„์„ ์ ˆ์•ฝํ•˜๊ณ , ๋ฆฌ์Šคํฌ๋ฅผ ์ค„์ด๊ณ , ์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. [๋” ์ž์„ธํ•œ ์ •๋ณด๋Š” ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ๋ฐ”๋ž๋‹ˆ๋‹ค.](https://tidelift.com/subscription/pkg/pypi-rich?utm_source=pypi-rich&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + +# Rich๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋“ค + +Rich๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ช‡๊ฐ€์ง€ ํ”„๋กœ์ ํŠธ๋“ค์ž…๋‹ˆ๋‹ค: + +- [BrancoLab/BrainRender](https://github.com/BrancoLab/BrainRender) + ์‹ ๊ฒฝํ•ด๋ถ€ํ•™ ๋ฐ์ดํ„ฐ์˜ 3์ฐจ์› ์‹œ๊ฐํ™”๋ฅผ ์œ„ํ•œ ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€ +- [Ciphey/Ciphey](https://github.com/Ciphey/Ciphey) + ์ž๋™ ์•”ํ˜ธํ•ด๋… ํˆด +- [emeryberger/scalene](https://github.com/emeryberger/scalene) + ํŒŒ์ด์ฌ์„ ์œ„ํ•œ ๊ณ ์„ฑ๋Šฅ, ๋†’์€ ์ •๋ฐ€๋„์˜ CPU / Memory ํ”„๋กœํŒŒ์ผ๋Ÿฌ +- [hedythedev/StarCli](https://github.com/hedythedev/starcli) + ๋‹น์‹ ์˜ ์ปค๋งจ๋“œ๋ผ์ธ์—์„œ GitHub ํŠธ๋ Œ๋”ฉ ํ”„๋กœ์ ํŠธ๋“ค์„ ๊ฒ€์ƒ‰ํ•ด๋ณด์„ธ์š” +- [intel/cve-bin-tool](https://github.com/intel/cve-bin-tool) + ์ด ํˆด์€ ์—ฌ๋Ÿฌ ๊ณตํ†ต์ ์ด๊ณ  ์ทจ์•ฝํ•œ ์ปดํฌ๋„จํŠธ๋“ค(openssl, libpng, libxml2, expat ๊ณผ ๋ช‡๊ฐ€์ง€ ๋”)์„ ์Šค์บ”ํ•ด, ์ด๋ฏธ ์•Œ๋ ค์ง„ ์ทจ์•ฝ์ ์„ ๊ฐ€์ง„ ์ผ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋‹น์‹ ์˜ ์‹œ์Šคํ…œ์— ์žˆ๋Š”์ง€ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. +- [nf-core/tools](https://github.com/nf-core/tools) + nf-core ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ์œ„ํ•œ ๋„์šฐ๋ฏธ ๋„๊ตฌ๋ฅผ ํฌํ•จํ•œ ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€. +- [cansarigol/pdbr](https://github.com/cansarigol/pdbr) + ๊ฐœ์„ ๋œ ๋””๋ฒ„๊น…์„ ์œ„ํ•œ pdb + Rich ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ +- [plant99/felicette](https://github.com/plant99/felicette) + ๋”๋ฏธ ์œ„์„ฑ ์ด๋ฏธ์ง€ +- [seleniumbase/SeleniumBase](https://github.com/seleniumbase/SeleniumBase) + Selenium & pytest๋กœ 10๋ฐฐ ๋” ๋น ๋ฅด๊ฒŒ ์ž๋™ํ™” & ํ…Œ์ŠคํŠธํ•˜์„ธ์š”. ๋ฐฐํ„ฐ๋ฆฌ๋„ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. +- [smacke/ffsubsync](https://github.com/smacke/ffsubsync) + ์ž๋™์œผ๋กœ ์ž๋ง‰๊ณผ ์˜์ƒ์˜ ์‹ฑํฌ๋ฅผ ๋งž์ถ”์„ธ์š”. +- [tryolabs/norfair](https://github.com/tryolabs/norfair) + ๋ชจ๋“  ํƒ์ง€๋œ ๊ฒƒ์— ์‹ค์‹œ๊ฐ„์œผ๋กœ 2D ์˜ค๋ธŒ์ ํŠธ ํŠธ๋ž˜ํ‚น์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒฝ๋Ÿ‰ํ™”๋œ ํŒŒ์ด์ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ. +- [ansible/ansible-lint](https://github.com/ansible/ansible-lint) + Ansible-lint๊ฐ€ playbooks๋ฅผ ํ™•์ธํ•ด ์ž ์žฌ์ ์œผ๋กœ ๊ฐœ์„ ๋  ์ˆ˜ ์žˆ๋Š” practices๋‚˜ ๋™์ž‘์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. +- [ansible-community/molecule](https://github.com/ansible-community/molecule) + Ansible Molecule์˜ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ +- +[Many more](https://github.com/willmcgugan/rich/network/dependents)! + + \ No newline at end of file diff --git a/README.md b/README.md index e6a25896d..880d2544d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Downloads](https://pepy.tech/badge/rich/month)](https://pepy.tech/project/rich) [![PyPI version](https://badge.fury.io/py/rich.svg)](https://badge.fury.io/py/rich) [![codecov](https://codecov.io/gh/willmcgugan/rich/branch/master/graph/badge.svg)](https://codecov.io/gh/willmcgugan/rich) [![Rich blog](https://img.shields.io/badge/blog-rich%20news-yellowgreen)](https://www.willmcgugan.com/tag/rich/) @@ -5,7 +6,7 @@ ![Logo](https://github.com/willmcgugan/rich/raw/master/imgs/logo.svg) -[ไธญๆ–‡ readme](https://github.com/willmcgugan/rich/blob/master/README.cn.md) โ€ข [Lengua espaรฑola readme](https://github.com/willmcgugan/rich/blob/master/README.es.md) โ€ข [Deutsche readme](https://github.com/willmcgugan/rich/blob/master/README.de.md) โ€ข [Lรคs pรฅ svenska](https://github.com/willmcgugan/rich/blob/master/README.sv.md) โ€ข [ๆ—ฅๆœฌ่ชž readme](https://github.com/willmcgugan/rich/blob/master/README.ja.md) +[ไธญๆ–‡ readme](https://github.com/willmcgugan/rich/blob/master/README.cn.md) โ€ข [Lengua espaรฑola readme](https://github.com/willmcgugan/rich/blob/master/README.es.md) โ€ข [Deutsche readme](https://github.com/willmcgugan/rich/blob/master/README.de.md) โ€ข [Lรคs pรฅ svenska](https://github.com/willmcgugan/rich/blob/master/README.sv.md) โ€ข [ๆ—ฅๆœฌ่ชž readme](https://github.com/willmcgugan/rich/blob/master/README.ja.md) โ€ข [ํ•œ๊ตญ์–ด readme](https://github.com/willmcgugan/rich/blob/master/README.kr.md) Rich is a Python library for _rich_ text and beautiful formatting in the terminal. @@ -439,3 +440,5 @@ Here are a few projects using Rich: - [ansible/ansible-lint](https://github.com/ansible/ansible-lint) Ansible-lint checks playbooks for practices and behaviour that could potentially be improved - [ansible-community/molecule](https://github.com/ansible-community/molecule) Ansible Molecule testing framework - +[Many more](https://github.com/willmcgugan/rich/network/dependents)! + + diff --git a/README.sv.md b/README.sv.md index d4347502e..49ae56f76 100644 --- a/README.sv.md +++ b/README.sv.md @@ -1,3 +1,4 @@ +[![Downloads](https://pepy.tech/badge/rich/month)](https://pepy.tech/project/rich) [![PyPI version](https://badge.fury.io/py/rich.svg)](https://badge.fury.io/py/rich) [![codecov](https://codecov.io/gh/willmcgugan/rich/branch/master/graph/badge.svg)](https://codecov.io/gh/willmcgugan/rich) [![Rich blog](https://img.shields.io/badge/blog-rich%20news-yellowgreen)](https://www.willmcgugan.com/tag/rich/) diff --git a/docs/requirements.txt b/docs/requirements.txt index 1d2bbca8e..629c36166 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ alabaster==0.7.12 -Sphinx==3.5.3 -sphinx-rtd-theme==0.5.1 +Sphinx==4.0.1 +sphinx-rtd-theme==0.5.2 sphinx-copybutton==0.3.1 diff --git a/docs/source/padding.rst b/docs/source/padding.rst index a72dcbf9d..0c0225b37 100644 --- a/docs/source/padding.rst +++ b/docs/source/padding.rst @@ -17,7 +17,7 @@ For example, the following displays 2 blank lines above and below the text, and test = Padding("Hello", (2, 4)) print(test) -The Padding class can also accept a ``style`` argument which applies a style to the padding and contents, and an ``expand`` switch which can be set to False to prevent the padding from extending to the full with of the terminal. Here's an example which demonstrates both these arguments:: +The Padding class can also accept a ``style`` argument which applies a style to the padding and contents, and an ``expand`` switch which can be set to False to prevent the padding from extending to the full width of the terminal. Here's an example which demonstrates both these arguments:: from rich import print from rich.padding import Padding diff --git a/examples/attrs.py b/examples/attrs.py new file mode 100644 index 000000000..35746738a --- /dev/null +++ b/examples/attrs.py @@ -0,0 +1,53 @@ +from typing import List + +try: + import attr +except ImportError: + print("This example requires attrs library") + print("pip install attrs") + raise + + +@attr.define +class Point3D: + x: float + y: float + z: float = 0 + + +@attr.define +class Triangle: + point1: Point3D + point2: Point3D + point3: Point3D + + +@attr.define +class Model: + name: str + triangles: List[Triangle] = attr.Factory(list) + + +if __name__ == "__main__": + model = Model( + name="Alien#1", + triangles=[ + Triangle( + Point3D(x=20, y=50), + Point3D(x=50, y=15, z=-45.34), + Point3D(3.1426, 83.2323, -16), + ) + ], + ) + + from rich.console import Console + from rich.pretty import Pretty + from rich.table import Column, Table + from rich.text import Text + + console = Console() + + table = Table("attrs *with* Rich", Column(Text.from_markup("attrs *without* Rich"))) + + table.add_row(Pretty(model), repr(model)) + console.print(table) diff --git a/examples/table_movie.py b/examples/table_movie.py index eaa35cdb1..2f05c9e82 100644 --- a/examples/table_movie.py +++ b/examples/table_movie.py @@ -111,7 +111,7 @@ def beat(length: int = 1) -> None: with beat(10): table.show_footer = True - table_width = Measurement.get(console, table, console.width).maximum + table_width = console.measure(table).maximum with beat(10): table.columns[2].justify = "right" @@ -175,7 +175,7 @@ def beat(length: int = 1) -> None: with beat(10): table.pad_edge = False - original_width = Measurement.get(console, table).maximum + original_width = console.measure(table).maximum for width in range(original_width, console.width, 2): with beat(1): diff --git a/examples/top_lite_simulator.py b/examples/top_lite_simulator.py index e4a10f581..4fecaaa8b 100644 --- a/examples/top_lite_simulator.py +++ b/examples/top_lite_simulator.py @@ -1,7 +1,7 @@ """Lite simulation of the top linux command.""" - import datetime import random +import sys import time from dataclasses import dataclass @@ -9,7 +9,11 @@ from rich.console import Console from rich.live import Live from rich.table import Table -from typing_extensions import Literal + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal @dataclass diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 000000000..c61bcf0ac --- /dev/null +++ b/mypy.ini @@ -0,0 +1,16 @@ +[mypy] + +[mypy-pygments.*] +ignore_missing_imports = True + +[mypy-IPython.*] +ignore_missing_imports = True + +[mypy-commonmark.*] +ignore_missing_imports = True + +[mypy-colorama.*] +ignore_missing_imports = True + +[mypy-ipywidgets.*] +ignore_missing_imports = True diff --git a/poetry.lock b/poetry.lock index 8ba16af9d..1f14c88bc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -8,12 +8,37 @@ python-versions = "*" [[package]] name = "appnope" -version = "0.1.0" -description = "Disable App Nap on OS X 10.9" +version = "0.1.2" +description = "Disable App Nap on macOS >= 10.9" category = "main" optional = true python-versions = "*" +[[package]] +name = "argon2-cffi" +version = "20.1.0" +description = "The secure Argon2 password hashing algorithm." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +cffi = ">=1.0.0" +six = "*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"] +docs = ["sphinx"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] + +[[package]] +name = "async-generator" +version = "1.10" +description = "Async generators and context managers for Python 3.5+" +category = "main" +optional = true +python-versions = ">=3.5" + [[package]] name = "atomicwrites" version = "1.4.0" @@ -24,17 +49,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "19.3.0" +version = "21.2.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] -dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] [[package]] name = "backcall" @@ -69,7 +94,7 @@ d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] name = "bleach" -version = "3.1.5" +version = "3.3.0" description = "An easy safelist-based HTML-sanitizing tool." category = "main" optional = true @@ -80,6 +105,17 @@ packaging = "*" six = ">=1.9.0" webencodings = "*" +[[package]] +name = "cffi" +version = "1.14.5" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +pycparser = "*" + [[package]] name = "click" version = "7.1.2" @@ -109,7 +145,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" -version = "5.3" +version = "5.5" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -128,15 +164,15 @@ python-versions = ">=3.6, <3.7" [[package]] name = "decorator" -version = "4.4.2" +version = "5.0.7" description = "Decorators for Humans" category = "main" optional = true -python-versions = ">=2.6, !=3.0.*, !=3.1.*" +python-versions = ">=3.5" [[package]] name = "defusedxml" -version = "0.6.0" +version = "0.7.1" description = "XML bomb protection for Python stdlib modules" category = "main" optional = true @@ -152,18 +188,19 @@ python-versions = ">=2.7" [[package]] name = "importlib-metadata" -version = "1.7.0" +version = "4.0.1" description = "Read metadata from Python packages" category = "main" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -175,7 +212,7 @@ python-versions = "*" [[package]] name = "ipykernel" -version = "5.3.2" +version = "5.5.4" description = "IPython Kernel for Jupyter" category = "main" optional = true @@ -189,7 +226,7 @@ tornado = ">=4.2" traitlets = ">=4.1.0" [package.extras] -test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose"] +test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose", "jedi (<=0.17.2)"] [[package]] name = "ipython" @@ -251,22 +288,22 @@ test = ["pytest (>=3.6.0)", "pytest-cov", "mock"] [[package]] name = "jedi" -version = "0.17.1" +version = "0.18.0" description = "An autocompletion tool for Python that can be used for text editors." category = "main" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] -parso = ">=0.7.0,<0.8.0" +parso = ">=0.8.0,<0.9.0" [package.extras] -qa = ["flake8 (==3.7.9)"] -testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] [[package]] name = "jinja2" -version = "2.11.2" +version = "2.11.3" description = "A very fast and expressive template engine." category = "main" optional = true @@ -298,7 +335,7 @@ format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator [[package]] name = "jupyter-client" -version = "6.1.5" +version = "6.1.13" description = "Jupyter protocol implementation and client libraries" category = "main" optional = true @@ -306,26 +343,39 @@ python-versions = ">=3.5" [package.dependencies] jupyter-core = ">=4.6.0" +nest-asyncio = ">=1.5" python-dateutil = ">=2.1" pyzmq = ">=13" tornado = ">=4.1" traitlets = "*" [package.extras] -test = ["ipykernel", "ipython", "mock", "pytest"] +doc = ["sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] +test = ["async-generator", "ipykernel", "ipython", "mock", "pytest-asyncio", "pytest-timeout", "pytest", "mypy", "pre-commit", "jedi (<0.18)"] [[package]] name = "jupyter-core" -version = "4.6.3" +version = "4.7.1" description = "Jupyter core package. A base package on which Jupyter projects rely." category = "main" optional = true -python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" +python-versions = ">=3.6" [package.dependencies] pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\""} traitlets = "*" +[[package]] +name = "jupyterlab-pygments" +version = "0.1.2" +description = "Pygments theme using JupyterLab CSS variables" +category = "main" +optional = true +python-versions = "*" + +[package.dependencies] +pygments = ">=2.4.1,<3" + [[package]] name = "jupyterlab-widgets" version = "1.0.0" @@ -374,13 +424,33 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "nbclient" +version = "0.5.1" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +category = "main" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +async-generator = "*" +jupyter-client = ">=6.1.5" +nbformat = ">=5.0" +nest-asyncio = "*" +traitlets = ">=4.2" + +[package.extras] +dev = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] +sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] +test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] + [[package]] name = "nbconvert" -version = "5.6.1" +version = "6.0.7" description = "Converting Jupyter Notebooks" category = "main" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] bleach = "*" @@ -388,23 +458,25 @@ defusedxml = "*" entrypoints = ">=0.2.2" jinja2 = ">=2.4" jupyter-core = "*" +jupyterlab-pygments = "*" mistune = ">=0.8.1,<2" +nbclient = ">=0.5.0,<0.6.0" nbformat = ">=4.4" pandocfilters = ">=1.4.1" -pygments = "*" +pygments = ">=2.4.1" testpath = "*" traitlets = ">=4.2" [package.extras] -all = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "mock"] -docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "jupyter-client (>=5.3.1)"] -execute = ["jupyter-client (>=5.3.1)"] +all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] +docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] serve = ["tornado (>=4.0)"] -test = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "mock"] +test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)"] +webpdf = ["pyppeteer (==0.2.2)"] [[package]] name = "nbformat" -version = "5.0.7" +version = "5.1.3" description = "The Jupyter Notebook format" category = "main" optional = true @@ -417,17 +489,27 @@ jupyter-core = "*" traitlets = ">=4.1" [package.extras] -test = ["pytest", "pytest-cov", "testpath"] +fast = ["fastjsonschema"] +test = ["check-manifest", "fastjsonschema", "testpath", "pytest", "pytest-cov"] + +[[package]] +name = "nest-asyncio" +version = "1.5.1" +description = "Patch asyncio to allow nested event loops" +category = "main" +optional = true +python-versions = ">=3.5" [[package]] name = "notebook" -version = "6.0.3" +version = "6.3.0" description = "A web-based notebook environment for interactive computing" category = "main" optional = true -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] +argon2-cffi = "*" ipykernel = "*" ipython-genutils = "*" jinja2 = "*" @@ -437,17 +519,19 @@ nbconvert = "*" nbformat = "*" prometheus-client = "*" pyzmq = ">=17" -Send2Trash = "*" -terminado = ">=0.8.1" -tornado = ">=5.0" +Send2Trash = ">=1.5.0" +terminado = ">=0.8.3" +tornado = ">=6.1" traitlets = ">=4.2.1" [package.extras] -test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "nose-exclude"] +docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt", "sphinx-rtd-theme"] +json-logging = ["json-logging"] +test = ["pytest", "coverage", "requests", "nbval", "selenium", "pytest-cov", "requests-unixsocket"] [[package]] name = "packaging" -version = "20.4" +version = "20.9" description = "Core utilities for Python packages" category = "main" optional = false @@ -455,26 +539,26 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] pyparsing = ">=2.0.2" -six = "*" [[package]] name = "pandocfilters" -version = "1.4.2" +version = "1.4.3" description = "Utilities for writing pandoc filters in python" category = "main" optional = true -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "parso" -version = "0.7.0" +version = "0.8.2" description = "A Python Parser" category = "main" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.extras] -testing = ["docopt", "pytest (>=3.0.7)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] [[package]] name = "pathspec" @@ -519,11 +603,11 @@ dev = ["pre-commit", "tox"] [[package]] name = "prometheus-client" -version = "0.8.0" +version = "0.10.1" description = "Python client for the Prometheus monitoring system." category = "main" optional = true -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] twisted = ["twisted"] @@ -541,7 +625,7 @@ wcwidth = "*" [[package]] name = "ptyprocess" -version = "0.6.0" +version = "0.7.0" description = "Run a subprocess in a pseudo terminal" category = "main" optional = true @@ -549,15 +633,23 @@ python-versions = "*" [[package]] name = "py" -version = "1.9.0" +version = "1.10.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pycparser" +version = "2.20" +description = "C parser in Python" +category = "main" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "pygments" -version = "2.8.1" +version = "2.9.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false @@ -573,18 +665,15 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pyrsistent" -version = "0.16.0" +version = "0.17.3" description = "Persistent/Functional/Immutable data structures" category = "main" optional = true -python-versions = "*" - -[package.dependencies] -six = "*" +python-versions = ">=3.5" [[package]] name = "pytest" -version = "6.2.2" +version = "6.2.4" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -632,7 +721,7 @@ six = ">=1.5" [[package]] name = "pywin32" -version = "228" +version = "300" description = "Python for Window Extensions" category = "main" optional = true @@ -640,23 +729,27 @@ python-versions = "*" [[package]] name = "pywinpty" -version = "0.5.7" -description = "Python bindings for the winpty library" +version = "1.0.1" +description = "Pseudo terminal support for Windows from Python." category = "main" optional = true python-versions = "*" [[package]] name = "pyzmq" -version = "19.0.1" +version = "22.0.3" description = "Python bindings for 0MQ" category = "main" optional = true -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" +python-versions = ">=3.6" + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} +py = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "regex" -version = "2020.11.13" +version = "2021.4.4" description = "Alternative regular expression module, to replace re." category = "dev" optional = false @@ -672,25 +765,28 @@ python-versions = "*" [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "terminado" -version = "0.8.3" -description = "Terminals served to xterm.js using Tornado websockets" +version = "0.9.4" +description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." category = "main" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] ptyprocess = {version = "*", markers = "os_name != \"nt\""} pywinpty = {version = ">=0.5", markers = "os_name == \"nt\""} tornado = ">=4" +[package.extras] +test = ["pytest"] + [[package]] name = "testpath" version = "0.4.4" @@ -712,7 +808,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tornado" -version = "6.0.4" +version = "6.1" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." category = "main" optional = true @@ -736,7 +832,7 @@ test = ["pytest", "mock"] [[package]] name = "typed-ast" -version = "1.4.1" +version = "1.4.3" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false @@ -744,7 +840,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "3.7.4.3" +version = "3.10.0.0" description = "Backported and Experimental Type Hints for Python 3.5+" category = "main" optional = false @@ -779,15 +875,15 @@ notebook = ">=4.4.1" [[package]] name = "zipp" -version = "3.1.0" +version = "3.4.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.6" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools", "func-timeout"] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [extras] jupyter = ["ipywidgets"] @@ -795,7 +891,7 @@ jupyter = ["ipywidgets"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "dd01a0076147c8fb784f6cfd4e100518f0a93ed7d7066c9400e6ce1ef27422c3" +content-hash = "3e70e4d0863ade176fcab5419bdcf39ccd3877a805cfa3a1cabd5e3ce356bc81" [metadata.files] appdirs = [ @@ -803,16 +899,40 @@ appdirs = [ {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] appnope = [ - {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, - {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, + {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, + {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, +] +argon2-cffi = [ + {file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6ea92c980586931a816d61e4faf6c192b4abce89aa767ff6581e6ddc985ed003"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:05a8ac07c7026542377e38389638a8a1e9b78f1cd8439cd7493b39f08dd75fbf"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-win32.whl", hash = "sha256:0bf066bc049332489bb2d75f69216416329d9dc65deee127152caeb16e5ce7d5"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-win_amd64.whl", hash = "sha256:57358570592c46c420300ec94f2ff3b32cbccd10d38bdc12dc6979c4a8484fbc"}, + {file = "argon2_cffi-20.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7d455c802727710e9dfa69b74ccaab04568386ca17b0ad36350b622cd34606fe"}, + {file = "argon2_cffi-20.1.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b160416adc0f012fb1f12588a5e6954889510f82f698e23ed4f4fa57f12a0647"}, + {file = "argon2_cffi-20.1.0-cp35-cp35m-win32.whl", hash = "sha256:9bee3212ba4f560af397b6d7146848c32a800652301843df06b9e8f68f0f7361"}, + {file = "argon2_cffi-20.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:392c3c2ef91d12da510cfb6f9bae52512a4552573a9e27600bdb800e05905d2b"}, + {file = "argon2_cffi-20.1.0-cp36-cp36m-win32.whl", hash = "sha256:ba7209b608945b889457f949cc04c8e762bed4fe3fec88ae9a6b7765ae82e496"}, + {file = "argon2_cffi-20.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:da7f0445b71db6d3a72462e04f36544b0de871289b0bc8a7cc87c0f5ec7079fa"}, + {file = "argon2_cffi-20.1.0-cp37-abi3-macosx_10_6_intel.whl", hash = "sha256:cc0e028b209a5483b6846053d5fd7165f460a1f14774d79e632e75e7ae64b82b"}, + {file = "argon2_cffi-20.1.0-cp37-cp37m-win32.whl", hash = "sha256:18dee20e25e4be86680b178b35ccfc5d495ebd5792cd00781548d50880fee5c5"}, + {file = "argon2_cffi-20.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6678bb047373f52bcff02db8afab0d2a77d83bde61cfecea7c5c62e2335cb203"}, + {file = "argon2_cffi-20.1.0-cp38-cp38-win32.whl", hash = "sha256:77e909cc756ef81d6abb60524d259d959bab384832f0c651ed7dcb6e5ccdbb78"}, + {file = "argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:9dfd5197852530294ecb5795c97a823839258dfd5eb9420233c7cfedec2058f2"}, + {file = "argon2_cffi-20.1.0-cp39-cp39-win32.whl", hash = "sha256:e2db6e85c057c16d0bd3b4d2b04f270a7467c147381e8fd73cbbe5bc719832be"}, + {file = "argon2_cffi-20.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8a84934bd818e14a17943de8099d41160da4a336bcc699bb4c394bbb9b94bd32"}, +] +async-generator = [ + {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, + {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, - {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, @@ -822,8 +942,47 @@ black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] bleach = [ - {file = "bleach-3.1.5-py2.py3-none-any.whl", hash = "sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f"}, - {file = "bleach-3.1.5.tar.gz", hash = "sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b"}, + {file = "bleach-3.3.0-py2.py3-none-any.whl", hash = "sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125"}, + {file = "bleach-3.3.0.tar.gz", hash = "sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"}, +] +cffi = [ + {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"}, + {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"}, + {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"}, + {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"}, + {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"}, + {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"}, + {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"}, + {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"}, + {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"}, + {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"}, + {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"}, + {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"}, + {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"}, + {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"}, + {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"}, + {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"}, + {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"}, + {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"}, + {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"}, + {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"}, + {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"}, + {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"}, + {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"}, + {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"}, + {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"}, + {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"}, + {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"}, + {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"}, + {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"}, ] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, @@ -838,68 +997,86 @@ commonmark = [ {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] coverage = [ - {file = "coverage-5.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270"}, - {file = "coverage-5.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4"}, - {file = "coverage-5.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9"}, - {file = "coverage-5.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729"}, - {file = "coverage-5.3-cp27-cp27m-win32.whl", hash = "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d"}, - {file = "coverage-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418"}, - {file = "coverage-5.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9"}, - {file = "coverage-5.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5"}, - {file = "coverage-5.3-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822"}, - {file = "coverage-5.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097"}, - {file = "coverage-5.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9"}, - {file = "coverage-5.3-cp35-cp35m-win32.whl", hash = "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636"}, - {file = "coverage-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f"}, - {file = "coverage-5.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237"}, - {file = "coverage-5.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54"}, - {file = "coverage-5.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7"}, - {file = "coverage-5.3-cp36-cp36m-win32.whl", hash = "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a"}, - {file = "coverage-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d"}, - {file = "coverage-5.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"}, - {file = "coverage-5.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f"}, - {file = "coverage-5.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c"}, - {file = "coverage-5.3-cp37-cp37m-win32.whl", hash = "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751"}, - {file = "coverage-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709"}, - {file = "coverage-5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516"}, - {file = "coverage-5.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f"}, - {file = "coverage-5.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259"}, - {file = "coverage-5.3-cp38-cp38-win32.whl", hash = "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82"}, - {file = "coverage-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221"}, - {file = "coverage-5.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978"}, - {file = "coverage-5.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21"}, - {file = "coverage-5.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24"}, - {file = "coverage-5.3-cp39-cp39-win32.whl", hash = "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7"}, - {file = "coverage-5.3-cp39-cp39-win_amd64.whl", hash = "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7"}, - {file = "coverage-5.3.tar.gz", hash = "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0"}, + {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, + {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, + {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, + {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, + {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, + {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, + {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, + {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, + {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, + {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, + {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, + {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, + {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, + {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, + {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, + {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, + {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, + {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, + {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, + {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, + {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] dataclasses = [ {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, ] decorator = [ - {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, - {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, + {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"}, + {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"}, ] defusedxml = [ - {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, - {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] entrypoints = [ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, ] importlib-metadata = [ - {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"}, - {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, + {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, + {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] ipykernel = [ - {file = "ipykernel-5.3.2-py3-none-any.whl", hash = "sha256:0a5f1fc6f63241b9710b5960d314ffe44d8a18bf6674e3f28d2542b192fa318c"}, - {file = "ipykernel-5.3.2.tar.gz", hash = "sha256:89dc4bd19c7781f6d7eef0e666c59ce57beac56bb39b511544a71397b7b31cbb"}, + {file = "ipykernel-5.5.4-py3-none-any.whl", hash = "sha256:f57739bf26d7396549562c0c888b96be896385ce099fb34ca89af359b7436b25"}, + {file = "ipykernel-5.5.4.tar.gz", hash = "sha256:1ce0e83672cc3bfdc1ffb5603e1d77ab125f24b41abc4612e22bfb3e994c0db2"}, ] ipython = [ {file = "ipython-7.16.1-py3-none-any.whl", hash = "sha256:2dbcc8c27ca7d3cfe4fcdff7f45b27f9a8d3edfa70ff8024a71c7a8eb5f09d64"}, @@ -914,24 +1091,28 @@ ipywidgets = [ {file = "ipywidgets-7.6.3.tar.gz", hash = "sha256:9f1a43e620530f9e570e4a493677d25f08310118d315b00e25a18f12913c41f0"}, ] jedi = [ - {file = "jedi-0.17.1-py2.py3-none-any.whl", hash = "sha256:1ddb0ec78059e8e27ec9eb5098360b4ea0a3dd840bedf21415ea820c21b40a22"}, - {file = "jedi-0.17.1.tar.gz", hash = "sha256:807d5d4f96711a2bcfdd5dfa3b1ae6d09aa53832b182090b222b5efb81f52f63"}, + {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, + {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, ] jinja2 = [ - {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, - {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, + {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, + {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] jsonschema = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, ] jupyter-client = [ - {file = "jupyter_client-6.1.5-py3-none-any.whl", hash = "sha256:9f0092a0951d878e7521924899e1fba6f689c7a99d43735a4c0bc05c6f311452"}, - {file = "jupyter_client-6.1.5.tar.gz", hash = "sha256:5099cda1ac86b27b655a715c51e15bdc8bd9595b2b17adb41a2bd446bbbafc4a"}, + {file = "jupyter_client-6.1.13-py3-none-any.whl", hash = "sha256:1df17b0525b45cc03645fc9eeab023765882d3c18fb100f82499cf6a353b3941"}, + {file = "jupyter_client-6.1.13.tar.gz", hash = "sha256:d03558bc9b7955d8b4a6df604a8d9d257e00bcea7fb364fe41cdef81d998a966"}, ] jupyter-core = [ - {file = "jupyter_core-4.6.3-py2.py3-none-any.whl", hash = "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21"}, - {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, + {file = "jupyter_core-4.7.1-py3-none-any.whl", hash = "sha256:8c6c0cac5c1b563622ad49321d5ec47017bd18b94facb381c6973a0486395f8e"}, + {file = "jupyter_core-4.7.1.tar.gz", hash = "sha256:79025cb3225efcd36847d0840f3fc672c0abd7afd0de83ba8a1d3837619122b4"}, +] +jupyterlab-pygments = [ + {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"}, + {file = "jupyterlab_pygments-0.1.2.tar.gz", hash = "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146"}, ] jupyterlab-widgets = [ {file = "jupyterlab_widgets-1.0.0-py3-none-any.whl", hash = "sha256:caeaf3e6103180e654e7d8d2b81b7d645e59e432487c1d35a41d6d3ee56b3fef"}, @@ -1023,28 +1204,36 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +nbclient = [ + {file = "nbclient-0.5.1-py3-none-any.whl", hash = "sha256:4d6b116187c795c99b9dba13d46e764d596574b14c296d60670c8dfe454db364"}, + {file = "nbclient-0.5.1.tar.gz", hash = "sha256:01e2d726d16eaf2cde6db74a87e2451453547e8832d142f73f72fddcd4fe0250"}, +] nbconvert = [ - {file = "nbconvert-5.6.1-py2.py3-none-any.whl", hash = "sha256:f0d6ec03875f96df45aa13e21fd9b8450c42d7e1830418cccc008c0df725fcee"}, - {file = "nbconvert-5.6.1.tar.gz", hash = "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523"}, + {file = "nbconvert-6.0.7-py3-none-any.whl", hash = "sha256:39e9f977920b203baea0be67eea59f7b37a761caa542abe80f5897ce3cf6311d"}, + {file = "nbconvert-6.0.7.tar.gz", hash = "sha256:cbbc13a86dfbd4d1b5dee106539de0795b4db156c894c2c5dc382062bbc29002"}, ] nbformat = [ - {file = "nbformat-5.0.7-py3-none-any.whl", hash = "sha256:ea55c9b817855e2dfcd3f66d74857342612a60b1f09653440f4a5845e6e3523f"}, - {file = "nbformat-5.0.7.tar.gz", hash = "sha256:54d4d6354835a936bad7e8182dcd003ca3dc0cedfee5a306090e04854343b340"}, + {file = "nbformat-5.1.3-py3-none-any.whl", hash = "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171"}, + {file = "nbformat-5.1.3.tar.gz", hash = "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8"}, +] +nest-asyncio = [ + {file = "nest_asyncio-1.5.1-py3-none-any.whl", hash = "sha256:76d6e972265063fe92a90b9cc4fb82616e07d586b346ed9d2c89a4187acea39c"}, + {file = "nest_asyncio-1.5.1.tar.gz", hash = "sha256:afc5a1c515210a23c461932765691ad39e8eba6551c055ac8d5546e69250d0aa"}, ] notebook = [ - {file = "notebook-6.0.3-py3-none-any.whl", hash = "sha256:3edc616c684214292994a3af05eaea4cc043f6b4247d830f3a2f209fa7639a80"}, - {file = "notebook-6.0.3.tar.gz", hash = "sha256:47a9092975c9e7965ada00b9a20f0cf637d001db60d241d479f53c0be117ad48"}, + {file = "notebook-6.3.0-py3-none-any.whl", hash = "sha256:cb271af1e8134e3d6fc6d458bdc79c40cbfc84c1eb036a493f216d58f0880e92"}, + {file = "notebook-6.3.0.tar.gz", hash = "sha256:cbc9398d6c81473e9cdb891d2cae9c0d3718fca289dda6d26df5cb660fcadc7d"}, ] packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] pandocfilters = [ - {file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"}, + {file = "pandocfilters-1.4.3.tar.gz", hash = "sha256:bc63fbb50534b4b1f8ebe1860889289e8af94a23bff7445259592df25a3906eb"}, ] parso = [ - {file = "parso-0.7.0-py2.py3-none-any.whl", hash = "sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0"}, - {file = "parso-0.7.0.tar.gz", hash = "sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c"}, + {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, + {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, ] pathspec = [ {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, @@ -1063,35 +1252,39 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] prometheus-client = [ - {file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"}, - {file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, + {file = "prometheus_client-0.10.1-py2.py3-none-any.whl", hash = "sha256:030e4f9df5f53db2292eec37c6255957eb76168c6f974e4176c711cf91ed34aa"}, + {file = "prometheus_client-0.10.1.tar.gz", hash = "sha256:b6c5a9643e3545bcbfd9451766cbaa5d9c67e7303c7bc32c750b6fa70ecb107d"}, ] prompt-toolkit = [ {file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"}, {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, ] ptyprocess = [ - {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, - {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] py = [ - {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, - {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, +] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, ] pygments = [ - {file = "Pygments-2.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"}, - {file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"}, + {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, + {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pyrsistent = [ - {file = "pyrsistent-0.16.0.tar.gz", hash = "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"}, + {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, ] pytest = [ - {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"}, - {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"}, + {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, + {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, ] pytest-cov = [ {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, @@ -1102,115 +1295,112 @@ python-dateutil = [ {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] pywin32 = [ - {file = "pywin32-228-cp27-cp27m-win32.whl", hash = "sha256:37dc9935f6a383cc744315ae0c2882ba1768d9b06700a70f35dc1ce73cd4ba9c"}, - {file = "pywin32-228-cp27-cp27m-win_amd64.whl", hash = "sha256:11cb6610efc2f078c9e6d8f5d0f957620c333f4b23466931a247fb945ed35e89"}, - {file = "pywin32-228-cp35-cp35m-win32.whl", hash = "sha256:1f45db18af5d36195447b2cffacd182fe2d296849ba0aecdab24d3852fbf3f80"}, - {file = "pywin32-228-cp35-cp35m-win_amd64.whl", hash = "sha256:6e38c44097a834a4707c1b63efa9c2435f5a42afabff634a17f563bc478dfcc8"}, - {file = "pywin32-228-cp36-cp36m-win32.whl", hash = "sha256:ec16d44b49b5f34e99eb97cf270806fdc560dff6f84d281eb2fcb89a014a56a9"}, - {file = "pywin32-228-cp36-cp36m-win_amd64.whl", hash = "sha256:a60d795c6590a5b6baeacd16c583d91cce8038f959bd80c53bd9a68f40130f2d"}, - {file = "pywin32-228-cp37-cp37m-win32.whl", hash = "sha256:af40887b6fc200eafe4d7742c48417529a8702dcc1a60bf89eee152d1d11209f"}, - {file = "pywin32-228-cp37-cp37m-win_amd64.whl", hash = "sha256:00eaf43dbd05ba6a9b0080c77e161e0b7a601f9a3f660727a952e40140537de7"}, - {file = "pywin32-228-cp38-cp38-win32.whl", hash = "sha256:fa6ba028909cfc64ce9e24bcf22f588b14871980d9787f1e2002c99af8f1850c"}, - {file = "pywin32-228-cp38-cp38-win_amd64.whl", hash = "sha256:9b3466083f8271e1a5eb0329f4e0d61925d46b40b195a33413e0905dccb285e8"}, - {file = "pywin32-228-cp39-cp39-win32.whl", hash = "sha256:ed74b72d8059a6606f64842e7917aeee99159ebd6b8d6261c518d002837be298"}, - {file = "pywin32-228-cp39-cp39-win_amd64.whl", hash = "sha256:8319bafdcd90b7202c50d6014efdfe4fde9311b3ff15fd6f893a45c0868de203"}, + {file = "pywin32-300-cp35-cp35m-win32.whl", hash = "sha256:1c204a81daed2089e55d11eefa4826c05e604d27fe2be40b6bf8db7b6a39da63"}, + {file = "pywin32-300-cp35-cp35m-win_amd64.whl", hash = "sha256:350c5644775736351b77ba68da09a39c760d75d2467ecec37bd3c36a94fbed64"}, + {file = "pywin32-300-cp36-cp36m-win32.whl", hash = "sha256:a3b4c48c852d4107e8a8ec980b76c94ce596ea66d60f7a697582ea9dce7e0db7"}, + {file = "pywin32-300-cp36-cp36m-win_amd64.whl", hash = "sha256:27a30b887afbf05a9cbb05e3ffd43104a9b71ce292f64a635389dbad0ed1cd85"}, + {file = "pywin32-300-cp37-cp37m-win32.whl", hash = "sha256:d7e8c7efc221f10d6400c19c32a031add1c4a58733298c09216f57b4fde110dc"}, + {file = "pywin32-300-cp37-cp37m-win_amd64.whl", hash = "sha256:8151e4d7a19262d6694162d6da85d99a16f8b908949797fd99c83a0bfaf5807d"}, + {file = "pywin32-300-cp38-cp38-win32.whl", hash = "sha256:fbb3b1b0fbd0b4fc2a3d1d81fe0783e30062c1abed1d17c32b7879d55858cfae"}, + {file = "pywin32-300-cp38-cp38-win_amd64.whl", hash = "sha256:60a8fa361091b2eea27f15718f8eb7f9297e8d51b54dbc4f55f3d238093d5190"}, + {file = "pywin32-300-cp39-cp39-win32.whl", hash = "sha256:638b68eea5cfc8def537e43e9554747f8dee786b090e47ead94bfdafdb0f2f50"}, + {file = "pywin32-300-cp39-cp39-win_amd64.whl", hash = "sha256:b1609ce9bd5c411b81f941b246d683d6508992093203d4eb7f278f4ed1085c3f"}, ] pywinpty = [ - {file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"}, - {file = "pywinpty-0.5.7-cp27-cp27m-win_amd64.whl", hash = "sha256:1e525a4de05e72016a7af27836d512db67d06a015aeaf2fa0180f8e6a039b3c2"}, - {file = "pywinpty-0.5.7-cp35-cp35m-win32.whl", hash = "sha256:2740eeeb59297593a0d3f762269b01d0285c1b829d6827445fcd348fb47f7e70"}, - {file = "pywinpty-0.5.7-cp35-cp35m-win_amd64.whl", hash = "sha256:33df97f79843b2b8b8bc5c7aaf54adec08cc1bae94ee99dfb1a93c7a67704d95"}, - {file = "pywinpty-0.5.7-cp36-cp36m-win32.whl", hash = "sha256:e854211df55d107f0edfda8a80b39dfc87015bef52a8fe6594eb379240d81df2"}, - {file = "pywinpty-0.5.7-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd838de92de1d4ebf0dce9d4d5e4fc38d0b7b1de837947a18b57a882f219139"}, - {file = "pywinpty-0.5.7-cp37-cp37m-win32.whl", hash = "sha256:5fb2c6c6819491b216f78acc2c521b9df21e0f53b9a399d58a5c151a3c4e2a2d"}, - {file = "pywinpty-0.5.7-cp37-cp37m-win_amd64.whl", hash = "sha256:dd22c8efacf600730abe4a46c1388355ce0d4ab75dc79b15d23a7bd87bf05b48"}, - {file = "pywinpty-0.5.7-cp38-cp38-win_amd64.whl", hash = "sha256:8fc5019ff3efb4f13708bd3b5ad327589c1a554cb516d792527361525a7cb78c"}, - {file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"}, + {file = "pywinpty-1.0.1-cp36-none-win_amd64.whl", hash = "sha256:739094e8d0d685a64c92ff91424cf43da9296110349036161ab294268e444d05"}, + {file = "pywinpty-1.0.1-cp37-none-win_amd64.whl", hash = "sha256:5447b8c158e5807237f80ea4e14262f0c05ff7c4d39f1c4b697ea6e8920786b2"}, + {file = "pywinpty-1.0.1-cp38-none-win_amd64.whl", hash = "sha256:aa3e4178503ff6be3e8a1d9ae4ce77de9058308562dbf26b505a51583be9f02d"}, + {file = "pywinpty-1.0.1-cp39-none-win_amd64.whl", hash = "sha256:58e23d59891e624d478ec7bcc42ced0ecfbf0a4e7cb0217de714f785f71c2461"}, + {file = "pywinpty-1.0.1.tar.gz", hash = "sha256:b3512d4a964a0abae1b77b6908917c62ea0ad7d8178696e4e973877fe9e820f9"}, ] pyzmq = [ - {file = "pyzmq-19.0.1-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:58688a2dfa044fad608a8e70ba8d019d0b872ec2acd75b7b5e37da8905605891"}, - {file = "pyzmq-19.0.1-cp27-cp27m-win32.whl", hash = "sha256:87c78f6936e2654397ca2979c1d323ee4a889eef536cc77a938c6b5be33351a7"}, - {file = "pyzmq-19.0.1-cp27-cp27m-win_amd64.whl", hash = "sha256:97b6255ae77328d0e80593681826a0479cb7bac0ba8251b4dd882f5145a2293a"}, - {file = "pyzmq-19.0.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:15b4cb21118f4589c4db8be4ac12b21c8b4d0d42b3ee435d47f686c32fe2e91f"}, - {file = "pyzmq-19.0.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:931339ac2000d12fe212e64f98ce291e81a7ec6c73b125f17cf08415b753c087"}, - {file = "pyzmq-19.0.1-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:2a88b8fabd9cc35bd59194a7723f3122166811ece8b74018147a4ed8489e6421"}, - {file = "pyzmq-19.0.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:bafd651b557dd81d89bd5f9c678872f3e7b7255c1c751b78d520df2caac80230"}, - {file = "pyzmq-19.0.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8952f6ba6ae598e792703f3134af5a01af8f5c7cf07e9a148f05a12b02412cea"}, - {file = "pyzmq-19.0.1-cp35-cp35m-win32.whl", hash = "sha256:54aa24fd60c4262286fc64ca632f9e747c7cc3a3a1144827490e1dc9b8a3a960"}, - {file = "pyzmq-19.0.1-cp35-cp35m-win_amd64.whl", hash = "sha256:dcbc3f30c11c60d709c30a213dc56e88ac016fe76ac6768e64717bd976072566"}, - {file = "pyzmq-19.0.1-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:6ca519309703e95d55965735a667809bbb65f52beda2fdb6312385d3e7a6d234"}, - {file = "pyzmq-19.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4ee0bfd82077a3ff11c985369529b12853a4064320523f8e5079b630f9551448"}, - {file = "pyzmq-19.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ba6f24431b569aec674ede49cad197cad59571c12deed6ad8e3c596da8288217"}, - {file = "pyzmq-19.0.1-cp36-cp36m-win32.whl", hash = "sha256:956775444d01331c7eb412c5fb9bb62130dfaac77e09f32764ea1865234e2ca9"}, - {file = "pyzmq-19.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b08780e3a55215873b3b8e6e7ca8987f14c902a24b6ac081b344fd430d6ca7cd"}, - {file = "pyzmq-19.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21f7d91f3536f480cb2c10d0756bfa717927090b7fb863e6323f766e5461ee1c"}, - {file = "pyzmq-19.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bfff5ffff051f5aa47ba3b379d87bd051c3196b0c8a603e8b7ed68a6b4f217ec"}, - {file = "pyzmq-19.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:07fb8fe6826a229dada876956590135871de60dbc7de5a18c3bcce2ed1f03c98"}, - {file = "pyzmq-19.0.1-cp37-cp37m-win32.whl", hash = "sha256:342fb8a1dddc569bc361387782e8088071593e7eaf3e3ecf7d6bd4976edff112"}, - {file = "pyzmq-19.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:faee2604f279d31312bc455f3d024f160b6168b9c1dde22bf62d8c88a4deca8e"}, - {file = "pyzmq-19.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b9d21fc56c8aacd2e6d14738021a9d64f3f69b30578a99325a728e38a349f85"}, - {file = "pyzmq-19.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af0c02cf49f4f9eedf38edb4f3b6bb621d83026e7e5d76eb5526cc5333782fd6"}, - {file = "pyzmq-19.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5f1f2eb22aab606f808163eb1d537ac9a0ba4283fbeb7a62eb48d9103cf015c2"}, - {file = "pyzmq-19.0.1-cp38-cp38-win32.whl", hash = "sha256:f9d7e742fb0196992477415bb34366c12e9bb9a0699b8b3f221ff93b213d7bec"}, - {file = "pyzmq-19.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:5b99c2ae8089ef50223c28bac57510c163bfdff158c9e90764f812b94e69a0e6"}, - {file = "pyzmq-19.0.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:cf5d689ba9513b9753959164cf500079383bc18859f58bf8ce06d8d4bef2b054"}, - {file = "pyzmq-19.0.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:aaa8b40b676576fd7806839a5de8e6d5d1b74981e6376d862af6c117af2a3c10"}, - {file = "pyzmq-19.0.1.tar.gz", hash = "sha256:13a5638ab24d628a6ade8f794195e1a1acd573496c3b85af2f1183603b7bf5e0"}, + {file = "pyzmq-22.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c0cde362075ee8f3d2b0353b283e203c2200243b5a15d5c5c03b78112a17e7d4"}, + {file = "pyzmq-22.0.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:ff1ea14075bbddd6f29bf6beb8a46d0db779bcec6b9820909584081ec119f8fd"}, + {file = "pyzmq-22.0.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:26380487eae4034d6c2a3fb8d0f2dff6dd0d9dd711894e8d25aa2d1938950a33"}, + {file = "pyzmq-22.0.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:3e29f9cf85a40d521d048b55c63f59d6c772ac1c4bf51cdfc23b62a62e377c33"}, + {file = "pyzmq-22.0.3-cp36-cp36m-win32.whl", hash = "sha256:4f34a173f813b38b83f058e267e30465ed64b22cd0cf6bad21148d3fa718f9bb"}, + {file = "pyzmq-22.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:30df70f81fe210506aa354d7fd486a39b87d9f7f24c3d3f4f698ec5d96b8c084"}, + {file = "pyzmq-22.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7026f0353977431fc884abd4ac28268894bd1a780ba84bb266d470b0ec26d2ed"}, + {file = "pyzmq-22.0.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6d4163704201fff0f3ab0cd5d7a0ea1514ecfffd3926d62ec7e740a04d2012c7"}, + {file = "pyzmq-22.0.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:763c175294d861869f18eb42901d500eda7d3fa4565f160b3b2fd2678ea0ebab"}, + {file = "pyzmq-22.0.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:61e4bb6cd60caf1abcd796c3f48395e22c5b486eeca6f3a8797975c57d94b03e"}, + {file = "pyzmq-22.0.3-cp37-cp37m-win32.whl", hash = "sha256:b25e5d339550a850f7e919fe8cb4c8eabe4c917613db48dab3df19bfb9a28969"}, + {file = "pyzmq-22.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3ef50d74469b03725d781a2a03c57537d86847ccde587130fe35caafea8f75c6"}, + {file = "pyzmq-22.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60e63577b85055e4cc43892fecd877b86695ee3ef12d5d10a3c5d6e77a7cc1a3"}, + {file = "pyzmq-22.0.3-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:f5831eff6b125992ec65d973f5151c48003b6754030094723ac4c6e80a97c8c4"}, + {file = "pyzmq-22.0.3-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:9221783dacb419604d5345d0e097bddef4459a9a95322de6c306bf1d9896559f"}, + {file = "pyzmq-22.0.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:b62ea18c0458a65ccd5be90f276f7a5a3f26a6dea0066d948ce2fa896051420f"}, + {file = "pyzmq-22.0.3-cp38-cp38-win32.whl", hash = "sha256:81e7df0da456206201e226491aa1fc449da85328bf33bbeec2c03bb3a9f18324"}, + {file = "pyzmq-22.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:f52070871a0fd90a99130babf21f8af192304ec1e995bec2a9533efc21ea4452"}, + {file = "pyzmq-22.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:c5e29fe4678f97ce429f076a2a049a3d0b2660ada8f2c621e5dc9939426056dd"}, + {file = "pyzmq-22.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d18ddc6741b51f3985978f2fda57ddcdae359662d7a6b395bc8ff2292fca14bd"}, + {file = "pyzmq-22.0.3-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4231943514812dfb74f44eadcf85e8dd8cf302b4d0bce450ce1357cac88dbfdc"}, + {file = "pyzmq-22.0.3-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:23a74de4b43c05c3044aeba0d1f3970def8f916151a712a3ac1e5cd9c0bc2902"}, + {file = "pyzmq-22.0.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:532af3e6dddea62d9c49062ece5add998c9823c2419da943cf95589f56737de0"}, + {file = "pyzmq-22.0.3-cp39-cp39-win32.whl", hash = "sha256:33acd2b9790818b9d00526135acf12790649d8d34b2b04d64558b469c9d86820"}, + {file = "pyzmq-22.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:a558c5bc89d56d7253187dccc4e81b5bb0eac5ae9511eb4951910a1245d04622"}, + {file = "pyzmq-22.0.3-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:581787c62eaa0e0db6c5413cedc393ebbadac6ddfd22e1cf9a60da23c4f1a4b2"}, + {file = "pyzmq-22.0.3-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:38e3dca75d81bec4f2defa14b0a65b74545812bb519a8e89c8df96bbf4639356"}, + {file = "pyzmq-22.0.3-pp36-pypy36_pp73-win32.whl", hash = "sha256:2f971431aaebe0a8b54ac018e041c2f0b949a43745444e4dadcc80d0f0ef8457"}, + {file = "pyzmq-22.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da7d4d4c778c86b60949d17531e60c54ed3726878de8a7f8a6d6e7f8cc8c3205"}, + {file = "pyzmq-22.0.3-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:13465c1ff969cab328bc92f7015ce3843f6e35f8871ad79d236e4fbc85dbe4cb"}, + {file = "pyzmq-22.0.3-pp37-pypy37_pp73-win32.whl", hash = "sha256:279cc9b51db48bec2db146f38e336049ac5a59e5f12fb3a8ad864e238c1c62e3"}, + {file = "pyzmq-22.0.3.tar.gz", hash = "sha256:f7f63ce127980d40f3e6a5fdb87abf17ce1a7c2bd8bf2c7560e1bbce8ab1f92d"}, ] regex = [ - {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, - {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, - {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, - {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, - {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, - {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, - {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, - {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, - {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, - {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, - {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, - {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, - {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, - {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, - {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, - {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, - {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, + {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, + {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, + {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, + {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, + {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, + {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, + {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, + {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, + {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, + {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, + {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, + {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, + {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, + {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, + {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, + {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, + {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, ] send2trash = [ {file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"}, {file = "Send2Trash-1.5.0.tar.gz", hash = "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2"}, ] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] terminado = [ - {file = "terminado-0.8.3-py2.py3-none-any.whl", hash = "sha256:a43dcb3e353bc680dd0783b1d9c3fc28d529f190bc54ba9a229f72fe6e7a54d7"}, - {file = "terminado-0.8.3.tar.gz", hash = "sha256:4804a774f802306a7d9af7322193c5390f1da0abb429e082a10ef1d46e6fb2c2"}, + {file = "terminado-0.9.4-py3-none-any.whl", hash = "sha256:daed77f9fad7b32558fa84b226a76f45a02242c20813502f36c4e1ade6d8f1ad"}, + {file = "terminado-0.9.4.tar.gz", hash = "sha256:9a7dbcfbc2778830eeb70261bf7aa9d98a3eac8631a3afe3febeb57c12f798be"}, ] testpath = [ {file = "testpath-0.4.4-py2.py3-none-any.whl", hash = "sha256:bfcf9411ef4bf3db7579063e0546938b1edda3d69f4e1fb8756991f5951f85d4"}, @@ -1221,56 +1411,88 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tornado = [ - {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, - {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, - {file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"}, - {file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"}, - {file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"}, - {file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"}, - {file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"}, - {file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"}, - {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, + {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, + {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, + {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, + {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, + {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, + {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, + {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, + {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, + {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, + {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, + {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, + {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, + {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, + {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, + {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, + {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, + {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, + {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, ] traitlets = [ {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, ] typed-ast = [ - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, - {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, - {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, - {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d"}, - {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, - {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d"}, - {file = "typed_ast-1.4.1-cp39-cp39-win32.whl", hash = "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395"}, - {file = "typed_ast-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c"}, - {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, + {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, + {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, + {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, + {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, + {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, + {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, + {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, + {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, + {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, + {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, + {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, + {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, + {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, + {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, + {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, + {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, + {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, @@ -1285,6 +1507,6 @@ widgetsnbextension = [ {file = "widgetsnbextension-3.5.1.tar.gz", hash = "sha256:079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7"}, ] zipp = [ - {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, - {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, + {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, + {file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"}, ] diff --git a/pyproject.toml b/pyproject.toml index 159ea5935..c6dd9901f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ name = "rich" homepage = "https://github.com/willmcgugan/rich" documentation = "https://rich.readthedocs.io/en/latest/" -version = "10.1.0" +version = "10.2.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" authors = ["Will McGugan "] license = "MIT" @@ -25,7 +25,7 @@ include = ["rich/py.typed"] [tool.poetry.dependencies] python = "^3.6" -typing-extensions = "^3.7.4" +typing-extensions = {version = "^3.7.4", python = "<3.8"} dataclasses = {version=">=0.7,<0.9", python = "~3.6"} pygments = "^2.6.0" commonmark = "^0.9.0" @@ -37,10 +37,11 @@ ipywidgets = {version = "^7.5.1", optional = true} jupyter = ["ipywidgets"] [tool.poetry.dev-dependencies] -pytest = "^6.2.2" +pytest = "^6.2.3" black = "^20.8b1" mypy = "^0.812" pytest-cov = "^2.11.1" +attrs = "^21.2.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/rich/__init__.py b/rich/__init__.py index b0e4c8d94..c75333f73 100644 --- a/rich/__init__.py +++ b/rich/__init__.py @@ -30,8 +30,8 @@ def get_console() -> "Console": return _console -def reconfigure(*args, **kwargs) -> None: - """Reconfigures the global console bu replacing it with another. +def reconfigure(*args: Any, **kwargs: Any) -> None: + """Reconfigures the global console by replacing it with another. Args: console (Console): Replacement console instance. @@ -42,7 +42,13 @@ def reconfigure(*args, **kwargs) -> None: _console.__dict__ = new_console.__dict__ -def print(*objects: Any, sep=" ", end="\n", file: IO[str] = None, flush: bool = False): +def print( + *objects: Any, + sep: str = " ", + end: str = "\n", + file: Optional[IO[str]] = None, + flush: bool = False +) -> None: r"""Print object(s) supplied via positional arguments. This function has an identical signature to the built-in print. For more advanced features, see the :class:`~rich.console.Console` class. @@ -63,8 +69,8 @@ def print(*objects: Any, sep=" ", end="\n", file: IO[str] = None, flush: bool = def inspect( obj: Any, *, - console: "Console" = None, - title: str = None, + console: Optional["Console"] = None, + title: Optional[str] = None, help: bool = False, methods: bool = False, docs: bool = True, @@ -73,7 +79,7 @@ def inspect( sort: bool = True, all: bool = False, value: bool = True -): +) -> None: """Inspect any Python object. * inspect() to see summarized info. diff --git a/rich/__main__.py b/rich/__main__.py index 4744e54a8..59fbd2566 100644 --- a/rich/__main__.py +++ b/rich/__main__.py @@ -1,10 +1,18 @@ import colorsys import io from time import process_time +from typing import Any from rich import box from rich.color import Color -from rich.console import Console, ConsoleOptions, RenderGroup, RenderResult +from rich.console import ( + Console, + ConsoleOptions, + ConsoleRenderable, + RenderGroup, + RenderResult, + RenderableType, +) from rich.markdown import Markdown from rich.measure import Measurement from rich.pretty import Pretty @@ -88,7 +96,7 @@ def make_test_card() -> Table: ), ) - def comparison(renderable1, renderable2) -> Table: + def comparison(renderable1: RenderableType, renderable2: RenderableType) -> Table: table = Table(show_header=False, pad_edge=False, box=None, expand=True) table.add_column("1", ratio=1) table.add_column("2", ratio=1) diff --git a/rich/_emoji_replace.py b/rich/_emoji_replace.py index ee8cde009..8539fd670 100644 --- a/rich/_emoji_replace.py +++ b/rich/_emoji_replace.py @@ -1,11 +1,18 @@ -from typing import Match +from typing import Callable, Match import re from ._emoji_codes import EMOJI -def _emoji_replace(text: str, _emoji_sub=re.compile(r"(:(\S*?):)").sub) -> str: +_ReStringMatch = Match[str] # regex match object +_ReSubCallable = Callable[[_ReStringMatch], str] # Callable invoked by re.sub +_EmojiSubMethod = Callable[[_ReSubCallable, str], str] # Sub method of a compiled re + + +def _emoji_replace( + text: str, _emoji_sub: _EmojiSubMethod = re.compile(r"(:(\S*?):)").sub +) -> str: """Replace emoji code in text.""" get_emoji = EMOJI.get diff --git a/rich/_inspect.py b/rich/_inspect.py index 6e6ed8786..68043b368 100644 --- a/rich/_inspect.py +++ b/rich/_inspect.py @@ -44,7 +44,7 @@ def __init__( self, obj: Any, *, - title: TextType = None, + title: Optional[TextType] = None, help: bool = False, methods: bool = False, docs: bool = True, @@ -204,8 +204,6 @@ def safe_getattr(attr_name: str) -> Tuple[Any, Any]: if items_table.row_count: yield items_table else: - yield self.highlighter( - Text.from_markup( - f"[i][b]{not_shown_count}[/b] attribute(s) not shown.[/i] Run [b][red]inspect[/red]([not b]inspect[/])[/b] for options." - ) + yield Text.from_markup( + f"[b cyan]{not_shown_count}[/][i] attribute(s) not shown.[/i] Run [b][magenta]inspect[/]([not b]inspect[/])[/b] for options." ) diff --git a/rich/_log_render.py b/rich/_log_render.py index 67579633a..3a77f0c87 100644 --- a/rich/_log_render.py +++ b/rich/_log_render.py @@ -33,12 +33,12 @@ def __call__( self, console: "Console", renderables: Iterable["ConsoleRenderable"], - log_time: datetime = None, - time_format: Union[str, FormatTimeCallable] = None, + log_time: Optional[datetime] = None, + time_format: Optional[Union[str, FormatTimeCallable]] = None, level: TextType = "", - path: str = None, - line_no: int = None, - link_path: str = None, + path: Optional[str] = None, + line_no: Optional[int] = None, + link_path: Optional[str] = None, ) -> "Table": from .containers import Renderables from .table import Table diff --git a/rich/_lru_cache.py b/rich/_lru_cache.py index b77c337ca..b7bf2ce1a 100644 --- a/rich/_lru_cache.py +++ b/rich/_lru_cache.py @@ -6,7 +6,7 @@ CacheValue = TypeVar("CacheValue") -class LRUCache(Generic[CacheKey, CacheValue], OrderedDict): +class LRUCache(Generic[CacheKey, CacheValue], OrderedDict): # type: ignore # https://github.com/python/mypy/issues/6904 """ A dictionary-like container that stores a given maximum items. diff --git a/rich/_ratio.py b/rich/_ratio.py index 6de54bc9e..8084d39f1 100644 --- a/rich/_ratio.py +++ b/rich/_ratio.py @@ -1,7 +1,12 @@ +import sys from fractions import Fraction from math import ceil, floor, modf from typing import cast, List, Optional, Sequence -from typing_extensions import Protocol + +if sys.version_info >= (3, 8): + from typing import Protocol +else: + from typing_extensions import Protocol class Edge(Protocol): @@ -106,7 +111,7 @@ def ratio_reduce( def ratio_distribute( - total: int, ratios: List[int], minimums: List[int] = None + total: int, ratios: List[int], minimums: Optional[List[int]] = None ) -> List[int]: """Distribute an integer total in to parts based on ratios. @@ -141,7 +146,7 @@ def ratio_distribute( return distributed_total -if __name__ == "__main__": # type: ignore +if __name__ == "__main__": from dataclasses import dataclass @dataclass diff --git a/rich/_timer.py b/rich/_timer.py index b30d37490..a2ca6be03 100644 --- a/rich/_timer.py +++ b/rich/_timer.py @@ -6,10 +6,11 @@ from time import time import contextlib +from typing import Generator @contextlib.contextmanager -def timer(subject: str = "time"): +def timer(subject: str = "time") -> Generator[None, None, None]: """print the elapsed time. (only used in debugging)""" start = time() yield diff --git a/rich/_windows.py b/rich/_windows.py index d252d1f9e..bac88e639 100644 --- a/rich/_windows.py +++ b/rich/_windows.py @@ -18,7 +18,11 @@ class WindowsConsoleFeatures: from ctypes import wintypes from ctypes import LibraryLoader - windll = LibraryLoader(ctypes.WinDLL) # type: ignore + if sys.platform == "win32": + windll = LibraryLoader(ctypes.WinDLL) + else: + windll = None + raise ImportError("Not windows") except (AttributeError, ImportError, ValueError): # Fallback if we can't load the Windows DLL @@ -53,7 +57,7 @@ def get_windows_console_features() -> WindowsConsoleFeatures: vt = bool(result and console_mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) truecolor = False if vt: - win_version = sys.getwindowsversion() # type: ignore + win_version = sys.getwindowsversion() truecolor = win_version.major > 10 or ( win_version.major == 10 and win_version.build >= 15063 ) diff --git a/rich/align.py b/rich/align.py index 83bf5d697..22e4c60f7 100644 --- a/rich/align.py +++ b/rich/align.py @@ -1,16 +1,20 @@ +import sys from itertools import chain -from typing import Iterable, TYPE_CHECKING +from typing import TYPE_CHECKING, Iterable, Optional + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal -from typing_extensions import Literal from .constrain import Constrain from .jupyter import JupyterMixin from .measure import Measurement from .segment import Segment from .style import StyleType - if TYPE_CHECKING: - from .console import Console, ConsoleOptions, RenderResult, RenderableType + from .console import Console, ConsoleOptions, RenderableType, RenderResult AlignMethod = Literal["left", "center", "right"] VerticalAlignMethod = Literal["top", "middle", "bottom"] @@ -37,12 +41,12 @@ def __init__( self, renderable: "RenderableType", align: AlignMethod = "left", - style: StyleType = None, + style: Optional[StyleType] = None, *, - vertical: VerticalAlignMethod = None, + vertical: Optional[VerticalAlignMethod] = None, pad: bool = True, - width: int = None, - height: int = None, + width: Optional[int] = None, + height: Optional[int] = None, ) -> None: if align not in ("left", "center", "right"): raise ValueError( @@ -67,12 +71,12 @@ def __repr__(self) -> str: def left( cls, renderable: "RenderableType", - style: StyleType = None, + style: Optional[StyleType] = None, *, - vertical: VerticalAlignMethod = None, + vertical: Optional[VerticalAlignMethod] = None, pad: bool = True, - width: int = None, - height: int = None, + width: Optional[int] = None, + height: Optional[int] = None, ) -> "Align": """Align a renderable to the left.""" return cls( @@ -89,12 +93,12 @@ def left( def center( cls, renderable: "RenderableType", - style: StyleType = None, + style: Optional[StyleType] = None, *, - vertical: VerticalAlignMethod = None, + vertical: Optional[VerticalAlignMethod] = None, pad: bool = True, - width: int = None, - height: int = None, + width: Optional[int] = None, + height: Optional[int] = None, ) -> "Align": """Align a renderable to the center.""" return cls( @@ -111,12 +115,12 @@ def center( def right( cls, renderable: "RenderableType", - style: StyleType = None, + style: Optional[StyleType] = None, *, - vertical: VerticalAlignMethod = None, + vertical: Optional[VerticalAlignMethod] = None, pad: bool = True, - width: int = None, - height: int = None, + width: Optional[int] = None, + height: Optional[int] = None, ) -> "Align": """Align a renderable to the right.""" return cls( @@ -133,7 +137,7 @@ def __rich_console__( self, console: "Console", options: "ConsoleOptions" ) -> "RenderResult": align = self.align - width = Measurement.get(console, options, self.renderable).maximum + width = console.measure(self.renderable, options=options).maximum rendered = console.render( Constrain( self.renderable, width if self.width is None else min(width, self.width) @@ -192,7 +196,7 @@ def generate_segments() -> Iterable[Segment]: else Segment("\n") ) - def blank_lines(count) -> Iterable[Segment]: + def blank_lines(count: int) -> Iterable[Segment]: if count > 0: for _ in range(count): yield blank_line @@ -242,7 +246,7 @@ class VerticalCenter(JupyterMixin): def __init__( self, renderable: "RenderableType", - style: StyleType = None, + style: Optional[StyleType] = None, ) -> None: self.renderable = renderable self.style = style @@ -264,7 +268,7 @@ def __rich_console__( bottom_space = height - top_space - len(lines) blank_line = Segment(f"{' ' * width}", style) - def blank_lines(count) -> Iterable[Segment]: + def blank_lines(count: int) -> Iterable[Segment]: for _ in range(count): yield blank_line yield new_line diff --git a/rich/ansi.py b/rich/ansi.py index 85410de5e..92e4772ed 100644 --- a/rich/ansi.py +++ b/rich/ansi.py @@ -208,7 +208,7 @@ def decode_line(self, line: str) -> Text: stdout = io.BytesIO() - def read(fd): + def read(fd: int) -> bytes: data = os.read(fd, 1024) stdout.write(data) return data diff --git a/rich/bar.py b/rich/bar.py index ecd18743e..ed86a552d 100644 --- a/rich/bar.py +++ b/rich/bar.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Optional, Union from .color import Color from .console import Console, ConsoleOptions, RenderResult @@ -32,7 +32,7 @@ def __init__( begin: float, end: float, *, - width: int = None, + width: Optional[int] = None, color: Union[Color, str] = "default", bgcolor: Union[Color, str] = "default", ): diff --git a/rich/box.py b/rich/box.py index b70b5c2cd..d044f74ae 100644 --- a/rich/box.py +++ b/rich/box.py @@ -1,6 +1,11 @@ +import sys from typing import TYPE_CHECKING, Iterable, List -from typing_extensions import Literal +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + from ._loop import loop_last diff --git a/rich/color.py b/rich/color.py index 3421061cc..a8032ee0d 100644 --- a/rich/color.py +++ b/rich/color.py @@ -305,7 +305,7 @@ def is_default(self) -> bool: return self.type == ColorType.DEFAULT def get_truecolor( - self, theme: "TerminalTheme" = None, foreground=True + self, theme: Optional["TerminalTheme"] = None, foreground: bool = True ) -> ColorTriplet: """Get an equivalent color triplet for this color. @@ -471,7 +471,7 @@ def get_ansi_codes(self, foreground: bool = True) -> Tuple[str, ...]: def downgrade(self, system: ColorSystem) -> "Color": """Downgrade a color system to a system with fewer colors.""" - if self.type == ColorType.DEFAULT or self.type == system: + if self.type in [ColorType.DEFAULT, system]: return self # Convert to 8-bit color from truecolor color if system == ColorSystem.EIGHT_BIT and self.system == ColorSystem.TRUECOLOR: diff --git a/rich/columns.py b/rich/columns.py index 9746ecb71..d68654e58 100644 --- a/rich/columns.py +++ b/rich/columns.py @@ -30,16 +30,16 @@ class Columns(JupyterMixin): def __init__( self, - renderables: Iterable[RenderableType] = None, + renderables: Optional[Iterable[RenderableType]] = None, padding: PaddingDimensions = (0, 1), *, - width: int = None, + width: Optional[int] = None, expand: bool = False, equal: bool = False, column_first: bool = False, right_to_left: bool = False, - align: AlignMethod = None, - title: TextType = None, + align: Optional[AlignMethod] = None, + title: Optional[TextType] = None, ) -> None: self.renderables = list(renderables or []) self.width = width diff --git a/rich/console.py b/rich/console.py index 71fa0809e..08dc9108a 100644 --- a/rich/console.py +++ b/rich/console.py @@ -12,6 +12,7 @@ from getpass import getpass from itertools import islice from time import monotonic +from types import TracebackType from typing import ( IO, TYPE_CHECKING, @@ -24,11 +25,17 @@ NamedTuple, Optional, TextIO, + Type, Union, cast, ) -from typing_extensions import Literal, Protocol, runtime_checkable + +if sys.version_info >= (3, 8): + from typing import Literal, Protocol, runtime_checkable +else: + from typing_extensions import Literal, Protocol, runtime_checkable + from . import errors, themes from ._emoji_replace import _emoji_replace @@ -40,7 +47,7 @@ from .markup import render as render_markup from .measure import Measurement, measure_renderables from .pager import Pager, SystemPager -from .pretty import is_expandable, Pretty +from .pretty import Pretty, is_expandable from .region import Region from .scope import render_scope from .screen import Screen @@ -60,6 +67,7 @@ HighlighterType = Callable[[Union[str, "Text"]], "Text"] JustifyMethod = Literal["default", "left", "center", "right", "full"] + OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"] @@ -143,7 +151,7 @@ def copy(self) -> "ConsoleOptions": Returns: ConsoleOptions: a copy of self. """ - options = ConsoleOptions.__new__(ConsoleOptions) + options: ConsoleOptions = ConsoleOptions.__new__(ConsoleOptions) options.__dict__ = self.__dict__.copy() return options @@ -289,7 +297,12 @@ def __enter__(self) -> "Capture": self._console.begin_capture() return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: self._result = self._console.end_capture() def get(self) -> str: @@ -313,7 +326,12 @@ def __enter__(self) -> "ThemeContext": self.console.push_theme(self.theme) return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: self.console.pop_theme() @@ -323,7 +341,7 @@ class PagerContext: def __init__( self, console: "Console", - pager: Pager = None, + pager: Optional[Pager] = None, styles: bool = False, links: bool = False, ) -> None: @@ -336,7 +354,12 @@ def __enter__(self) -> "PagerContext": self._console._enter_buffer() return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: if exc_type is None: with self._console._lock: buffer: List[Segment] = self._console._buffer[:] @@ -362,7 +385,9 @@ def __init__( self.screen = Screen(style=style) self._changed = False - def update(self, *renderables: RenderableType, style: StyleType = None) -> None: + def update( + self, *renderables: RenderableType, style: Optional[StyleType] = None + ) -> None: """Update the screen. Args: @@ -384,7 +409,12 @@ def __enter__(self) -> "ScreenContext": self.console.show_cursor(False) return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: if self._changed: self.console.set_alt_screen(False) if self.hide_cursor: @@ -424,18 +454,20 @@ def __rich_console__( yield from self.renderables -def render_group(fit: bool = True) -> Callable: +def render_group(fit: bool = True) -> Callable[..., Callable[..., RenderGroup]]: """A decorator that turns an iterable of renderables in to a group. Args: fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True. """ - def decorator(method): + def decorator( + method: Callable[..., Iterable[RenderableType]] + ) -> Callable[..., RenderGroup]: """Convert a method that returns an iterable of renderables in to a RenderGroup.""" @wraps(method) - def _replace(*args, **kwargs): + def _replace(*args: Any, **kwargs: Any) -> RenderGroup: renderables = method(*args, **kwargs) return RenderGroup(*renderables, fit=fit) @@ -451,7 +483,7 @@ def _is_jupyter() -> bool: # pragma: no cover except NameError: return False ipython = get_ipython() # type: ignore - shell = ipython.__class__.__name__ # type: ignore + shell = ipython.__class__.__name__ if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell": return True # Jupyter notebook or qtconsole elif shell == "TerminalInteractiveShell": @@ -520,7 +552,7 @@ def detect_legacy_windows() -> bool: if detect_legacy_windows(): # pragma: no cover from colorama import init - init() + init(strip=False) class Console: @@ -566,18 +598,18 @@ def __init__( color_system: Optional[ Literal["auto", "standard", "256", "truecolor", "windows"] ] = "auto", - force_terminal: bool = None, - force_jupyter: bool = None, - force_interactive: bool = None, + force_terminal: Optional[bool] = None, + force_jupyter: Optional[bool] = None, + force_interactive: Optional[bool] = None, soft_wrap: bool = False, - theme: Theme = None, + theme: Optional[Theme] = None, stderr: bool = False, - file: IO[str] = None, + file: Optional[IO[str]] = None, quiet: bool = False, - width: int = None, - height: int = None, - style: StyleType = None, - no_color: bool = None, + width: Optional[int] = None, + height: Optional[int] = None, + style: Optional[StyleType] = None, + no_color: Optional[bool] = None, tab_size: int = 8, record: bool = False, markup: bool = True, @@ -587,11 +619,11 @@ def __init__( log_path: bool = True, log_time_format: Union[str, FormatTimeCallable] = "[%X]", highlighter: Optional["HighlighterType"] = ReprHighlighter(), - legacy_windows: bool = None, + legacy_windows: Optional[bool] = None, safe_box: bool = True, - get_datetime: Callable[[], datetime] = None, - get_time: Callable[[], float] = None, - _environ: Mapping[str, str] = None, + get_datetime: Optional[Callable[[], datetime]] = None, + get_time: Optional[Callable[[], float]] = None, + _environ: Optional[Mapping[str, str]] = None, ): # Copy of os.environ allows us to replace it for testing if _environ is not None: @@ -771,7 +803,7 @@ def __enter__(self) -> "Console": self._enter_buffer() return self - def __exit__(self, exc_type, exc_value, traceback) -> None: + def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: """Exit buffer context.""" self._exit_buffer() @@ -849,7 +881,7 @@ def is_terminal(self) -> bool: """ if self._force_terminal is not None: return self._force_terminal - isatty = getattr(self.file, "isatty", None) + isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None) return False if isatty is None else isatty() @property @@ -953,7 +985,7 @@ def capture(self) -> Capture: return capture def pager( - self, pager: Pager = None, styles: bool = False, links: bool = False + self, pager: Optional[Pager] = None, styles: bool = False, links: bool = False ) -> PagerContext: """A context manager to display anything printed within a "pager". The pager application is defined by the system and will typically support at least pressing a key to scroll. @@ -1072,7 +1104,7 @@ def is_alt_screen(self) -> bool: return self._is_alt_screen def screen( - self, hide_cursor: bool = True, style: StyleType = None + self, hide_cursor: bool = True, style: Optional[StyleType] = None ) -> "ScreenContext": """Context manager to enable and disable 'alternative screen' mode. @@ -1085,8 +1117,25 @@ def screen( """ return ScreenContext(self, hide_cursor=hide_cursor, style=style or "") + def measure( + self, renderable: RenderableType, *, options: Optional[ConsoleOptions] = None + ) -> Measurement: + """Measure a renderable. Returns a :class:`~rich.measure.Measurement` object which contains + information regarding the number of characters required to print the renderable. + + Args: + renderable (RenderableType): Any renderable or string. + options (Optional[ConsoleOptions], optional): Options to use when measuring, or None + to use default options. Defaults to None. + + Returns: + Measurement: A measurement of the renderable. + """ + measurement = Measurement.get(self, options or self.options, renderable) + return measurement + def render( - self, renderable: RenderableType, options: ConsoleOptions = None + self, renderable: RenderableType, options: Optional[ConsoleOptions] = None ) -> Iterable[Segment]: """Render an object in to an iterable of `Segment` instances. @@ -1115,7 +1164,7 @@ def render( text_renderable = self.render_str( renderable, highlight=_options.highlight, markup=_options.markup ) - render_iterable = text_renderable.__rich_console__(self, _options) # type: ignore + render_iterable = text_renderable.__rich_console__(self, _options) else: raise errors.NotRenderableError( f"Unable to render {renderable!r}; " @@ -1193,12 +1242,12 @@ def render_str( text: str, *, style: Union[str, Style] = "", - justify: JustifyMethod = None, - overflow: OverflowMethod = None, - emoji: bool = None, - markup: bool = None, - highlight: bool = None, - highlighter: HighlighterType = None, + justify: Optional[JustifyMethod] = None, + overflow: Optional[OverflowMethod] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + highlighter: Optional[HighlighterType] = None, ) -> "Text": """Convert a string to a Text instance. This is is called automatically if you print or log a string. @@ -1241,7 +1290,7 @@ def render_str( return rich_text def get_style( - self, name: Union[str, Style], *, default: Union[Style, str] = None + self, name: Union[str, Style], *, default: Optional[Union[Style, str]] = None ) -> Style: """Get a Style instance by it's theme name or parse a definition. @@ -1276,10 +1325,10 @@ def _collect_renderables( sep: str, end: str, *, - justify: JustifyMethod = None, - emoji: bool = None, - markup: bool = None, - highlight: bool = None, + justify: Optional[JustifyMethod] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, ) -> List[ConsoleRenderable]: """Combine a number of renderables and text into one renderable. @@ -1386,10 +1435,10 @@ def control(self, *control: Control) -> None: def out( self, *objects: Any, - sep=" ", - end="\n", - style: Union[str, Style] = None, - highlight: bool = None, + sep: str = " ", + end: str = "\n", + style: Optional[Union[str, Style]] = None, + highlight: Optional[bool] = None, ) -> None: """Output to the terminal. This is a low-level way of writing to the terminal which unlike :meth:`~rich.console.Console.print` won't pretty print, wrap text, or apply markup, but will @@ -1418,19 +1467,19 @@ def out( def print( self, *objects: Any, - sep=" ", - end="\n", - style: Union[str, Style] = None, - justify: JustifyMethod = None, - overflow: OverflowMethod = None, - no_wrap: bool = None, - emoji: bool = None, - markup: bool = None, - highlight: bool = None, - width: int = None, - height: int = None, + sep: str = " ", + end: str = "\n", + style: Optional[Union[str, Style]] = None, + justify: Optional[JustifyMethod] = None, + overflow: Optional[OverflowMethod] = None, + no_wrap: Optional[bool] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, + width: Optional[int] = None, + height: Optional[int] = None, crop: bool = True, - soft_wrap: bool = None, + soft_wrap: Optional[bool] = None, ) -> None: """Print to the console. @@ -1509,8 +1558,8 @@ def update_screen( self, renderable: RenderableType, *, - region: Region = None, - options: ConsoleOptions = None, + region: Optional[Region] = None, + options: Optional[ConsoleOptions] = None, ) -> None: """Update the screen at a given offset. @@ -1539,7 +1588,9 @@ def update_screen( lines = self.render_lines(renderable, options=render_options) self.update_screen_lines(lines, x, y) - def update_screen_lines(self, lines: List[List[Segment]], x: int = 0, y: int = 0): + def update_screen_lines( + self, lines: List[List[Segment]], x: int = 0, y: int = 0 + ) -> None: """Update lines of the screen at a given offset. Args: @@ -1589,15 +1640,15 @@ def print_exception( def log( self, *objects: Any, - sep=" ", - end="\n", - style: Union[str, Style] = None, - justify: JustifyMethod = None, - emoji: bool = None, - markup: bool = None, - highlight: bool = None, + sep: str = " ", + end: str = "\n", + style: Optional[Union[str, Style]] = None, + justify: Optional[JustifyMethod] = None, + emoji: Optional[bool] = None, + markup: Optional[bool] = None, + highlight: Optional[bool] = None, log_locals: bool = False, - _stack_offset=1, + _stack_offset: int = 1, ) -> None: """Log rich content to the terminal. @@ -1734,7 +1785,7 @@ def input( markup: bool = True, emoji: bool = True, password: bool = False, - stream: TextIO = None, + stream: Optional[TextIO] = None, ) -> str: """Displays a prompt and waits for input from the user. The prompt may contain color / style. @@ -1816,9 +1867,9 @@ def save_text(self, path: str, *, clear: bool = True, styles: bool = False) -> N def export_html( self, *, - theme: TerminalTheme = None, + theme: Optional[TerminalTheme] = None, clear: bool = True, - code_format: str = None, + code_format: Optional[str] = None, inline_styles: bool = False, ) -> str: """Generate HTML from console contents (requires record=True argument in constructor). @@ -1896,9 +1947,9 @@ def save_html( self, path: str, *, - theme: TerminalTheme = None, + theme: Optional[TerminalTheme] = None, clear: bool = True, - code_format=CONSOLE_HTML_FORMAT, + code_format: str = CONSOLE_HTML_FORMAT, inline_styles: bool = False, ) -> None: """Generate HTML from console contents and write to a file (requires record=True argument in constructor). diff --git a/rich/containers.py b/rich/containers.py index 0f13fb810..8fbacf75b 100644 --- a/rich/containers.py +++ b/rich/containers.py @@ -3,6 +3,9 @@ Iterator, Iterable, List, + Optional, + Union, + cast, overload, TypeVar, TYPE_CHECKING, @@ -28,7 +31,9 @@ class Renderables: """A list subclass which renders its contents to the console.""" - def __init__(self, renderables: Iterable["RenderableType"] = None) -> None: + def __init__( + self, renderables: Optional[Iterable["RenderableType"]] = None + ) -> None: self._renderables: List["RenderableType"] = ( list(renderables) if renderables is not None else [] ) @@ -76,10 +81,10 @@ def __getitem__(self, index: int) -> "Text": ... @overload - def __getitem__(self, index: slice) -> "Lines": + def __getitem__(self, index: slice) -> List["Text"]: ... - def __getitem__(self, index): + def __getitem__(self, index: Union[slice, int]) -> Union["Text", List["Text"]]: return self._lines[index] def __setitem__(self, index: int, value: "Text") -> "Lines": @@ -101,7 +106,7 @@ def append(self, line: "Text") -> None: def extend(self, lines: Iterable["Text"]) -> None: self._lines.extend(lines) - def pop(self, index=-1) -> "Text": + def pop(self, index: int = -1) -> "Text": return self._lines.pop(index) def justify( diff --git a/rich/control.py b/rich/control.py index 8a6236437..0a13b8ac5 100644 --- a/rich/control.py +++ b/rich/control.py @@ -1,4 +1,4 @@ -from typing import Callable, Dict, Iterable, List, TYPE_CHECKING, Union +from typing import Any, Callable, Dict, Iterable, List, TYPE_CHECKING, Union from .segment import ControlCode, ControlType, Segment @@ -14,7 +14,7 @@ _CONTROL_TRANSLATE = {_codepoint: None for _codepoint in STRIP_CONTROL_CODES} -CONTROL_CODES_FORMAT: Dict[int, Callable] = { +CONTROL_CODES_FORMAT: Dict[int, Callable[..., str]] = { ControlType.BELL: lambda: "\x07", ControlType.CARRIAGE_RETURN: lambda: "\r", ControlType.HOME: lambda: "\x1b[H", @@ -157,7 +157,9 @@ def __rich_console__( yield self.segment -def strip_control_codes(text: str, _translate_table=_CONTROL_TRANSLATE) -> str: +def strip_control_codes( + text: str, _translate_table: Dict[int, None] = _CONTROL_TRANSLATE +) -> str: """Remove control codes from text. Args: diff --git a/rich/default_styles.py b/rich/default_styles.py index 1e87d6aea..6ffe4a7c0 100644 --- a/rich/default_styles.py +++ b/rich/default_styles.py @@ -2,6 +2,7 @@ from .style import Style + DEFAULT_STYLES: Dict[str, Style] = { "none": Style.null(), "reset": Style( @@ -82,6 +83,9 @@ "repr.none": Style(color="magenta", italic=True), "repr.url": Style(underline=True, color="bright_blue", italic=False, bold=False), "repr.uuid": Style(color="bright_yellow", bold=False), + "repr.call": Style(color="magenta", bold=True), + "repr.path": Style(color="magenta"), + "repr.filename": Style(color="bright_magenta"), "rule.line": Style(color="bright_green"), "rule.text": Style.null(), "prompt": Style.null(), @@ -94,8 +98,6 @@ "scope.key": Style(color="yellow", italic=True), "scope.key.special": Style(color="yellow", italic=True, dim=True), "scope.equals": Style(color="red"), - "repr.path": Style(color="magenta"), - "repr.filename": Style(color="bright_magenta"), "table.header": Style(bold=True), "table.footer": Style(bold=True), "table.cell": Style.null(), @@ -125,9 +127,6 @@ "status.spinner": Style(color="green"), "tree": Style(), "tree.line": Style(), -} - -MARKDOWN_STYLES = { "markdown.paragraph": Style(), "markdown.text": Style(), "markdown.emph": Style(italic=True), @@ -151,6 +150,3 @@ "markdown.link": Style(color="bright_blue"), "markdown.link_url": Style(color="blue"), } - - -DEFAULT_STYLES.update(MARKDOWN_STYLES) diff --git a/rich/highlighter.py b/rich/highlighter.py index c2ee7cc76..59eae5858 100644 --- a/rich/highlighter.py +++ b/rich/highlighter.py @@ -83,13 +83,14 @@ class ReprHighlighter(RegexHighlighter): highlights = [ r"(?P\<)(?P[\w\-\.\:]*)(?P.*?)(?P\>)", r"(?P[\w_]{1,50})=(?P\"?[\w_]+\"?)?", + r"(?P[\{\[\(\)\]\}])", _combine_regex( r"(?P[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})", r"(?P([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})", r"(?P(?:[0-9A-Fa-f]{1,2}-){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){3}[0-9A-Fa-f]{4})", r"(?P(?:[0-9A-Fa-f]{1,2}-){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})", - r"(?P[\{\[\(\)\]\}])", - r"(?PTrue)|(?PFalse)|(?PNone)", + r"(?P[\w\.]*?)\(", + r"\b(?PTrue)\b|\b(?PFalse)\b|\b(?PNone)\b", r"(?P\.\.\.)", r"(?P(?\B(\/[\w\.\-\_\+]+)*\/)(?P[\w\.\-\_\+]*)?", diff --git a/rich/jupyter.py b/rich/jupyter.py index c4d665690..cc0279797 100644 --- a/rich/jupyter.py +++ b/rich/jupyter.py @@ -1,4 +1,4 @@ -from typing import Iterable, List +from typing import Any, Dict, Iterable, List from . import get_console from .segment import Segment @@ -17,7 +17,9 @@ def __init__(self, html: str, text: str) -> None: self.html = html self.text = text - def _repr_mimebundle_(self, include, exclude, **kwargs): + def _repr_mimebundle_( + self, include: Iterable[str], exclude: Iterable[str], **kwargs: Any + ) -> Dict[str, str]: data = {"text/plain": self.text, "text/html": self.html} if include: data = {k: v for (k, v) in data.items() if k in include} @@ -29,7 +31,9 @@ def _repr_mimebundle_(self, include, exclude, **kwargs): class JupyterMixin: """Add to an Rich renderable to make it render in Jupyter notebook.""" - def _repr_mimebundle_(self, include, exclude, **kwargs): + def _repr_mimebundle_( + self, include: Iterable[str], exclude: Iterable[str], **kwargs: Any + ) -> Dict[str, str]: console = get_console() segments = list(console.render(self, console.options)) # type: ignore html = _render_segments(segments) @@ -76,7 +80,7 @@ def display(segments: Iterable[Segment], text: str) -> None: ipython_display(jupyter_renderable) -def print(*args, **kwargs) -> None: +def print(*args: Any, **kwargs: Any) -> None: """Proxy for Console print.""" console = get_console() return console.print(*args, **kwargs) diff --git a/rich/layout.py b/rich/layout.py index 21057828e..5b91214aa 100644 --- a/rich/layout.py +++ b/rich/layout.py @@ -154,14 +154,14 @@ class Layout: def __init__( self, - renderable: RenderableType = None, + renderable: Optional[RenderableType] = None, *, - name: str = None, - size: int = None, + name: Optional[str] = None, + size: Optional[int] = None, minimum_size: int = 1, ratio: int = 1, visible: bool = True, - height: int = None, + height: Optional[int] = None, ) -> None: self._renderable = renderable or _Placeholder(self) self.size = size @@ -222,7 +222,7 @@ def tree(self) -> "Tree": from rich.table import Table from rich.tree import Tree - def summary(layout) -> Table: + def summary(layout: "Layout") -> Table: icon = layout.splitter.get_tree_icon() @@ -412,7 +412,7 @@ def __rich_console__( yield new_line -if __name__ == "__main__": # type: ignore +if __name__ == "__main__": from rich.console import Console from rich.panel import Panel diff --git a/rich/live.py b/rich/live.py index 428228f64..f2ba64a7e 100644 --- a/rich/live.py +++ b/rich/live.py @@ -1,6 +1,7 @@ import sys from threading import Event, RLock, Thread -from typing import IO, Any, Callable, List, Optional +from types import TracebackType +from typing import IO, Any, Callable, List, Optional, TextIO, Type, cast from . import get_console from .console import Console, ConsoleRenderable, RenderableType, RenderHook @@ -49,9 +50,9 @@ class Live(JupyterMixin, RenderHook): def __init__( self, - renderable: RenderableType = None, + renderable: Optional[RenderableType] = None, *, - console: Console = None, + console: Optional[Console] = None, screen: bool = False, auto_refresh: bool = True, refresh_per_second: float = 4, @@ -59,7 +60,7 @@ def __init__( redirect_stdout: bool = True, redirect_stderr: bool = True, vertical_overflow: VerticalOverflowMethod = "ellipsis", - get_renderable: Callable[[], RenderableType] = None, + get_renderable: Optional[Callable[[], RenderableType]] = None, ) -> None: assert refresh_per_second > 0, "refresh_per_second must be > 0" self._renderable = renderable @@ -100,7 +101,7 @@ def get_renderable(self) -> RenderableType: ) return renderable or "" - def start(self, refresh=False) -> None: + def start(self, refresh: bool = False) -> None: """Start live rendering display. Args: @@ -163,26 +164,31 @@ def __enter__(self) -> "Live": self.start(refresh=self._renderable is not None) return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: self.stop() - def _enable_redirect_io(self): + def _enable_redirect_io(self) -> None: """Enable redirecting of stdout / stderr.""" if self.console.is_terminal: - if self._redirect_stdout and not isinstance(sys.stdout, FileProxy): # type: ignore + if self._redirect_stdout and not isinstance(sys.stdout, FileProxy): self._restore_stdout = sys.stdout - sys.stdout = FileProxy(self.console, sys.stdout) - if self._redirect_stderr and not isinstance(sys.stderr, FileProxy): # type: ignore + sys.stdout = cast("TextIO", FileProxy(self.console, sys.stdout)) + if self._redirect_stderr and not isinstance(sys.stderr, FileProxy): self._restore_stderr = sys.stderr - sys.stderr = FileProxy(self.console, sys.stderr) + sys.stderr = cast("TextIO", FileProxy(self.console, sys.stderr)) - def _disable_redirect_io(self): + def _disable_redirect_io(self) -> None: """Disable redirecting of stdout / stderr.""" if self._restore_stdout: - sys.stdout = self._restore_stdout + sys.stdout = cast("TextIO", self._restore_stdout) self._restore_stdout = None if self._restore_stderr: - sys.stderr = self._restore_stderr + sys.stderr = cast("TextIO", self._restore_stderr) self._restore_stderr = None @property diff --git a/rich/live_render.py b/rich/live_render.py index 4294b9923..63b6c5cf6 100644 --- a/rich/live_render.py +++ b/rich/live_render.py @@ -1,7 +1,11 @@ -from threading import RLock +import sys from typing import Optional, Tuple -from typing_extensions import Literal +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + from ._loop import loop_last from .console import Console, ConsoleOptions, RenderableType, RenderResult diff --git a/rich/logging.py b/rich/logging.py index 04b466003..87b648b69 100644 --- a/rich/logging.py +++ b/rich/logging.py @@ -58,14 +58,14 @@ class RichHandler(Handler): def __init__( self, level: Union[int, str] = logging.NOTSET, - console: Console = None, + console: Optional[Console] = None, *, show_time: bool = True, omit_repeated_times: bool = True, show_level: bool = True, show_path: bool = True, enable_link_path: bool = True, - highlighter: Highlighter = None, + highlighter: Optional[Highlighter] = None, markup: bool = False, rich_tracebacks: bool = False, tracebacks_width: Optional[int] = None, @@ -247,7 +247,7 @@ def render( log.info("POST /admin/ 401 42234") log.warning("password was rejected for admin site.") - def divide(): + def divide() -> None: number = 1 divisor = 0 foos = ["foo"] * 100 diff --git a/rich/markdown.py b/rich/markdown.py index 23c6124d6..ec3656921 100644 --- a/rich/markdown.py +++ b/rich/markdown.py @@ -5,11 +5,12 @@ from . import box from ._loop import loop_first from ._stack import Stack -from .console import Console, ConsoleOptions, JustifyMethod, RenderResult, Segment +from .console import Console, ConsoleOptions, JustifyMethod, RenderResult from .containers import Renderables from .jupyter import JupyterMixin from .panel import Panel from .rule import Rule +from .segment import Segment from .style import Style, StyleStack from .syntax import Syntax from .text import Text, TextType @@ -32,7 +33,7 @@ def create(cls, markdown: "Markdown", node: Any) -> "MarkdownElement": """ return cls() - def on_enter(self, context: "MarkdownContext"): + def on_enter(self, context: "MarkdownContext") -> None: """Called when the node is entered. Args: @@ -107,7 +108,7 @@ class Paragraph(TextElement): justify: JustifyMethod @classmethod - def create(cls, markdown: "Markdown", node) -> "Paragraph": + def create(cls, markdown: "Markdown", node: MarkdownElement) -> "Paragraph": return cls(justify=markdown.justify or "left") def __init__(self, justify: JustifyMethod) -> None: @@ -348,7 +349,7 @@ def __init__( console: Console, options: ConsoleOptions, style: Style, - inline_code_lexer: str = None, + inline_code_lexer: Optional[str] = None, inline_code_theme: str = "monokai", ) -> None: self.console = console @@ -419,17 +420,17 @@ def __init__( self, markup: str, code_theme: str = "monokai", - justify: JustifyMethod = None, + justify: Optional[JustifyMethod] = None, style: Union[str, Style] = "none", hyperlinks: bool = True, - inline_code_lexer: str = None, - inline_code_theme: str = None, + inline_code_lexer: Optional[str] = None, + inline_code_theme: Optional[str] = None, ) -> None: self.markup = markup parser = Parser() self.parsed = parser.parse(markup) self.code_theme = code_theme - self.justify = justify + self.justify: Optional[JustifyMethod] = justify self.style = style self.hyperlinks = hyperlinks self.inline_code_lexer = inline_code_lexer diff --git a/rich/markup.py b/rich/markup.py index 57ed0eaf7..179cabc57 100644 --- a/rich/markup.py +++ b/rich/markup.py @@ -1,5 +1,5 @@ import re -from typing import Iterable, List, Match, NamedTuple, Optional, Tuple, Union +from typing import Callable, Iterable, List, Match, NamedTuple, Optional, Tuple, Union from .errors import MarkupError from .style import Style @@ -36,7 +36,14 @@ def markup(self) -> str: ) -def escape(markup: str, _escape=re.compile(r"(\\*)(\[[a-z#\/].*?\])").sub) -> str: +_ReStringMatch = Match[str] # regex match object +_ReSubCallable = Callable[[_ReStringMatch], str] # Callable invoked by re.sub +_EscapeSubMethod = Callable[[_ReSubCallable, str], str] # Sub method of a compiled re + + +def escape( + markup: str, _escape: _EscapeSubMethod = re.compile(r"(\\*)(\[[a-z#\/].*?\])").sub +) -> str: """Escapes text so that it won't be interpreted as markup. Args: diff --git a/rich/measure.py b/rich/measure.py index cd1d3e12f..4382184f6 100644 --- a/rich/measure.py +++ b/rich/measure.py @@ -1,5 +1,5 @@ from operator import itemgetter -from typing import Iterable, NamedTuple, TYPE_CHECKING +from typing import Callable, Iterable, NamedTuple, Optional, TYPE_CHECKING from . import errors from .protocol import is_renderable @@ -56,7 +56,9 @@ def with_minimum(self, width: int) -> "Measurement": width = max(0, width) return Measurement(max(minimum, width), max(maximum, width)) - def clamp(self, min_width: int = None, max_width: int = None) -> "Measurement": + def clamp( + self, min_width: Optional[int] = None, max_width: Optional[int] = None + ) -> "Measurement": """Clamp a measurement within the specified range. Args: @@ -98,7 +100,9 @@ def get( if hasattr(renderable, "__rich__"): renderable = renderable.__rich__() # type: ignore if is_renderable(renderable): - get_console_width = getattr(renderable, "__rich_measure__", None) + get_console_width: Callable[ + ["Console", "ConsoleOptions"], "Measurement" + ] = getattr(renderable, "__rich_measure__", None) if get_console_width is not None: render_width = ( get_console_width(console, options) diff --git a/rich/pager.py b/rich/pager.py index e226540a3..4290021f2 100644 --- a/rich/pager.py +++ b/rich/pager.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +from typing import Any, Callable class Pager(ABC): @@ -16,7 +17,9 @@ def show(self, content: str) -> None: class SystemPager(Pager): """Uses the pager installed on the system.""" - _pager = lambda self, content: __import__("pydoc").pager(content) + _pager: Callable[[Any, str], Any] = lambda self, content: __import__("pydoc").pager( + content + ) def show(self, content: str) -> None: """Use the same pager used by pydoc.""" diff --git a/rich/panel.py b/rich/panel.py index 71ca9ebc0..8fbaab0af 100644 --- a/rich/panel.py +++ b/rich/panel.py @@ -40,7 +40,7 @@ def __init__( renderable: "RenderableType", box: Box = ROUNDED, *, - title: TextType = None, + title: Optional[TextType] = None, title_align: AlignMethod = "center", safe_box: Optional[bool] = None, expand: bool = True, @@ -70,14 +70,14 @@ def fit( renderable: "RenderableType", box: Box = ROUNDED, *, - title: TextType = None, + title: Optional[TextType] = None, title_align: AlignMethod = "center", safe_box: Optional[bool] = None, style: StyleType = "none", border_style: StyleType = "none", width: Optional[int] = None, padding: PaddingDimensions = (0, 1), - ): + ) -> "Panel": """An alternative constructor that sets expand=False.""" return cls( renderable, @@ -123,7 +123,7 @@ def __rich_console__( else min(options.max_width, self.width) ) - safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box # type: ignore + safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box box = self.box.substitute(options, safe=safe_box) title_text = self._title @@ -133,8 +133,8 @@ def __rich_console__( child_width = ( width - 2 if self.expand - else Measurement.get( - console, options.update_width(width - 2), renderable + else console.measure( + renderable, options=options.update_width(width - 2) ).maximum ) child_height = self.height or options.height or None diff --git a/rich/pretty.py b/rich/pretty.py index 7903b2cc2..827faba55 100644 --- a/rich/pretty.py +++ b/rich/pretty.py @@ -2,10 +2,11 @@ import os import sys from array import array -from collections import Counter, defaultdict, deque +from collections import Counter, defaultdict, deque, UserDict, UserList from dataclasses import dataclass, fields, is_dataclass from itertools import islice from typing import ( + DefaultDict, TYPE_CHECKING, Any, Callable, @@ -17,9 +18,25 @@ Union, Tuple, ) +from types import MappingProxyType -from rich.highlighter import ReprHighlighter +try: + import attr as _attr_module +except ImportError: # pragma: no cover + _attr_module = None # type: ignore + +def _is_attr_object(obj: Any) -> bool: + """Check if an object was created with attrs module.""" + return _attr_module is not None and _attr_module.has(type(obj)) + + +def _get_attr_fields(obj: Any) -> Iterable["_attr_module.Attribute[Any]"]: + """Get fields for an attrs object.""" + return _attr_module.fields(type(obj)) if _attr_module is not None else [] + + +from .highlighter import ReprHighlighter from . import get_console from ._loop import loop_last from ._pick import pick_bool @@ -42,12 +59,12 @@ def install( - console: "Console" = None, + console: Optional["Console"] = None, overflow: "OverflowMethod" = "ignore", crop: bool = False, indent_guides: bool = False, - max_length: int = None, - max_string: int = None, + max_length: Optional[int] = None, + max_string: Optional[int] = None, expand_all: bool = False, ) -> None: """Install automatic pretty printing in the Python REPL. @@ -154,15 +171,15 @@ class Pretty(JupyterMixin): def __init__( self, _object: Any, - highlighter: "HighlighterType" = None, + highlighter: Optional["HighlighterType"] = None, *, indent_size: int = 4, - justify: "JustifyMethod" = None, + justify: Optional["JustifyMethod"] = None, overflow: Optional["OverflowMethod"] = None, no_wrap: Optional[bool] = False, indent_guides: bool = False, - max_length: int = None, - max_string: int = None, + max_length: Optional[int] = None, + max_string: Optional[int] = None, expand_all: bool = False, margin: int = 0, insert_line: bool = False, @@ -230,7 +247,7 @@ def __rich_measure__( return Measurement(text_width, text_width) -def _get_braces_for_defaultdict(_object: defaultdict) -> Tuple[str, str, str]: +def _get_braces_for_defaultdict(_object: DefaultDict[Any, Any]) -> Tuple[str, str, str]: return ( f"defaultdict({_object.default_factory!r}, {{", "})", @@ -238,7 +255,7 @@ def _get_braces_for_defaultdict(_object: defaultdict) -> Tuple[str, str, str]: ) -def _get_braces_for_array(_object: array) -> Tuple[str, str, str]: +def _get_braces_for_array(_object: "array[Any]") -> Tuple[str, str, str]: return (f"array({_object.typecode!r}, [", "])", "array({_object.typecode!r})") @@ -249,13 +266,16 @@ def _get_braces_for_array(_object: array) -> Tuple[str, str, str]: Counter: lambda _object: ("Counter({", "})", "Counter()"), deque: lambda _object: ("deque([", "])", "deque()"), dict: lambda _object: ("{", "}", "{}"), + UserDict: lambda _object: ("{", "}", "{}"), frozenset: lambda _object: ("frozenset({", "})", "frozenset()"), list: lambda _object: ("[", "]", "[]"), + UserList: lambda _object: ("[", "]", "[]"), set: lambda _object: ("{", "}", "set()"), tuple: lambda _object: ("(", ")", "()"), + MappingProxyType: lambda _object: ("mappingproxy({", "})", "mappingproxy({})"), } _CONTAINERS = tuple(_BRACES.keys()) -_MAPPING_CONTAINERS = (dict, os._Environ) +_MAPPING_CONTAINERS = (dict, os._Environ, MappingProxyType, UserDict) def is_expandable(obj: Any) -> bool: @@ -264,6 +284,7 @@ def is_expandable(obj: Any) -> bool: isinstance(obj, _CONTAINERS) or (is_dataclass(obj) and not isinstance(obj, type)) or hasattr(obj, "__rich_repr__") + or _is_attr_object(obj) ) @@ -413,7 +434,9 @@ def __str__(self) -> str: return f"{self.whitespace}{self.text}{self.node or ''}{self.suffix}" -def traverse(_object: Any, max_length: int = None, max_string: int = None) -> Node: +def traverse( + _object: Any, max_length: Optional[int] = None, max_string: Optional[int] = None +) -> Node: """Traverse object and generate a tree. Args: @@ -453,7 +476,7 @@ def _traverse(obj: Any, root: bool = False) -> Node: py_version = (sys.version_info.major, sys.version_info.minor) children: List[Node] - def iter_rich_args(rich_args) -> Iterable[Union[Any, Tuple[str, Any]]]: + def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]: for arg in rich_args: if isinstance(arg, tuple): if len(arg) == 3: @@ -487,7 +510,6 @@ def iter_rich_args(rich_args) -> Iterable[Union[Any, Tuple[str, Any]]]: child_node = _traverse(child) child_node.last = last child_node.key_repr = key - child_node.last = last child_node.key_separator = "=" append(child_node) else: @@ -498,6 +520,50 @@ def iter_rich_args(rich_args) -> Iterable[Union[Any, Tuple[str, Any]]]: node = Node( value_repr=f"{obj.__class__.__name__}()", children=[], last=root ) + elif _is_attr_object(obj): + children = [] + append = children.append + + attr_fields = _get_attr_fields(obj) + if attr_fields: + node = Node( + open_brace=f"{obj.__class__.__name__}(", + close_brace=")", + children=children, + last=root, + ) + + def iter_attrs() -> Iterable[ + Tuple[str, Any, Optional[Callable[[Any], str]]] + ]: + """Iterate over attr fields and values.""" + for attr in attr_fields: + if attr.repr: + try: + value = getattr(obj, attr.name) + except Exception as error: + # Can happen, albeit rarely + yield (attr.name, error, None) + else: + yield ( + attr.name, + value, + attr.repr if callable(attr.repr) else None, + ) + + for last, (name, value, repr_callable) in loop_last(iter_attrs()): + if repr_callable: + child_node = Node(value_repr=str(repr_callable(value))) + else: + child_node = _traverse(value) + child_node.last = last + child_node.key_repr = name + child_node.key_separator = "=" + append(child_node) + else: + node = Node( + value_repr=f"{obj.__class__.__name__}()", children=[], last=root + ) elif ( is_dataclass(obj) @@ -531,7 +597,12 @@ def iter_rich_args(rich_args) -> Iterable[Union[Any, Tuple[str, Any]]]: pop_visited(obj_id) - elif obj_type in _CONTAINERS: + elif isinstance(obj, _CONTAINERS): + for container_type in _CONTAINERS: + if isinstance(obj, container_type): + obj_type = container_type + break + obj_id = id(obj) if obj_id in visited_ids: # Recursion detected @@ -540,7 +611,9 @@ def iter_rich_args(rich_args) -> Iterable[Union[Any, Tuple[str, Any]]]: open_brace, close_brace, empty = _BRACES[obj_type](obj) - if obj: + if obj_type.__repr__ != type(obj).__repr__: + node = Node(value_repr=to_repr(obj), last=root) + elif obj: children = [] node = Node( open_brace=open_brace, @@ -589,8 +662,8 @@ def pretty_repr( *, max_width: int = 80, indent_size: int = 4, - max_length: int = None, - max_string: int = None, + max_length: Optional[int] = None, + max_string: Optional[int] = None, expand_all: bool = False, ) -> str: """Prettify repr string by expanding on to new lines to fit within a given width. @@ -622,12 +695,12 @@ def pretty_repr( def pprint( _object: Any, *, - console: "Console" = None, + console: Optional["Console"] = None, indent_guides: bool = True, - max_length: int = None, - max_string: int = None, + max_length: Optional[int] = None, + max_string: Optional[int] = None, expand_all: bool = False, -): +) -> None: """A convenience function for pretty printing. Args: @@ -656,8 +729,9 @@ def pprint( if __name__ == "__main__": # pragma: no cover class BrokenRepr: - def __repr__(self): + def __repr__(self) -> str: 1 / 0 + return "this will fail" d = defaultdict(int) d["foo"] = 5 diff --git a/rich/progress.py b/rich/progress.py index 12545ebf1..e67173d3f 100644 --- a/rich/progress.py +++ b/rich/progress.py @@ -6,6 +6,7 @@ from datetime import timedelta from math import ceil from threading import Event, RLock, Thread +from types import TracebackType from typing import ( Any, Callable, @@ -18,19 +19,15 @@ Optional, Sequence, Tuple, + Type, TypeVar, Union, ) from . import filesize, get_console -from .console import ( - Console, - JustifyMethod, - RenderableType, - RenderGroup, -) -from .jupyter import JupyterMixin +from .console import Console, JustifyMethod, RenderableType, RenderGroup from .highlighter import Highlighter +from .jupyter import JupyterMixin from .live import Live from .progress_bar import ProgressBar from .spinner import Spinner @@ -75,19 +72,24 @@ def __enter__(self) -> "_TrackThread": self.start() return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: self.done.set() self.join() def track( sequence: Union[Sequence[ProgressType], Iterable[ProgressType]], - description="Working...", + description: str = "Working...", total: Optional[float] = None, - auto_refresh=True, + auto_refresh: bool = True, console: Optional[Console] = None, transient: bool = False, - get_time: Callable[[], float] = None, + get_time: Optional[Callable[[], float]] = None, refresh_per_second: float = 10, style: StyleType = "bar.back", complete_style: StyleType = "bar.complete", @@ -153,7 +155,7 @@ class ProgressColumn(ABC): max_refresh: Optional[float] = None - def __init__(self, table_column: Column = None) -> None: + def __init__(self, table_column: Optional[Column] = None) -> None: self._table_column = table_column self._renderable_cache: Dict[TaskID, Tuple[float, RenderableType]] = {} self._update_time: Optional[float] = None @@ -171,7 +173,7 @@ def __call__(self, task: "Task") -> RenderableType: Returns: RenderableType: Anything renderable (including str). """ - current_time = task.get_time() # type: ignore + current_time = task.get_time() if self.max_refresh is not None and not task.completed: try: timestamp, renderable = self._renderable_cache[task.id] @@ -197,7 +199,9 @@ class RenderableColumn(ProgressColumn): renderable (RenderableType, optional): Any renderable. Defaults to empty string. """ - def __init__(self, renderable: RenderableType = "", *, table_column: Column = None): + def __init__( + self, renderable: RenderableType = "", *, table_column: Optional[Column] = None + ): self.renderable = renderable super().__init__(table_column=table_column) @@ -221,7 +225,7 @@ def __init__( style: Optional[StyleType] = "progress.spinner", speed: float = 1.0, finished_text: TextType = " ", - table_column: Column = None, + table_column: Optional[Column] = None, ): self.spinner = Spinner(spinner_name, style=style, speed=speed) self.finished_text = ( @@ -236,7 +240,7 @@ def set_spinner( spinner_name: str, spinner_style: Optional[StyleType] = "progress.spinner", speed: float = 1.0, - ): + ) -> None: """Set a new spinner. Args: @@ -246,7 +250,7 @@ def set_spinner( """ self.spinner = Spinner(spinner_name, style=spinner_style, speed=speed) - def render(self, task: "Task") -> Text: + def render(self, task: "Task") -> RenderableType: text = ( self.finished_text if task.finished @@ -264,8 +268,8 @@ def __init__( style: StyleType = "none", justify: JustifyMethod = "left", markup: bool = True, - highlighter: Highlighter = None, - table_column: Column = None, + highlighter: Optional[Highlighter] = None, + table_column: Optional[Column] = None, ) -> None: self.text_format = text_format self.justify = justify @@ -303,7 +307,7 @@ def __init__( complete_style: StyleType = "bar.complete", finished_style: StyleType = "bar.finished", pulse_style: StyleType = "bar.pulse", - table_column: Column = None, + table_column: Optional[Column] = None, ) -> None: self.bar_width = bar_width self.style = style @@ -379,7 +383,9 @@ class DownloadColumn(ProgressColumn): binary_units (bool, optional): Use binary units, KiB, MiB etc. Defaults to False. """ - def __init__(self, binary_units: bool = False, table_column: Column = None) -> None: + def __init__( + self, binary_units: bool = False, table_column: Optional[Column] = None + ) -> None: self.binary_units = binary_units super().__init__(table_column=table_column) @@ -568,19 +574,19 @@ class Progress(JupyterMixin): def __init__( self, *columns: Union[str, ProgressColumn], - console: Console = None, + console: Optional[Console] = None, auto_refresh: bool = True, refresh_per_second: float = 10, speed_estimate_period: float = 30.0, transient: bool = False, redirect_stdout: bool = True, redirect_stderr: bool = True, - get_time: GetTimeCallable = None, + get_time: Optional[GetTimeCallable] = None, disable: bool = False, expand: bool = False, ) -> None: assert ( - refresh_per_second is None or refresh_per_second > 0 # type: ignore + refresh_per_second is None or refresh_per_second > 0 ), "refresh_per_second must be > 0" self._lock = RLock() self.columns = columns or ( @@ -645,7 +651,12 @@ def __enter__(self) -> "Progress": self.start() return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: self.stop() def track( @@ -653,7 +664,7 @@ def track( sequence: Union[Iterable[ProgressType], Sequence[ProgressType]], total: Optional[float] = None, task_id: Optional[TaskID] = None, - description="Working...", + description: str = "Working...", update_period: float = 0.1, ) -> Iterable[ProgressType]: """Track progress by iterating over a sequence. @@ -731,10 +742,10 @@ def update( task_id: TaskID, *, total: Optional[float] = None, - completed: float = None, - advance: float = None, - description: str = None, - visible: bool = None, + completed: Optional[float] = None, + advance: Optional[float] = None, + description: Optional[str] = None, + visible: Optional[bool] = None, refresh: bool = False, **fields: Any, ) -> None: diff --git a/rich/progress_bar.py b/rich/progress_bar.py index bf4f3b532..1797b5f78 100644 --- a/rich/progress_bar.py +++ b/rich/progress_bar.py @@ -34,13 +34,13 @@ def __init__( self, total: float = 100.0, completed: float = 0, - width: int = None, + width: Optional[int] = None, pulse: bool = False, style: StyleType = "bar.back", complete_style: StyleType = "bar.complete", finished_style: StyleType = "bar.finished", pulse_style: StyleType = "bar.pulse", - animation_time: float = None, + animation_time: Optional[float] = None, ): self.total = total self.completed = completed @@ -111,7 +111,7 @@ def _get_pulse_segments( append(_Segment(bar, _Style(color=from_triplet(color)))) return segments - def update(self, completed: float, total: float = None) -> None: + def update(self, completed: float, total: Optional[float] = None) -> None: """Update progress with new values. Args: diff --git a/rich/prompt.py b/rich/prompt.py index 7db5d2db7..daf76df85 100644 --- a/rich/prompt.py +++ b/rich/prompt.py @@ -54,9 +54,9 @@ def __init__( self, prompt: TextType = "", *, - console: Console = None, + console: Optional[Console] = None, password: bool = False, - choices: List[str] = None, + choices: Optional[List[str]] = None, show_default: bool = True, show_choices: bool = True, ) -> None: @@ -78,13 +78,13 @@ def ask( cls, prompt: TextType = "", *, - console: Console = None, + console: Optional[Console] = None, password: bool = False, - choices: List[str] = None, + choices: Optional[List[str]] = None, show_default: bool = True, show_choices: bool = True, default: DefaultType, - stream: TextIO = None, + stream: Optional[TextIO] = None, ) -> Union[DefaultType, PromptType]: ... @@ -94,12 +94,12 @@ def ask( cls, prompt: TextType = "", *, - console: Console = None, + console: Optional[Console] = None, password: bool = False, - choices: List[str] = None, + choices: Optional[List[str]] = None, show_default: bool = True, show_choices: bool = True, - stream: TextIO = None, + stream: Optional[TextIO] = None, ) -> PromptType: ... @@ -108,13 +108,13 @@ def ask( cls, prompt: TextType = "", *, - console: Console = None, + console: Optional[Console] = None, password: bool = False, - choices: List[str] = None, + choices: Optional[List[str]] = None, show_default: bool = True, show_choices: bool = True, default: Any = ..., - stream: TextIO = None, + stream: Optional[TextIO] = None, ) -> Any: """Shortcut to construct and run a prompt loop and return the result. @@ -188,7 +188,7 @@ def get_input( console: Console, prompt: TextType, password: bool, - stream: TextIO = None, + stream: Optional[TextIO] = None, ) -> str: """Get input from user. @@ -235,7 +235,7 @@ def process_response(self, value: str) -> PromptType: if self.choices is not None and not self.check_choice(value): raise InvalidResponse(self.illegal_choice_message) - return return_value + return return_value # type: ignore def on_validate_error(self, value: str, error: InvalidResponse) -> None: """Called to handle validation error. @@ -250,16 +250,16 @@ def pre_prompt(self) -> None: """Hook to display something before the prompt.""" @overload - def __call__(self, *, stream: TextIO = None) -> PromptType: + def __call__(self, *, stream: Optional[TextIO] = None) -> PromptType: ... @overload def __call__( - self, *, default: DefaultType, stream: TextIO = None + self, *, default: DefaultType, stream: Optional[TextIO] = None ) -> Union[PromptType, DefaultType]: ... - def __call__(self, *, default: Any = ..., stream: TextIO = None) -> Any: + def __call__(self, *, default: Any = ..., stream: Optional[TextIO] = None) -> Any: """Run the prompt loop. Args: diff --git a/rich/repr.py b/rich/repr.py index 51566eb18..157090f72 100644 --- a/rich/repr.py +++ b/rich/repr.py @@ -10,7 +10,7 @@ def rich_repr(cls: Type[T]) -> Type[T]: """Class decorator to create __repr__ from __rich_repr__""" - def auto_repr(self) -> str: + def auto_repr(self: Any) -> str: repr_str: List[str] = [] append = repr_str.append for arg in self.__rich_repr__(): diff --git a/rich/scope.py b/rich/scope.py index 9ba08b9f7..a1b76969c 100644 --- a/rich/scope.py +++ b/rich/scope.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from typing import TYPE_CHECKING, Any, Tuple +from typing import TYPE_CHECKING, Any, Optional, Tuple from .highlighter import ReprHighlighter from .panel import Panel @@ -12,13 +12,13 @@ def render_scope( - scope: Mapping, + scope: "Mapping[str, Any]", *, - title: TextType = None, + title: Optional[TextType] = None, sort_keys: bool = True, indent_guides: bool = False, - max_length: int = None, - max_string: int = None, + max_length: Optional[int] = None, + max_string: Optional[int] = None, ) -> "ConsoleRenderable": """Render python variables in a given scope. diff --git a/rich/screen.py b/rich/screen.py index 5a3fb4c0f..189fdc3de 100644 --- a/rich/screen.py +++ b/rich/screen.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import Optional, TYPE_CHECKING from .measure import Measurement from .segment import Segment @@ -19,7 +19,9 @@ class Screen: """ def __init__( - self, renderable: "RenderableType" = None, style: StyleType = None + self, + renderable: Optional["RenderableType"] = None, + style: Optional[StyleType] = None, ) -> None: self.renderable = renderable self.style = style diff --git a/rich/segment.py b/rich/segment.py index d7953daf6..094a7b25a 100644 --- a/rich/segment.py +++ b/rich/segment.py @@ -82,8 +82,8 @@ def line(cls) -> "Segment": def apply_style( cls, segments: Iterable["Segment"], - style: Style = None, - post_style: Style = None, + style: Optional[Style] = None, + post_style: Optional[Style] = None, ) -> Iterable["Segment"]: """Apply style(s) to an iterable of segments. @@ -118,7 +118,7 @@ def apply_style( @classmethod def filter_control( - cls, segments: Iterable["Segment"], is_control=False + cls, segments: Iterable["Segment"], is_control: bool = False ) -> Iterable["Segment"]: """Filter segments by ``is_control`` attribute. @@ -169,7 +169,7 @@ def split_and_crop_lines( cls, segments: Iterable["Segment"], length: int, - style: Style = None, + style: Optional[Style] = None, pad: bool = True, include_new_lines: bool = True, ) -> Iterable[List["Segment"]]: @@ -213,7 +213,11 @@ def split_and_crop_lines( @classmethod def adjust_line_length( - cls, line: List["Segment"], length: int, style: Style = None, pad: bool = True + cls, + line: List["Segment"], + length: int, + style: Optional[Style] = None, + pad: bool = True, ) -> List["Segment"]: """Adjust a line to a given width (cropping or padding as required). @@ -283,8 +287,8 @@ def set_shape( cls, lines: List[List["Segment"]], width: int, - height: int = None, - style: Style = None, + height: Optional[int] = None, + style: Optional[Style] = None, new_lines: bool = False, ) -> List[List["Segment"]]: """Set the shape of a list of lines (enclosing rectangle). diff --git a/rich/spinner.py b/rich/spinner.py index 373854f3c..b3d9d9c0c 100644 --- a/rich/spinner.py +++ b/rich/spinner.py @@ -1,25 +1,30 @@ from typing import cast, List, Optional, TYPE_CHECKING from ._spinners import SPINNERS -from .console import Console from .measure import Measurement -from .style import StyleType -from .text import Text, TextType +from .table import Table +from .text import Text if TYPE_CHECKING: - from .console import Console, ConsoleOptions, RenderResult + from .console import Console, ConsoleOptions, RenderResult, RenderableType + from .style import StyleType class Spinner: def __init__( - self, name: str, text: TextType = "", *, style: StyleType = None, speed=1.0 + self, + name: str, + text: "RenderableType" = "", + *, + style: Optional["StyleType"] = None, + speed: float = 1.0, ) -> None: """A spinner animation. Args: name (str): Name of spinner (run python -m rich.spinner). - text (TextType, optional): Text to display at the right of the spinner. Defaults to "". - style (StyleType, optional): Style for sinner amimation. Defaults to None. + text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "". + style (StyleType, optional): Style for spinner animation. Defaults to None. speed (float, optional): Speed factor for animation. Defaults to 1.0. Raises: @@ -35,16 +40,13 @@ def __init__( self.start_time: Optional[float] = None self.style = style self.speed = speed - self.time = 0.0 + self.frame_no_offset: float = 0.0 + self._update_speed = 0.0 def __rich_console__( self, console: "Console", options: "ConsoleOptions" ) -> "RenderResult": - time = console.get_time() - if self.start_time is None: - self.start_time = time - text = self.render(time - self.start_time) - yield text + yield self.render(console.get_time()) def __rich_measure__( self, console: "Console", options: "ConsoleOptions" @@ -52,18 +54,60 @@ def __rich_measure__( text = self.render(0) return Measurement.get(console, options, text) - def render(self, time: float) -> Text: + def render(self, time: float) -> "RenderableType": """Render the spinner for a given time. Args: time (float): Time in seconds. Returns: - Text: A Text instance containing animation frame. + RenderableType: A renderable containing animation frame. + """ + if self.start_time is None: + self.start_time = time + + frame_no = ((time - self.start_time) * self.speed) / ( + self.interval / 1000.0 + ) + self.frame_no_offset + frame = Text( + self.frames[int(frame_no) % len(self.frames)], style=self.style or "" + ) + + if self._update_speed: + self.frame_no_offset = frame_no + self.start_time = time + self.speed = self._update_speed + self._update_speed = 0.0 + + if not self.text: + return frame + elif isinstance(self.text, (str, Text)): + return Text.assemble(frame, " ", self.text) + else: + table = Table.grid(padding=1) + table.add_row(frame, self.text) + return table + + def update( + self, + *, + text: "RenderableType" = "", + style: Optional["StyleType"] = None, + speed: Optional[float] = None, + ) -> None: + """Updates attributes of a spinner after it has been started. + + Args: + text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "". + style (StyleType, optional): Style for spinner animation. Defaults to None. + speed (float, optional): Speed factor for animation. Defaults to None. """ - frame_no = int((time * self.speed) / (self.interval / 1000.0)) - frame = Text(self.frames[frame_no % len(self.frames)], style=self.style or "") - return Text.assemble(frame, " ", self.text) if self.text else frame + if text: + self.text = text + if style: + self.style = style + if speed: + self._update_speed = speed if __name__ == "__main__": # pragma: no cover diff --git a/rich/status.py b/rich/status.py index 62f1ba95d..09eff405e 100644 --- a/rich/status.py +++ b/rich/status.py @@ -1,11 +1,11 @@ -from typing import Optional +from types import TracebackType +from typing import Optional, Type from .console import Console, RenderableType from .jupyter import JupyterMixin from .live import Live from .spinner import Spinner from .style import StyleType -from .table import Table class Status(JupyterMixin): @@ -24,33 +24,26 @@ def __init__( self, status: RenderableType, *, - console: Console = None, + console: Optional[Console] = None, spinner: str = "dots", spinner_style: StyleType = "status.spinner", speed: float = 1.0, refresh_per_second: float = 12.5, ): self.status = status - self.spinner = spinner self.spinner_style = spinner_style self.speed = speed - self._spinner = Spinner(spinner, style=spinner_style, speed=speed) + self._spinner = Spinner(spinner, text=status, style=spinner_style, speed=speed) self._live = Live( self.renderable, console=console, refresh_per_second=refresh_per_second, transient=True, ) - self.update( - status=status, spinner=spinner, spinner_style=spinner_style, speed=speed - ) @property - def renderable(self) -> Table: - """Get the renderable for the status (a table with spinner and status).""" - table = Table.grid(padding=1) - table.add_row(self._spinner, self.status) - return table + def renderable(self) -> Spinner: + return self._spinner @property def console(self) -> "Console": @@ -64,7 +57,7 @@ def update( spinner: Optional[str] = None, spinner_style: Optional[StyleType] = None, speed: Optional[float] = None, - ): + ) -> None: """Update status. Args: @@ -75,16 +68,19 @@ def update( """ if status is not None: self.status = status - if spinner is not None: - self.spinner = spinner if spinner_style is not None: self.spinner_style = spinner_style if speed is not None: self.speed = speed - self._spinner = Spinner( - self.spinner, style=self.spinner_style, speed=self.speed - ) - self._live.update(self.renderable, refresh=True) + if spinner is not None: + self._spinner = Spinner( + spinner, text=self.status, style=self.spinner_style, speed=self.speed + ) + self._live.update(self.renderable, refresh=True) + else: + self._spinner.update( + text=self.status, style=self.spinner_style, speed=self.speed + ) def start(self) -> None: """Start the status animation.""" @@ -101,7 +97,12 @@ def __enter__(self) -> "Status": self.start() return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: self.stop() diff --git a/rich/style.py b/rich/style.py index 594bcd52d..7f3d9a17b 100644 --- a/rich/style.py +++ b/rich/style.py @@ -93,22 +93,22 @@ class Style: def __init__( self, *, - color: Union[Color, str] = None, - bgcolor: Union[Color, str] = None, - bold: bool = None, - dim: bool = None, - italic: bool = None, - underline: bool = None, - blink: bool = None, - blink2: bool = None, - reverse: bool = None, - conceal: bool = None, - strike: bool = None, - underline2: bool = None, - frame: bool = None, - encircle: bool = None, - overline: bool = None, - link: str = None, + color: Optional[Union[Color, str]] = None, + bgcolor: Optional[Union[Color, str]] = None, + bold: Optional[bool] = None, + dim: Optional[bool] = None, + italic: Optional[bool] = None, + underline: Optional[bool] = None, + blink: Optional[bool] = None, + blink2: Optional[bool] = None, + reverse: Optional[bool] = None, + conceal: Optional[bool] = None, + strike: Optional[bool] = None, + underline2: Optional[bool] = None, + frame: Optional[bool] = None, + encircle: Optional[bool] = None, + overline: Optional[bool] = None, + link: Optional[str] = None, ): self._ansi: Optional[str] = None self._style_definition: Optional[str] = None @@ -176,14 +176,16 @@ def null(cls) -> "Style": return NULL_STYLE @classmethod - def from_color(cls, color: Color = None, bgcolor: Color = None) -> "Style": + def from_color( + cls, color: Optional[Color] = None, bgcolor: Optional[Color] = None + ) -> "Style": """Create a new style with colors and no attributes. Returns: color (Optional[Color]): A (foreground) color, or None for no color. Defaults to None. bgcolor (Optional[Color]): A (background) color, or None for no color. Defaults to None. """ - style = cls.__new__(Style) + style: Style = cls.__new__(Style) style._ansi = None style._style_definition = None style._color = color @@ -388,7 +390,7 @@ def without_color(self) -> "Style": """Get a copy of the style with color removed.""" if self._null: return NULL_STYLE - style = self.__new__(Style) + style: Style = self.__new__(Style) style._ansi = None style._style_definition = None style._color = None @@ -492,7 +494,7 @@ def parse(cls, style_definition: str) -> "Style": return style @lru_cache(maxsize=1024) - def get_html_style(self, theme: TerminalTheme = None) -> str: + def get_html_style(self, theme: Optional[TerminalTheme] = None) -> str: """Get a CSS style rule.""" theme = theme or DEFAULT_TERMINAL_THEME css: List[str] = [] @@ -562,7 +564,7 @@ def copy(self) -> "Style": """ if self._null: return NULL_STYLE - style = self.__new__(Style) + style: Style = self.__new__(Style) style._ansi = self._ansi style._style_definition = self._style_definition style._color = self._color @@ -575,7 +577,7 @@ def copy(self) -> "Style": style._null = False return style - def update_link(self, link: str = None) -> "Style": + def update_link(self, link: Optional[str] = None) -> "Style": """Get a copy with a different value for link. Args: @@ -584,7 +586,7 @@ def update_link(self, link: str = None) -> "Style": Returns: Style: A new Style instance. """ - style = self.__new__(Style) + style: Style = self.__new__(Style) style._ansi = self._ansi style._style_definition = self._style_definition style._color = self._color @@ -637,12 +639,12 @@ def test(self, text: Optional[str] = None) -> None: def __add__(self, style: Optional["Style"]) -> "Style": if not (isinstance(style, Style) or style is None): - return NotImplemented # type: ignore + return NotImplemented if style is None or style._null: return self if self._null: return style - new_style = self.__new__(Style) + new_style: Style = self.__new__(Style) new_style._ansi = None new_style._style_definition = None new_style._color = style._color or self._color diff --git a/rich/syntax.py b/rich/syntax.py index 8d3851fca..66a0bb4e9 100644 --- a/rich/syntax.py +++ b/rich/syntax.py @@ -1,8 +1,9 @@ import os.path import platform +from rich.containers import Lines import textwrap from abc import ABC, abstractmethod -from typing import Any, Dict, Iterable, Optional, Set, Tuple, Type, Union +from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, Union from pygments.lexers import get_lexer_by_name, guess_lexer_for_filename from pygments.style import Style as PygmentsStyle @@ -23,9 +24,10 @@ from ._loop import loop_first from .color import Color, blend_rgb -from .console import Console, ConsoleOptions, JustifyMethod, RenderResult, Segment +from .console import Console, ConsoleOptions, JustifyMethod, RenderResult from .jupyter import JupyterMixin from .measure import Measurement +from .segment import Segment from .style import Style from .text import Text @@ -230,12 +232,12 @@ def __init__( dedent: bool = False, line_numbers: bool = False, start_line: int = 1, - line_range: Tuple[int, int] = None, - highlight_lines: Set[int] = None, + line_range: Optional[Tuple[int, int]] = None, + highlight_lines: Optional[Set[int]] = None, code_width: Optional[int] = None, tab_size: int = 4, word_wrap: bool = False, - background_color: str = None, + background_color: Optional[str] = None, indent_guides: bool = False, ) -> None: self.code = code @@ -264,13 +266,13 @@ def from_path( theme: Union[str, SyntaxTheme] = DEFAULT_THEME, dedent: bool = False, line_numbers: bool = False, - line_range: Tuple[int, int] = None, + line_range: Optional[Tuple[int, int]] = None, start_line: int = 1, - highlight_lines: Set[int] = None, + highlight_lines: Optional[Set[int]] = None, code_width: Optional[int] = None, tab_size: int = 4, word_wrap: bool = False, - background_color: str = None, + background_color: Optional[str] = None, indent_guides: bool = False, ) -> "Syntax": """Construct a Syntax object from a file. @@ -331,11 +333,6 @@ def from_path( def _get_base_style(self) -> Style: """Get the base style.""" - # default_style = ( - # Style(bgcolor=self.background_color) - # if self.background_color is not None - # else self._theme.get_background_style() - # ) default_style = self._theme.get_background_style() + self.background_style return default_style @@ -351,7 +348,9 @@ def _get_token_color(self, token_type: TokenType) -> Optional[Color]: style = self._theme.get_style_for_token(token_type) return style.color - def highlight(self, code: str, line_range: Tuple[int, int] = None) -> Text: + def highlight( + self, code: str, line_range: Optional[Tuple[int, int]] = None + ) -> Text: """Highlight code and return a Text instance. Args: @@ -375,7 +374,12 @@ def highlight(self, code: str, line_range: Tuple[int, int] = None) -> Text: ) _get_theme_style = self._theme.get_style_for_token try: - lexer = get_lexer_by_name(self.lexer_name) + lexer = get_lexer_by_name( + self.lexer_name, + stripnl=False, + ensurenl=True, + tabsize=self.tab_size, + ) except ClassNotFound: text.append(code) else: @@ -496,10 +500,11 @@ def __rich_console__( start_line, end_line = self.line_range line_offset = max(0, start_line - 1) - code = textwrap.dedent(self.code) if self.dedent else self.code + ends_on_nl = self.code.endswith("\n") + code = self.code if ends_on_nl else self.code + "\n" + code = textwrap.dedent(code) if self.dedent else code code = code.expandtabs(self.tab_size) text = self.highlight(code, self.line_range) - text.remove_suffix("\n") ( background_style, @@ -508,6 +513,8 @@ def __rich_console__( ) = self._get_number_styles(console) if not self.line_numbers and not self.word_wrap and not self.line_range: + if not ends_on_nl: + text.remove_suffix("\n") # Simple case of just rendering text style = ( self._get_base_style() @@ -534,7 +541,7 @@ def __rich_console__( yield from syntax_line return - lines = text.split("\n") + lines: Union[List[Text], Lines] = text.split("\n", allow_blank=ends_on_nl) if self.line_range: lines = lines[line_offset:end_line] @@ -549,7 +556,7 @@ def __rich_console__( Text("\n") .join(lines) .with_indent_guides(self.tab_size, style=style) - .split("\n") + .split("\n", allow_blank=True) ) numbers_column_width = self._numbers_column_width diff --git a/rich/table.py b/rich/table.py index 8fc6379d9..54e39c949 100644 --- a/rich/table.py +++ b/rich/table.py @@ -151,10 +151,10 @@ class Table(JupyterMixin): def __init__( self, *headers: Union[Column, str], - title: TextType = None, - caption: TextType = None, - width: int = None, - min_width: int = None, + title: Optional[TextType] = None, + caption: Optional[TextType] = None, + width: Optional[int] = None, + min_width: Optional[int] = None, box: Optional[box.Box] = box.HEAVY_HEAD, safe_box: Optional[bool] = None, padding: PaddingDimensions = (0, 1), @@ -167,12 +167,12 @@ def __init__( show_lines: bool = False, leading: int = 0, style: StyleType = "none", - row_styles: Iterable[StyleType] = None, + row_styles: Optional[Iterable[StyleType]] = None, header_style: Optional[StyleType] = "table.header", footer_style: Optional[StyleType] = "table.footer", - border_style: StyleType = None, - title_style: StyleType = None, - caption_style: StyleType = None, + border_style: Optional[StyleType] = None, + title_style: Optional[StyleType] = None, + caption_style: Optional[StyleType] = None, title_justify: "JustifyMethod" = "center", caption_justify: "JustifyMethod" = "center", highlight: bool = False, @@ -330,15 +330,15 @@ def add_column( header: "RenderableType" = "", footer: "RenderableType" = "", *, - header_style: StyleType = None, - footer_style: StyleType = None, - style: StyleType = None, + header_style: Optional[StyleType] = None, + footer_style: Optional[StyleType] = None, + style: Optional[StyleType] = None, justify: "JustifyMethod" = "left", overflow: "OverflowMethod" = "ellipsis", - width: int = None, - min_width: int = None, - max_width: int = None, - ratio: int = None, + width: Optional[int] = None, + min_width: Optional[int] = None, + max_width: Optional[int] = None, + ratio: Optional[int] = None, no_wrap: bool = False, ) -> None: """Add a column to the table. @@ -379,7 +379,7 @@ def add_column( def add_row( self, *renderables: Optional["RenderableType"], - style: StyleType = None, + style: Optional[StyleType] = None, end_section: bool = False, ) -> None: """Add a row of renderables. diff --git a/rich/tabulate.py b/rich/tabulate.py index 680bd3214..82fb0e1f9 100644 --- a/rich/tabulate.py +++ b/rich/tabulate.py @@ -1,5 +1,5 @@ from collections.abc import Mapping -from typing import Optional +from typing import Any, Optional from rich.console import JustifyMethod @@ -10,9 +10,9 @@ def tabulate_mapping( - mapping: Mapping, - title: str = None, - caption: str = None, + mapping: "Mapping[Any, Any]", + title: Optional[str] = None, + caption: Optional[str] = None, title_justify: Optional[JustifyMethod] = None, caption_justify: Optional[JustifyMethod] = None, ) -> Table: diff --git a/rich/terminal_theme.py b/rich/terminal_theme.py index a5ca1c0c7..801ac0b7b 100644 --- a/rich/terminal_theme.py +++ b/rich/terminal_theme.py @@ -1,4 +1,4 @@ -from typing import List, Tuple +from typing import List, Optional, Tuple from .color_triplet import ColorTriplet from .palette import Palette @@ -22,7 +22,7 @@ def __init__( background: _ColorTuple, foreground: _ColorTuple, normal: List[_ColorTuple], - bright: List[_ColorTuple] = None, + bright: Optional[List[_ColorTuple]] = None, ) -> None: self.background_color = ColorTriplet(*background) self.foreground_color = ColorTriplet(*foreground) diff --git a/rich/text.py b/rich/text.py index c5edc0299..405e3cea9 100644 --- a/rich/text.py +++ b/rich/text.py @@ -129,12 +129,12 @@ def __init__( text: str = "", style: Union[str, Style] = "", *, - justify: "JustifyMethod" = None, - overflow: "OverflowMethod" = None, - no_wrap: bool = None, + justify: Optional["JustifyMethod"] = None, + overflow: Optional["OverflowMethod"] = None, + no_wrap: Optional[bool] = None, end: str = "\n", tab_size: Optional[int] = 8, - spans: List[Span] = None, + spans: Optional[List[Span]] = None, ) -> None: self._text = [strip_control_codes(text)] self.style = style @@ -178,7 +178,7 @@ def __contains__(self, other: object) -> bool: return False def __getitem__(self, slice: Union[int, slice]) -> "Text": - def get_text_at(offset) -> "Text": + def get_text_at(offset: int) -> "Text": _Span = Span text = Text( self.plain[offset], @@ -215,8 +215,8 @@ def from_markup( *, style: Union[str, Style] = "", emoji: bool = True, - justify: "JustifyMethod" = None, - overflow: "OverflowMethod" = None, + justify: Optional["JustifyMethod"] = None, + overflow: Optional["OverflowMethod"] = None, ) -> "Text": """Create Text instance from markup. @@ -242,8 +242,8 @@ def styled( text: str, style: StyleType = "", *, - justify: "JustifyMethod" = None, - overflow: "OverflowMethod" = None, + justify: Optional["JustifyMethod"] = None, + overflow: Optional["OverflowMethod"] = None, ) -> "Text": """Construct a Text instance with a pre-applied styled. A style applied in this way won't be used to pad the text when it is justified. @@ -266,9 +266,9 @@ def assemble( cls, *parts: Union[str, "Text", Tuple[str, StyleType]], style: Union[str, Style] = "", - justify: "JustifyMethod" = None, - overflow: "OverflowMethod" = None, - no_wrap: bool = None, + justify: Optional["JustifyMethod"] = None, + overflow: Optional["OverflowMethod"] = None, + no_wrap: Optional[bool] = None, end: str = "\n", tab_size: int = 8, ) -> "Text": @@ -357,7 +357,10 @@ def copy(self) -> "Text": return copy_self def stylize( - self, style: Union[str, Style], start: int = 0, end: Optional[int] = None + self, + style: Union[str, Style], + start: int = 0, + end: Optional[int] = None, ) -> None: """Apply a style to the text, or a portion of the text. @@ -412,7 +415,7 @@ def get_style_at_offset(self, console: "Console", offset: int) -> Style: def highlight_regex( self, re_highlight: str, - style: Union[GetStyleCallable, StyleType] = None, + style: Optional[Union[GetStyleCallable, StyleType]] = None, *, style_prefix: str = "", ) -> int: @@ -506,12 +509,15 @@ def set_length(self, new_length: int) -> None: def __rich_console__( self, console: "Console", options: "ConsoleOptions" ) -> Iterable[Segment]: - tab_size: int = console.tab_size or self.tab_size or 8 # type: ignore - justify = cast( - "JustifyMethod", self.justify or options.justify or DEFAULT_OVERFLOW + tab_size: int = console.tab_size or self.tab_size or 8 + justify = ( + cast("JustifyMethod", self.justify) or options.justify or DEFAULT_OVERFLOW ) - overflow = cast( - "OverflowMethod", self.overflow or options.overflow or DEFAULT_OVERFLOW + + overflow = ( + cast("OverflowMethod", self.overflow) + or options.overflow + or DEFAULT_OVERFLOW ) lines = self.wrap( @@ -635,7 +641,7 @@ def iter_text() -> Iterable["Text"]: new_text._length = offset return new_text - def expand_tabs(self, tab_size: int = None) -> None: + def expand_tabs(self, tab_size: Optional[int] = None) -> None: """Converts tabs to spaces. Args: @@ -774,7 +780,7 @@ def align(self, align: AlignMethod, width: int, character: str = " ") -> None: self.pad_left(excess_space, character) def append( - self, text: Union["Text", str], style: Union[str, "Style"] = None + self, text: Union["Text", str], style: Optional[Union[str, "Style"]] = None ) -> "Text": """Add text with an optional style. @@ -836,7 +842,9 @@ def append_text(self, text: "Text") -> "Text": self._length += len(text) return self - def append_tokens(self, tokens: Iterable[Tuple[str, Optional[StyleType]]]): + def append_tokens( + self, tokens: Iterable[Tuple[str, Optional[StyleType]]] + ) -> "Text": """Append iterable of str and style. Style may be a Style instance or a str style definition. Args: @@ -867,7 +875,7 @@ def copy_styles(self, text: "Text") -> None: def split( self, - separator="\n", + separator: str = "\n", *, include_separator: bool = False, allow_blank: bool = False, @@ -993,10 +1001,10 @@ def wrap( console: "Console", width: int, *, - justify: "JustifyMethod" = None, - overflow: "OverflowMethod" = None, + justify: Optional["JustifyMethod"] = None, + overflow: Optional["OverflowMethod"] = None, tab_size: int = 8, - no_wrap: bool = None, + no_wrap: Optional[bool] = None, ) -> Lines: """Word wrap the text. @@ -1012,10 +1020,11 @@ def wrap( Returns: Lines: Number of lines. """ - wrap_justify = cast("JustifyMethod", justify or self.justify or DEFAULT_JUSTIFY) - wrap_overflow = cast( - "OverflowMethod", overflow or self.overflow or DEFAULT_OVERFLOW + wrap_justify = cast("JustifyMethod", justify or self.justify) or DEFAULT_JUSTIFY + wrap_overflow = ( + cast("OverflowMethod", overflow or self.overflow) or DEFAULT_OVERFLOW ) + no_wrap = pick_bool(no_wrap, self.no_wrap, False) or overflow == "ignore" lines = Lines() @@ -1077,7 +1086,7 @@ def detect_indentation(self) -> int: def with_indent_guides( self, - indent_size: int = None, + indent_size: Optional[int] = None, *, character: str = "โ”‚", style: StyleType = "dim green", @@ -1103,7 +1112,7 @@ def with_indent_guides( new_lines: List[Text] = [] add_line = new_lines.append blank_lines = 0 - for line in text.split(): + for line in text.split(allow_blank=True): match = re_indent.match(line.plain) if not match or not match.group(2): blank_lines += 1 diff --git a/rich/theme.py b/rich/theme.py index 68b1ef3d2..bfb3c7f82 100644 --- a/rich/theme.py +++ b/rich/theme.py @@ -1,5 +1,5 @@ import configparser -from typing import Dict, List, IO, Mapping +from typing import Dict, List, IO, Mapping, Optional from .default_styles import DEFAULT_STYLES from .style import Style, StyleType @@ -15,7 +15,9 @@ class Theme: styles: Dict[str, Style] - def __init__(self, styles: Mapping[str, StyleType] = None, inherit: bool = True): + def __init__( + self, styles: Optional[Mapping[str, StyleType]] = None, inherit: bool = True + ): self.styles = DEFAULT_STYLES.copy() if inherit else {} if styles is not None: self.styles.update( @@ -35,7 +37,7 @@ def config(self) -> str: @classmethod def from_file( - cls, config_file: IO[str], source: str = None, inherit: bool = True + cls, config_file: IO[str], source: Optional[str] = None, inherit: bool = True ) -> "Theme": """Load a theme from a text mode file. diff --git a/rich/traceback.py b/rich/traceback.py index c3f5c0eba..4861ac0e2 100644 --- a/rich/traceback.py +++ b/rich/traceback.py @@ -40,14 +40,14 @@ def install( *, - console: Console = None, + console: Optional[Console] = None, width: Optional[int] = 100, extra_lines: int = 3, theme: Optional[str] = None, word_wrap: bool = False, show_locals: bool = False, indent_guides: bool = True, -) -> Callable: +) -> Callable[[Type[BaseException], BaseException, Optional[TracebackType]], Any]: """Install a rich traceback handler. Once installed, any tracebacks will be printed with syntax highlighting and rich formatting. @@ -88,17 +88,19 @@ def excepthook( ) ) - def ipy_excepthook_closure(ip) -> None: # pragma: no cover + def ipy_excepthook_closure(ip: Any) -> None: # pragma: no cover tb_data = {} # store information about showtraceback call default_showtraceback = ip.showtraceback # keep reference of default traceback - def ipy_show_traceback(*args, **kwargs) -> None: + def ipy_show_traceback(*args: Any, **kwargs: Any) -> None: """wrap the default ip.showtraceback to store info for ip._showtraceback""" nonlocal tb_data tb_data = kwargs default_showtraceback(*args, **kwargs) - def ipy_display_traceback(*args, is_syntax: bool = False, **kwargs) -> None: + def ipy_display_traceback( + *args: Any, is_syntax: bool = False, **kwargs: Any + ) -> None: """Internally called traceback from ip._showtraceback""" nonlocal tb_data exc_tuple = ip._get_exc_info() @@ -131,12 +133,12 @@ def ipy_display_traceback(*args, is_syntax: bool = False, **kwargs) -> None: # if wihin ipython, use customized traceback ip = get_ipython() # type: ignore ipy_excepthook_closure(ip) - return sys.excepthook + return sys.excepthook # type: ignore # more strict signature that mypy cant interpret except Exception: # otherwise use default system hook old_excepthook = sys.excepthook sys.excepthook = excepthook - return old_excepthook + return old_excepthook # type: ignore # more strict signature that mypy cant interpret @dataclass @@ -202,7 +204,7 @@ class Traceback: def __init__( self, - trace: Trace = None, + trace: Optional[Trace] = None, width: Optional[int] = 100, extra_lines: int = 3, theme: Optional[str] = None, @@ -234,7 +236,7 @@ def __init__( @classmethod def from_exception( cls, - exc_type: Type, + exc_type: Type[Any], exc_value: BaseException, traceback: Optional[TracebackType], width: Optional[int] = 100, @@ -444,11 +446,13 @@ def __rich_console__( (f"{stack.exc_type}: ", "traceback.exc_type"), highlighter(stack.syntax_error.msg), ) - else: + elif stack.exc_value: yield Text.assemble( (f"{stack.exc_type}: ", "traceback.exc_type"), highlighter(stack.exc_value), ) + else: + yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type")) if not last: if stack.is_cause: @@ -591,11 +595,11 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]: console = Console() import sys - def bar(a): # ่ฟ™ๆ˜ฏๅฏนไบšๆดฒ่ฏญ่จ€ๆ”ฏๆŒ็š„ๆต‹่ฏ•ใ€‚้ขๅฏนๆจกๆฃฑไธคๅฏ็š„ๆƒณๆณ•๏ผŒๆ‹’็ป็Œœๆต‹็š„่ฏฑๆƒ‘ + def bar(a: Any) -> None: # ่ฟ™ๆ˜ฏๅฏนไบšๆดฒ่ฏญ่จ€ๆ”ฏๆŒ็š„ๆต‹่ฏ•ใ€‚้ขๅฏนๆจกๆฃฑไธคๅฏ็š„ๆƒณๆณ•๏ผŒๆ‹’็ป็Œœๆต‹็š„่ฏฑๆƒ‘ one = 1 print(one / a) - def foo(a): + def foo(a: Any) -> None: zed = { "characters": { @@ -608,7 +612,7 @@ def foo(a): } bar(a) - def error(): + def error() -> None: try: try: diff --git a/rich/tree.py b/rich/tree.py index 5fc4487a8..38c9fa170 100644 --- a/rich/tree.py +++ b/rich/tree.py @@ -1,4 +1,4 @@ -from typing import Iterator, List, Tuple +from typing import Iterator, List, Optional, Tuple from ._loop import loop_first, loop_last from .console import Console, ConsoleOptions, RenderableType, RenderResult @@ -26,8 +26,8 @@ def __init__( *, style: StyleType = "tree", guide_style: StyleType = "tree.line", - expanded=True, - highlight=False, + expanded: bool = True, + highlight: bool = False, ) -> None: self.label = label self.style = style @@ -40,10 +40,10 @@ def add( self, label: RenderableType, *, - style: StyleType = None, - guide_style: StyleType = None, - expanded=True, - highlight=False, + style: Optional[StyleType] = None, + guide_style: Optional[StyleType] = None, + expanded: bool = True, + highlight: bool = False, ) -> "Tree": """Add a child tree. diff --git a/tests/test_layout.py b/tests/test_layout.py index f0befcdad..e233a7b9c 100644 --- a/tests/test_layout.py +++ b/tests/test_layout.py @@ -83,11 +83,14 @@ def test_refresh_screen(): layout = Layout() layout.split_row(Layout(name="foo"), Layout(name="bar")) console = Console(force_terminal=True, width=20, height=5) - console.print(layout) + with console.capture(): + console.print(layout) with console.screen(): with console.capture() as capture: layout.refresh_screen(console, "foo") result = capture.get() + print() print(repr(result)) - expected = "\x1b[1;1H\x1b[34mโ•ญโ”€\x1b[0m\x1b[34m \x1b[0m\x1b[32m'foo'\x1b[0m\x1b[34mโ”€โ•ฎ\x1b[0m\x1b[2;1H\x1b[34mโ”‚\x1b[0m Layout \x1b[34mโ”‚\x1b[0m\x1b[3;1H\x1b[34mโ”‚\x1b[0m \x1b[1m(\x1b[0m \x1b[34mโ”‚\x1b[0m\x1b[4;1H\x1b[34mโ”‚\x1b[0m \x1b[33mna\x1b[0m \x1b[34mโ”‚\x1b[0m\x1b[5;1H\x1b[34mโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ\x1b[0m" + expected = "\x1b[1;1H\x1b[34mโ•ญโ”€\x1b[0m\x1b[34m \x1b[0m\x1b[32m'foo'\x1b[0m\x1b[34mโ”€โ•ฎ\x1b[0m\x1b[2;1H\x1b[34mโ”‚\x1b[0m \x1b[1;35mLayout\x1b[0m \x1b[34mโ”‚\x1b[0m\x1b[3;1H\x1b[34mโ”‚\x1b[0m \x1b[1m(\x1b[0m \x1b[34mโ”‚\x1b[0m\x1b[4;1H\x1b[34mโ”‚\x1b[0m \x1b[33mna\x1b[0m \x1b[34mโ”‚\x1b[0m\x1b[5;1H\x1b[34mโ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ\x1b[0m" + assert result == expected diff --git a/tests/test_pretty.py b/tests/test_pretty.py index d8c046c27..ec12a058f 100644 --- a/tests/test_pretty.py +++ b/tests/test_pretty.py @@ -1,14 +1,23 @@ from array import array -from collections import defaultdict +from collections import defaultdict, UserDict, UserList from dataclasses import dataclass, field import io import sys from typing import List +import attr +import pytest + from rich.console import Console from rich.pretty import install, Pretty, pprint, pretty_repr, Node +skip_py36 = pytest.mark.skipif( + sys.version_info.minor == 6 and sys.version_info.major == 3, + reason="rendered differently on py3.6", +) + + def test_install(): console = Console(file=io.StringIO()) dh = sys.displayhook @@ -181,3 +190,60 @@ def __repr__(self): return "" assert pretty_repr(Foo()) == "" + + +def test_attrs(): + @attr.define + class Point: + x: int + y: int + foo: str = attr.field(repr=str.upper) + z: int = 0 + + result = pretty_repr(Point(1, 2, foo="bar")) + print(repr(result)) + expected = "Point(x=1, y=2, foo=BAR, z=0)" + assert result == expected + + +def test_attrs_empty(): + @attr.define + class Nada: + pass + + result = pretty_repr(Nada()) + print(repr(result)) + expected = "Nada()" + assert result == expected + + +@skip_py36 +def test_attrs_broken(): + @attr.define + class Foo: + bar: int + + foo = Foo(1) + del foo.bar + result = pretty_repr(foo) + print(repr(result)) + expected = "Foo(bar=AttributeError('bar'))" + assert result == expected + + +def test_user_dict(): + class D1(UserDict): + pass + + class D2(UserDict): + def __repr__(self): + return "FOO" + + d1 = D1({"foo": "bar"}) + d2 = D2({"foo": "bar"}) + result = pretty_repr(d1, expand_all=True) + print(repr(result)) + assert result == "{\n 'foo': 'bar'\n}" + result = pretty_repr(d2, expand_all=True) + print(repr(result)) + assert result == "FOO" diff --git a/tests/test_progress.py b/tests/test_progress.py index e20c6ab63..1036c78c8 100644 --- a/tests/test_progress.py +++ b/tests/test_progress.py @@ -88,9 +88,22 @@ def test_renderable_column(): def test_spinner_column(): + time = 1.0 + + def get_time(): + nonlocal time + return time + column = SpinnerColumn() column.set_spinner("dots2") - task = Task(1, "test", 100, 20, _get_time=lambda: 1.0) + task = Task(1, "test", 100, 20, _get_time=get_time) + result = column.render(task) + print(repr(result)) + expected = "โฃพ" + assert str(result) == expected + + time += 1.0 + column.spinner.update(speed=0.5) result = column.render(task) print(repr(result)) expected = "โกฟ" diff --git a/tests/test_repr.py b/tests/test_repr.py index ee6ba0af0..2cc7aeb07 100644 --- a/tests/test_repr.py +++ b/tests/test_repr.py @@ -1,10 +1,12 @@ +from typing import Optional + from rich.console import Console from rich.repr import rich_repr @rich_repr class Foo: - def __init__(self, foo: str, bar: int = None, egg: int = 1): + def __init__(self, foo: str, bar: Optional[int] = None, egg: int = 1): self.foo = foo self.bar = bar self.egg = egg diff --git a/tests/test_spinner.py b/tests/test_spinner.py index 77b1a03b2..28aac7fc0 100644 --- a/tests/test_spinner.py +++ b/tests/test_spinner.py @@ -1,14 +1,13 @@ -from time import time import pytest from rich.console import Console from rich.measure import Measurement +from rich.rule import Rule from rich.spinner import Spinner def test_spinner_create(): - spinner = Spinner("dots") - assert spinner.time == 0.0 + Spinner("dots") with pytest.raises(KeyError): Spinner("foobar") @@ -34,6 +33,32 @@ def get_time(): assert result == expected +def test_spinner_update(): + time = 0.0 + + def get_time(): + nonlocal time + return time + + console = Console(width=20, force_terminal=True, get_time=get_time) + console.begin_capture() + spinner = Spinner("dots") + console.print(spinner) + + spinner.update(text="Bar", style="green", speed=2) + time += 80 / 1000 + console.print(spinner) + + spinner.update(text=Rule("Bar")) + time += 80 / 1000 + console.print(spinner) + + result = console.end_capture() + print(repr(result)) + expected = f"โ ‹\n\x1b[32mโ ™\x1b[0m Bar\n\x1b[32mโ ธ\x1b[0m \x1b[92mโ”€โ”€โ”€โ”€โ”€โ”€ \x1b[0mBar\x1b[92m โ”€โ”€โ”€โ”€โ”€โ”€โ”€\x1b[0m\n" + assert result == expected + + def test_rich_measure(): console = Console(width=80, color_system=None, force_terminal=True) spinner = Spinner("dots", "Foo") diff --git a/tests/test_status.py b/tests/test_status.py index 3abaa33b4..43ef94e01 100644 --- a/tests/test_status.py +++ b/tests/test_status.py @@ -1,8 +1,8 @@ from time import sleep from rich.console import Console +from rich.spinner import Spinner from rich.status import Status -from rich.table import Table def test_status(): @@ -12,9 +12,13 @@ def test_status(): ) status = Status("foo", console=console) assert status.console == console - status.update(status="bar", spinner="dots2", spinner_style="red", speed=2.0) + previous_status_renderable = status.renderable + status.update(status="bar", spinner_style="red", speed=2.0) - assert isinstance(status.renderable, Table) + assert previous_status_renderable == status.renderable + assert isinstance(status.renderable, Spinner) + status.update(spinner="dots2") + assert previous_status_renderable != status.renderable # TODO: Testing output is tricky with threads with status: diff --git a/tests/test_syntax.py b/tests/test_syntax.py index d9edadb98..090d08d25 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -11,7 +11,7 @@ from rich.syntax import Syntax, ANSISyntaxTheme, PygmentsSyntaxTheme, Color, Console -CODE = ''' +CODE = '''\ def loop_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]: """Iterate and generate a tuple with a flag for first and last value.""" iter_values = iter(values) @@ -24,8 +24,20 @@ def loop_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]: yield first, False, previous_value first = False previous_value = value - yield first, True, previous_value -''' + yield first, True, previous_value''' + + +def test_blank_lines(): + code = "\n\nimport this\n\n" + syntax = Syntax( + code, lexer_name="python", theme="ascii_light", code_width=30, line_numbers=True + ) + result = render(syntax) + print(repr(result)) + assert ( + result + == "\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m1 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m2 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m3 \x1b[0m\x1b[1;38;2;0;128;0;48;2;248;248;248mimport\x1b[0m\x1b[38;2;0;0;0;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m4 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m5 \x1b[0m\x1b[48;2;248;248;248m \x1b[0m\n" + ) def test_python_render(): diff --git a/tests/test_text.py b/tests/test_text.py index 98edfd15a..5c5a30d7c 100644 --- a/tests/test_text.py +++ b/tests/test_text.py @@ -652,7 +652,7 @@ def test_indentation_guides(): result = test.with_indent_guides() print(result.plain) print(repr(result.plain)) - expected = "for a in range(10):\nโ”‚ print(a)\n\nfoo = [\nโ”‚ 1,\nโ”‚ {\nโ”‚ โ”‚ 2\nโ”‚ }\n]\n" + expected = "for a in range(10):\nโ”‚ print(a)\n\nfoo = [\nโ”‚ 1,\nโ”‚ {\nโ”‚ โ”‚ 2\nโ”‚ }\n]\n\n" assert result.plain == expected diff --git a/tests/test_traceback.py b/tests/test_traceback.py index 6941a2e4e..33ec880b4 100644 --- a/tests/test_traceback.py +++ b/tests/test_traceback.py @@ -81,6 +81,17 @@ def test_print_exception(): assert "ZeroDivisionError" in exception_text +def test_print_exception_no_msg(): + console = Console(width=100, file=io.StringIO()) + try: + raise RuntimeError + except Exception: + console.print_exception() + exception_text = console.file.getvalue() + assert "RuntimeError" in exception_text + assert "RuntimeError:" not in exception_text + + def test_print_exception_locals(): console = Console(width=100, file=io.StringIO()) try: diff --git a/tox.ini b/tox.ini index 24e44a220..b1e21f3b7 100644 --- a/tox.ini +++ b/tox.ini @@ -8,11 +8,8 @@ isolated_build = True [testenv] description = Run unit-testing -# develop temporary disabled as project packaging does not work with it yet: -# https://github.com/willmcgugan/rich/issues/345 -usedevelop = False deps = - -r requirements-dev.txt + poetry # do not put * in passenv as it may break builds due to reduced isolation passenv = CI @@ -25,15 +22,14 @@ setenv = PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 commands = - # failsafe as older pip may install incompatible dependencies - pip check + poetry install pytest --cov-report term-missing --cov=rich tests/ {posargs} [testenv:lint] description = Runs all linting tasks commands = black . - mypy -p rich --ignore-missing-imports --warn-unreachable + mypy -p rich --config-file= --ignore-missing-imports --no-implicit-optional --warn-unreachable skip_install = true [testenv:docs]