diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1e55b7..7b17d733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ 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.7.0] - 2021-08-05 + +### Added + +- Added Text.apply_meta +- Added meta argument to Text.assemble +- Added Style.from_meta +- Added Style.on +- Added Text.on + +### Changed + +- Changed `RenderGroup` to `Group` and `render_group` to `group` (old names remain for compatibility but will be deprecated in the future) +- Changed `rich.repr.RichReprResult` to `rich.repr.Result` (old names remain for compatibility but will be deprecated in the future) +- Changed meta serialization to use pickle rather than marshal to permit callables + ## [10.6.0] - 2021-07-12 ### Deprecated diff --git a/Makefile b/Makefile index 9fed1390..56977d2c 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ format-check: format: black . typecheck: - mypy -p rich --strict + mypy -p rich --strict --no-incremental typecheck-report: mypy -p rich --strict --html-report mypy_report .PHONY: docs diff --git a/docs/source/group.rst b/docs/source/group.rst index 7159a9d9..22a30aa5 100644 --- a/docs/source/group.rst +++ b/docs/source/group.rst @@ -1,28 +1,28 @@ Render Groups ============= -The :class:`~rich.console.RenderGroup` class allows you to group several renderables together so they may be rendered in a context where only a single renderable may be supplied. For instance, you might want to display several renderables within a :class:`~rich.panel.Panel`. +The :class:`~rich.console.Group` class allows you to group several renderables together so they may be rendered in a context where only a single renderable may be supplied. For instance, you might want to display several renderables within a :class:`~rich.panel.Panel`. -To render two panels within a third panel, you would construct a RenderGroup with the *child* renderables as positional arguments then wrap the result in another Panel:: +To render two panels within a third panel, you would construct a Group with the *child* renderables as positional arguments then wrap the result in another Panel:: from rich import print - from rich.console import RenderGroup + from rich.console import Group from rich.panel import Panel - panel_group = RenderGroup( + panel_group = Group( Panel("Hello", style="on blue"), Panel("World", style="on red"), ) print(Panel(panel_group)) -This pattern is nice when you know in advance what renderables will be in a group, but can get awkward if you have a larger number of renderables, especially if they are dynamic. Rich provides a :func:`~rich.console.render_group` decorator to help with these situations. The decorator builds a render group from an iterator of renderables. The following is the equivalent of the previous example using the decorator:: +This pattern is nice when you know in advance what renderables will be in a group, but can get awkward if you have a larger number of renderables, especially if they are dynamic. Rich provides a :func:`~rich.console.group` decorator to help with these situations. The decorator builds a group from an iterator of renderables. The following is the equivalent of the previous example using the decorator:: from rich import print - from rich.console import render_group + from rich.console import group from rich.panel import Panel - @render_group() + @group() def get_panels(): yield Panel("Hello", style="on blue") yield Panel("World", style="on red") diff --git a/docs/source/markup.rst b/docs/source/markup.rst index 7a58017b..37995202 100644 --- a/docs/source/markup.rst +++ b/docs/source/markup.rst @@ -5,6 +5,9 @@ Console Markup Rich supports a simple markup which you can use to insert color and styles virtually everywhere Rich would accept a string (e.g. :meth:`~rich.console.Console.print` and :meth:`~rich.console.Console.log`). +Run the following command to see some examples:: + + python -m rich.markup Syntax ------ diff --git a/examples/fullscreen.py b/examples/fullscreen.py index b4c763d2..5ccc6fb3 100644 --- a/examples/fullscreen.py +++ b/examples/fullscreen.py @@ -7,7 +7,7 @@ from datetime import datetime from rich import box from rich.align import Align -from rich.console import Console, RenderGroup +from rich.console import Console, Group from rich.layout import Layout from rich.panel import Panel from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn @@ -67,7 +67,7 @@ def make_sponsor_message() -> Panel: message_panel = Panel( Align.center( - RenderGroup(intro_message, "\n", Align.center(sponsor_message)), + Group(intro_message, "\n", Align.center(sponsor_message)), vertical="middle", ), box=box.ROUNDED, diff --git a/examples/group.py b/examples/group.py index a61f830d..598e4c39 100644 --- a/examples/group.py +++ b/examples/group.py @@ -1,8 +1,8 @@ from rich import print -from rich.console import RenderGroup +from rich.console import Group from rich.panel import Panel -panel_group = RenderGroup( +panel_group = Group( Panel("Hello", style="on blue"), Panel("World", style="on red"), ) diff --git a/examples/group2.py b/examples/group2.py index 13be0704..54c1bf77 100644 --- a/examples/group2.py +++ b/examples/group2.py @@ -1,9 +1,9 @@ from rich import print -from rich.console import render_group +from rich.console import group from rich.panel import Panel -@render_group() +@group() def get_panels(): yield Panel("Hello", style="on blue") yield Panel("World", style="on red") diff --git a/poetry.lock b/poetry.lock index 63a69fdf..a13df573 100644 --- a/poetry.lock +++ b/poetry.lock @@ -94,11 +94,11 @@ d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] name = "bleach" -version = "3.3.0" +version = "4.0.0" description = "An easy safelist-based HTML-sanitizing tool." category = "main" optional = true -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" [package.dependencies] packaging = "*" @@ -192,7 +192,7 @@ python-versions = ">=2.7" [[package]] name = "importlib-metadata" -version = "4.6.1" +version = "4.6.3" description = "Read metadata from Python packages" category = "main" optional = false @@ -509,7 +509,7 @@ python-versions = ">=3.5" [[package]] name = "notebook" -version = "6.4.0" +version = "6.4.1" description = "A web-based notebook environment for interactive computing" category = "main" optional = true @@ -569,11 +569,11 @@ testing = ["docopt", "pytest (<6.0.0)"] [[package]] name = "pathspec" -version = "0.8.1" +version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "pexpect" @@ -718,7 +718,7 @@ testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtuale [[package]] name = "python-dateutil" -version = "2.8.1" +version = "2.8.2" description = "Extensions to the standard Python datetime module" category = "main" optional = true @@ -745,7 +745,7 @@ python-versions = ">=3.6" [[package]] name = "pyzmq" -version = "22.1.0" +version = "22.2.1" description = "Python bindings for 0MQ" category = "main" optional = true @@ -757,7 +757,7 @@ py = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "regex" -version = "2021.7.6" +version = "2021.8.3" description = "Alternative regular expression module, to replace re." category = "dev" optional = false @@ -940,6 +940,10 @@ argon2-cffi = [ {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"}, + {file = "argon2_cffi-20.1.0-pp36-pypy36_pp73-macosx_10_7_x86_64.whl", hash = "sha256:b94042e5dcaa5d08cf104a54bfae614be502c6f44c9c89ad1535b2ebdaacbd4c"}, + {file = "argon2_cffi-20.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:8282b84ceb46b5b75c3a882b28856b8cd7e647ac71995e71b6705ec06fc232c3"}, + {file = "argon2_cffi-20.1.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3aa804c0e52f208973845e8b10c70d8957c9e5a666f702793256242e9167c4e0"}, + {file = "argon2_cffi-20.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:36320372133a003374ef4275fbfce78b7ab581440dfca9f9471be3dd9a522428"}, ] async-generator = [ {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, @@ -961,8 +965,8 @@ black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] bleach = [ - {file = "bleach-3.3.0-py2.py3-none-any.whl", hash = "sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125"}, - {file = "bleach-3.3.0.tar.gz", hash = "sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"}, + {file = "bleach-4.0.0-py2.py3-none-any.whl", hash = "sha256:c1685a132e6a9a38bf93752e5faab33a9517a6c0bb2f37b785e47bf253bdb51d"}, + {file = "bleach-4.0.0.tar.gz", hash = "sha256:ffa9221c6ac29399cc50fcc33473366edd0cf8d5e2cbbbb63296dc327fb67cc8"}, ] cffi = [ {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"}, @@ -972,6 +976,11 @@ cffi = [ {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"}, {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"}, {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"}, + {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"}, + {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"}, + {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"}, + {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"}, {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"}, {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"}, {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"}, @@ -1089,8 +1098,8 @@ entrypoints = [ {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.6.1-py3-none-any.whl", hash = "sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e"}, - {file = "importlib_metadata-4.6.1.tar.gz", hash = "sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac"}, + {file = "importlib_metadata-4.6.3-py3-none-any.whl", hash = "sha256:51c6635429c77cf1ae634c997ff9e53ca3438b495f10a55ba28594dd69764a8b"}, + {file = "importlib_metadata-4.6.3.tar.gz", hash = "sha256:0645585859e9a6689c523927a5032f2ba5919f1f7d0e84bd4533312320de1ff9"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1226,8 +1235,8 @@ nest-asyncio = [ {file = "nest_asyncio-1.5.1.tar.gz", hash = "sha256:afc5a1c515210a23c461932765691ad39e8eba6551c055ac8d5546e69250d0aa"}, ] notebook = [ - {file = "notebook-6.4.0-py3-none-any.whl", hash = "sha256:f7f0a71a999c7967d9418272ae4c3378a220bd28330fbfb49860e46cf8a5838a"}, - {file = "notebook-6.4.0.tar.gz", hash = "sha256:9c4625e2a2aa49d6eae4ce20cbc3d8976db19267e32d2a304880e0c10bf8aef9"}, + {file = "notebook-6.4.1-py3-none-any.whl", hash = "sha256:5d999285fd449898c4dfa0b7880dd881f317c653eb221e8d51d0b5400751ce35"}, + {file = "notebook-6.4.1.tar.gz", hash = "sha256:2a67037730e2b2991f5d684ecb07255bbb191f49234888e71e1fabf8e50679a6"}, ] packaging = [ {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, @@ -1241,8 +1250,8 @@ parso = [ {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, ] pathspec = [ - {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, - {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, @@ -1316,8 +1325,8 @@ pytest-cov = [ {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, ] python-dateutil = [ - {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, - {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] pywin32 = [ {file = "pywin32-301-cp35-cp35m-win32.whl", hash = "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7"}, @@ -1339,81 +1348,71 @@ pywinpty = [ {file = "pywinpty-1.1.3.tar.gz", hash = "sha256:3a1d57b338390333812a5eed31c93c7d8ba82b131078063703e731946d90c9f2"}, ] pyzmq = [ - {file = "pyzmq-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4e9b9a2f6944acdaf57316436c1acdcb30b8df76726bcf570ad9342bc5001654"}, - {file = "pyzmq-22.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24fb5bb641f0b2aa25fc3832f4b6fc62430f14a7d328229fe994b2bcdc07c93a"}, - {file = "pyzmq-22.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c4674004ed64685a38bee222cd75afa769424ec603f9329f0dd4777138337f48"}, - {file = "pyzmq-22.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:461ed80d741692d9457ab820b1cc057ba9c37c394e67b647b639f623c8b321f6"}, - {file = "pyzmq-22.1.0-cp36-cp36m-win32.whl", hash = "sha256:de5806be66c9108e4dcdaced084e8ceae14100aa559e2d57b4f0cceb98c462de"}, - {file = "pyzmq-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a1c77796f395804d6002ff56a6a8168c1f98579896897ad7e35665a9b4a9eec5"}, - {file = "pyzmq-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a81c9e6754465d09a87e3acd74d9bb1f0039b2d785c6899622f0afdb41d760"}, - {file = "pyzmq-22.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0f0f27eaab9ba7b92d73d71c51d1a04464a1da6097a252d007922103253d2313"}, - {file = "pyzmq-22.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4b8fb1b3174b56fd020e4b10232b1764e52cf7f3babcfb460c5253bdc48adad0"}, - {file = "pyzmq-22.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c8fff75af4c7af92dce9f81fa2a83ed009c3e1f33ee8b5222db2ef80b94e242e"}, - {file = "pyzmq-22.1.0-cp37-cp37m-win32.whl", hash = "sha256:cb9f9fe1305ef69b65794655fd89b2209b11bff3e837de981820a8aa051ef914"}, - {file = "pyzmq-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bf80b2cec42d96117248b99d3c86e263a00469c840a778e6cb52d916f4fdf82c"}, - {file = "pyzmq-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0ea7f4237991b0f745a4432c63e888450840bf8cb6c48b93fb7d62864f455529"}, - {file = "pyzmq-22.1.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:12ffcf33db6ba7c0e5aaf901e65517f5e2b719367b80bcbfad692f546a297c7a"}, - {file = "pyzmq-22.1.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d3ecfee2ee8d91ab2e08d2d8e89302c729b244e302bbc39c5b5dde42306ff003"}, - {file = "pyzmq-22.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:68e2c4505992ab5b89f976f89a9135742b18d60068f761bef994a6805f1cae0c"}, - {file = "pyzmq-22.1.0-cp38-cp38-win32.whl", hash = "sha256:285514956c08c7830da9d94e01f5414661a987831bd9f95e4d89cc8aaae8da10"}, - {file = "pyzmq-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5e5be93e1714a59a535bbbc086b9e4fd2448c7547c5288548f6fd86353cad9e"}, - {file = "pyzmq-22.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b2f707b52e09098a7770503e39294ca6e22ae5138ffa1dd36248b6436d23d78e"}, - {file = "pyzmq-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:18dd2ca4540c476558099891c129e6f94109971d110b549db2a9775c817cedbd"}, - {file = "pyzmq-22.1.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:c6d0c32532a0519997e1ded767e184ebb8543bdb351f8eff8570bd461e874efc"}, - {file = "pyzmq-22.1.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9ee48413a2d3cd867fd836737b4c89c24cea1150a37f4856d82d20293fa7519f"}, - {file = "pyzmq-22.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4c4fe69c7dc0d13d4ae180ad650bb900854367f3349d3c16f0569f6c6447f698"}, - {file = "pyzmq-22.1.0-cp39-cp39-win32.whl", hash = "sha256:fc712a90401bcbf3fa25747f189d6dcfccbecc32712701cad25c6355589dac57"}, - {file = "pyzmq-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:68be16107f41563b9f67d93dff1c9f5587e0f76aa8fd91dc04c83d813bcdab1f"}, - {file = "pyzmq-22.1.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:734ea6565c71fc2d03d5b8c7d0d7519c96bb5567e0396da1b563c24a4ac66f0c"}, - {file = "pyzmq-22.1.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:1389b615917d4196962a9b469e947ba862a8ec6f5094a47da5e7a8d404bc07a4"}, - {file = "pyzmq-22.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:41049cff5265e9cd75606aa2c90a76b9c80b98d8fe70ee08cf4af3cedb113358"}, - {file = "pyzmq-22.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f49755684a963731479ff3035d45a8185545b4c9f662d368bd349c419839886d"}, - {file = "pyzmq-22.1.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:6355f81947e1fe6e7bb9e123aeb3067264391d3ebe8402709f824ef8673fa6f3"}, - {file = "pyzmq-22.1.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:089b974ec04d663b8685ac90e86bfe0e4da9d911ff3cf52cb765ff22408b102d"}, - {file = "pyzmq-22.1.0.tar.gz", hash = "sha256:7040d6dd85ea65703904d023d7f57fab793d7ffee9ba9e14f3b897f34ff2415d"}, + {file = "pyzmq-22.2.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b921758f8b5098faa85f341bbdd5e36d5339de5e9032ca2b07d8c8e7bec5069b"}, + {file = "pyzmq-22.2.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:240b83b3a8175b2f616f80092cbb019fcd5c18598f78ffc6aa0ae9034b300f14"}, + {file = "pyzmq-22.2.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:da7f7f3bb08bcf59a6b60b4e53dd8f08bb00c9e61045319d825a906dbb3c8fb7"}, + {file = "pyzmq-22.2.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e66025b64c4724ba683d6d4a4e5ee23de12fe9ae683908f0c7f0f91b4a2fd94e"}, + {file = "pyzmq-22.2.1-cp36-cp36m-win32.whl", hash = "sha256:50d007d5702171bc810c1e74498fa2c7bc5b50f9750697f7fd2a3e71a25aad91"}, + {file = "pyzmq-22.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b4a51c7d906dc263a0cc5590761e53e0a68f2c2fefe549cbef21c9ee5d2d98a4"}, + {file = "pyzmq-22.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:93705cb90baa9d6f75e8448861a1efd3329006f79095ab18846bd1eaa342f7c3"}, + {file = "pyzmq-22.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:620b0abb813958cb3ecb5144c177e26cde92fee6f43c4b9de6b329515532bf27"}, + {file = "pyzmq-22.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2dd3896b3c952cf6c8013deda53c1df16bf962f355b5503d23521e0f6403ae3d"}, + {file = "pyzmq-22.2.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6e9c030222893afa86881d7485d3e841969760a16004bd23e9a83cca28b42778"}, + {file = "pyzmq-22.2.1-cp37-cp37m-win32.whl", hash = "sha256:262f470e7acde18b7217aac78d19d2e29ced91a5afbeb7d98521ebf26461aa7e"}, + {file = "pyzmq-22.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:246f27b88722cfa729bb04881e94484e40b085720d728c1b05133b3f331b0b7b"}, + {file = "pyzmq-22.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0d17bac19e934e9f547a8811b7c2a32651a7840f38086b924e2e3dcb2fae5c3a"}, + {file = "pyzmq-22.2.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5933d1f4087de6e52906f72d92e1e4dcc630d371860b92c55d7f7a4b815a664c"}, + {file = "pyzmq-22.2.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac4497e4b7d134ee53ce5532d9cc3b640d6e71806a55062984e0c99a2f88f465"}, + {file = "pyzmq-22.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66375a6094af72a6098ed4403b15b4db6bf00013c6febc1baa832e7abda827f4"}, + {file = "pyzmq-22.2.1-cp38-cp38-win32.whl", hash = "sha256:b2c16d20bd0aef8e57bc9505fdd80ea0d6008020c3740accd96acf1b3d1b5347"}, + {file = "pyzmq-22.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff345d48940c834168f81fa1d4724675099f148f1ab6369748c4d712ed71bf7c"}, + {file = "pyzmq-22.2.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:f5c84c5de9a773bbf8b22c51e28380999ea72e5e85b4db8edf5e69a7a0d4d9f9"}, + {file = "pyzmq-22.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2534a036b777f957bd6b89b55fb2136775ca2659fb0f1c85036ba78d17d86fd5"}, + {file = "pyzmq-22.2.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a649065413ba4eab92a783a7caa4de8ce14cf46ba8a2a09951426143f1298adb"}, + {file = "pyzmq-22.2.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c9cb0bd3a3cb7ccad3caa1d7b0d18ba71ed3a4a3610028e506a4084371d4d223"}, + {file = "pyzmq-22.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4428302c389fffc0c9c07a78cad5376636b9d096f332acfe66b321ae9ff2c63"}, + {file = "pyzmq-22.2.1-cp39-cp39-win32.whl", hash = "sha256:6a5b4566f66d953601d0d47d4071897f550a265bafd52ebcad5ac7aad3838cbb"}, + {file = "pyzmq-22.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:89200ab6ef9081c72a04ed84c52a50b60dcb0655375aeedb40689bc7c934715e"}, + {file = "pyzmq-22.2.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed67df4eaa99a20d162d76655bda23160abdf8abf82a17f41dfd3962e608dbcc"}, + {file = "pyzmq-22.2.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:021e22a8c58ab294bd4b96448a2ca4e716e1d76600192ff84c33d71edb1fbd37"}, + {file = "pyzmq-22.2.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:200ac096cee5499964c90687306a7244b79ef891f773ed4cf15019fd1f3df330"}, + {file = "pyzmq-22.2.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b3f57bee62e36be5c97712de32237c5589caee0d1154c2ad01a888accfae20bc"}, + {file = "pyzmq-22.2.1.tar.gz", hash = "sha256:6d18c76676771fd891ca8e0e68da0bbfb88e30129835c0ade748016adb3b6242"}, ] regex = [ - {file = "regex-2021.7.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e6a1e5ca97d411a461041d057348e578dc344ecd2add3555aedba3b408c9f874"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6afe6a627888c9a6cfbb603d1d017ce204cebd589d66e0703309b8048c3b0854"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ccb3d2190476d00414aab36cca453e4596e8f70a206e2aa8db3d495a109153d2"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:ed693137a9187052fc46eedfafdcb74e09917166362af4cc4fddc3b31560e93d"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99d8ab206a5270c1002bfcf25c51bf329ca951e5a169f3b43214fdda1f0b5f0d"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:b85ac458354165405c8a84725de7bbd07b00d9f72c31a60ffbf96bb38d3e25fa"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3f5716923d3d0bfb27048242a6e0f14eecdb2e2a7fac47eda1d055288595f222"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5983c19d0beb6af88cb4d47afb92d96751fb3fa1784d8785b1cdf14c6519407"}, - {file = "regex-2021.7.6-cp36-cp36m-win32.whl", hash = "sha256:c92831dac113a6e0ab28bc98f33781383fe294df1a2c3dfd1e850114da35fd5b"}, - {file = "regex-2021.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:791aa1b300e5b6e5d597c37c346fb4d66422178566bbb426dd87eaae475053fb"}, - {file = "regex-2021.7.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59506c6e8bd9306cd8a41511e32d16d5d1194110b8cfe5a11d102d8b63cf945d"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:564a4c8a29435d1f2256ba247a0315325ea63335508ad8ed938a4f14c4116a5d"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:59c00bb8dd8775473cbfb967925ad2c3ecc8886b3b2d0c90a8e2707e06c743f0"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9a854b916806c7e3b40e6616ac9e85d3cdb7649d9e6590653deb5b341a736cec"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:db2b7df831c3187a37f3bb80ec095f249fa276dbe09abd3d35297fc250385694"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:173bc44ff95bc1e96398c38f3629d86fa72e539c79900283afa895694229fe6a"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:15dddb19823f5147e7517bb12635b3c82e6f2a3a6b696cc3e321522e8b9308ad"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ddeabc7652024803666ea09f32dd1ed40a0579b6fbb2a213eba590683025895"}, - {file = "regex-2021.7.6-cp37-cp37m-win32.whl", hash = "sha256:f080248b3e029d052bf74a897b9d74cfb7643537fbde97fe8225a6467fb559b5"}, - {file = "regex-2021.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d8bbce0c96462dbceaa7ac4a7dfbbee92745b801b24bce10a98d2f2b1ea9432f"}, - {file = "regex-2021.7.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edd1a68f79b89b0c57339bce297ad5d5ffcc6ae7e1afdb10f1947706ed066c9c"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:422dec1e7cbb2efbbe50e3f1de36b82906def93ed48da12d1714cabcd993d7f0"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cbe23b323988a04c3e5b0c387fe3f8f363bf06c0680daf775875d979e376bd26"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:0eb2c6e0fcec5e0f1d3bcc1133556563222a2ffd2211945d7b1480c1b1a42a6f"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1c78780bf46d620ff4fff40728f98b8afd8b8e35c3efd638c7df67be2d5cddbf"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bc84fb254a875a9f66616ed4538542fb7965db6356f3df571d783f7c8d256edd"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:598c0a79b4b851b922f504f9f39a863d83ebdfff787261a5ed061c21e67dd761"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875c355360d0f8d3d827e462b29ea7682bf52327d500a4f837e934e9e4656068"}, - {file = "regex-2021.7.6-cp38-cp38-win32.whl", hash = "sha256:e586f448df2bbc37dfadccdb7ccd125c62b4348cb90c10840d695592aa1b29e0"}, - {file = "regex-2021.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:2fe5e71e11a54e3355fa272137d521a40aace5d937d08b494bed4529964c19c4"}, - {file = "regex-2021.7.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6110bab7eab6566492618540c70edd4d2a18f40ca1d51d704f1d81c52d245026"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4f64fc59fd5b10557f6cd0937e1597af022ad9b27d454e182485f1db3008f417"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:89e5528803566af4df368df2d6f503c84fbfb8249e6631c7b025fe23e6bd0cde"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2366fe0479ca0e9afa534174faa2beae87847d208d457d200183f28c74eaea59"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f9392a4555f3e4cb45310a65b403d86b589adc773898c25a39184b1ba4db8985"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:2bceeb491b38225b1fee4517107b8491ba54fba77cf22a12e996d96a3c55613d"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:f98dc35ab9a749276f1a4a38ab3e0e2ba1662ce710f6530f5b0a6656f1c32b58"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:319eb2a8d0888fa6f1d9177705f341bc9455a2c8aca130016e52c7fe8d6c37a3"}, - {file = "regex-2021.7.6-cp39-cp39-win32.whl", hash = "sha256:eaf58b9e30e0e546cdc3ac06cf9165a1ca5b3de8221e9df679416ca667972035"}, - {file = "regex-2021.7.6-cp39-cp39-win_amd64.whl", hash = "sha256:4c9c3155fe74269f61e27617529b7f09552fbb12e44b1189cebbdb24294e6e1c"}, - {file = "regex-2021.7.6.tar.gz", hash = "sha256:8394e266005f2d8c6f0bc6780001f7afa3ef81a7a2111fa35058ded6fce79e4d"}, + {file = "regex-2021.8.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8764a78c5464ac6bde91a8c87dd718c27c1cabb7ed2b4beaf36d3e8e390567f9"}, + {file = "regex-2021.8.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4551728b767f35f86b8e5ec19a363df87450c7376d7419c3cac5b9ceb4bce576"}, + {file = "regex-2021.8.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:577737ec3d4c195c4aef01b757905779a9e9aee608fa1cf0aec16b5576c893d3"}, + {file = "regex-2021.8.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c856ec9b42e5af4fe2d8e75970fcc3a2c15925cbcc6e7a9bcb44583b10b95e80"}, + {file = "regex-2021.8.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3835de96524a7b6869a6c710b26c90e94558c31006e96ca3cf6af6751b27dca1"}, + {file = "regex-2021.8.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cea56288eeda8b7511d507bbe7790d89ae7049daa5f51ae31a35ae3c05408531"}, + {file = "regex-2021.8.3-cp36-cp36m-win32.whl", hash = "sha256:a4eddbe2a715b2dd3849afbdeacf1cc283160b24e09baf64fa5675f51940419d"}, + {file = "regex-2021.8.3-cp36-cp36m-win_amd64.whl", hash = "sha256:57fece29f7cc55d882fe282d9de52f2f522bb85290555b49394102f3621751ee"}, + {file = "regex-2021.8.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a5c6dbe09aff091adfa8c7cfc1a0e83fdb8021ddb2c183512775a14f1435fe16"}, + {file = "regex-2021.8.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff4a8ad9638b7ca52313d8732f37ecd5fd3c8e3aff10a8ccb93176fd5b3812f6"}, + {file = "regex-2021.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b63e3571b24a7959017573b6455e05b675050bbbea69408f35f3cb984ec54363"}, + {file = "regex-2021.8.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fbc20975eee093efa2071de80df7f972b7b35e560b213aafabcec7c0bd00bd8c"}, + {file = "regex-2021.8.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14caacd1853e40103f59571f169704367e79fb78fac3d6d09ac84d9197cadd16"}, + {file = "regex-2021.8.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb350eb1060591d8e89d6bac4713d41006cd4d479f5e11db334a48ff8999512f"}, + {file = "regex-2021.8.3-cp37-cp37m-win32.whl", hash = "sha256:18fdc51458abc0a974822333bd3a932d4e06ba2a3243e9a1da305668bd62ec6d"}, + {file = "regex-2021.8.3-cp37-cp37m-win_amd64.whl", hash = "sha256:026beb631097a4a3def7299aa5825e05e057de3c6d72b139c37813bfa351274b"}, + {file = "regex-2021.8.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:16d9eaa8c7e91537516c20da37db975f09ac2e7772a0694b245076c6d68f85da"}, + {file = "regex-2021.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3905c86cc4ab6d71635d6419a6f8d972cab7c634539bba6053c47354fd04452c"}, + {file = "regex-2021.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937b20955806381e08e54bd9d71f83276d1f883264808521b70b33d98e4dec5d"}, + {file = "regex-2021.8.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:28e8af338240b6f39713a34e337c3813047896ace09d51593d6907c66c0708ba"}, + {file = "regex-2021.8.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c09d88a07483231119f5017904db8f60ad67906efac3f1baa31b9b7f7cca281"}, + {file = "regex-2021.8.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:85f568892422a0e96235eb8ea6c5a41c8ccbf55576a2260c0160800dbd7c4f20"}, + {file = "regex-2021.8.3-cp38-cp38-win32.whl", hash = "sha256:bf6d987edd4a44dd2fa2723fca2790f9442ae4de2c8438e53fcb1befdf5d823a"}, + {file = "regex-2021.8.3-cp38-cp38-win_amd64.whl", hash = "sha256:8fe58d9f6e3d1abf690174fd75800fda9bdc23d2a287e77758dc0e8567e38ce6"}, + {file = "regex-2021.8.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7976d410e42be9ae7458c1816a416218364e06e162b82e42f7060737e711d9ce"}, + {file = "regex-2021.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9569da9e78f0947b249370cb8fadf1015a193c359e7e442ac9ecc585d937f08d"}, + {file = "regex-2021.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bbe342c5b2dec5c5223e7c363f291558bc27982ef39ffd6569e8c082bdc83"}, + {file = "regex-2021.8.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f421e3cdd3a273bace013751c345f4ebeef08f05e8c10757533ada360b51a39"}, + {file = "regex-2021.8.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea212df6e5d3f60341aef46401d32fcfded85593af1d82b8b4a7a68cd67fdd6b"}, + {file = "regex-2021.8.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a3b73390511edd2db2d34ff09aa0b2c08be974c71b4c0505b4a048d5dc128c2b"}, + {file = "regex-2021.8.3-cp39-cp39-win32.whl", hash = "sha256:f35567470ee6dbfb946f069ed5f5615b40edcbb5f1e6e1d3d2b114468d505fc6"}, + {file = "regex-2021.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:bfa6a679410b394600eafd16336b2ce8de43e9b13f7fb9247d84ef5ad2b45e91"}, + {file = "regex-2021.8.3.tar.gz", hash = "sha256:8935937dad2c9b369c3d932b0edbc52a62647c2afb2fafc0c280f14a8bf56a6a"}, ] send2trash = [ {file = "Send2Trash-1.7.1-py3-none-any.whl", hash = "sha256:c20fee8c09378231b3907df9c215ec9766a84ee20053d99fbad854fe8bd42159"}, diff --git a/pyproject.toml b/pyproject.toml index 40479098..32611cef 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.6.0" +version = "10.7.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" authors = ["Will McGugan "] license = "MIT" diff --git a/rich/__main__.py b/rich/__main__.py index 59fbd256..9ba36744 100644 --- a/rich/__main__.py +++ b/rich/__main__.py @@ -9,7 +9,7 @@ from rich.console import ( Console, ConsoleOptions, ConsoleRenderable, - RenderGroup, + Group, RenderResult, RenderableType, ) @@ -88,7 +88,7 @@ def make_test_card() -> Table: ) table.add_row( "Text", - RenderGroup( + Group( Text.from_markup( """Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n""" ), diff --git a/rich/_inspect.py b/rich/_inspect.py index 68043b36..0fd84389 100644 --- a/rich/_inspect.py +++ b/rich/_inspect.py @@ -3,7 +3,7 @@ from __future__ import absolute_import from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature from typing import Any, Iterable, Optional, Tuple -from .console import RenderableType, RenderGroup +from .console import RenderableType, Group from .highlighter import ReprHighlighter from .jupyter import JupyterMixin from .panel import Panel @@ -79,7 +79,7 @@ class Inspect(JupyterMixin): def __rich__(self) -> Panel: return Panel.fit( - RenderGroup(*self._render()), + Group(*self._render()), title=self.title, border_style="scope.border", padding=(0, 1), diff --git a/rich/align.py b/rich/align.py index b1f9b3bb..3f605a7b 100644 --- a/rich/align.py +++ b/rich/align.py @@ -289,7 +289,7 @@ class VerticalCenter(JupyterMixin): if __name__ == "__main__": # pragma: no cover - from rich.console import Console, RenderGroup + from rich.console import Console, Group from rich.highlighter import ReprHighlighter from rich.panel import Panel @@ -297,7 +297,7 @@ if __name__ == "__main__": # pragma: no cover console = Console() panel = Panel( - RenderGroup( + Group( Align.left(highlighter("align='left'")), Align.center(highlighter("align='center'")), Align.right(highlighter("align='right'")), diff --git a/rich/cells.py b/rich/cells.py index e9ce2a02..7f9c0c16 100644 --- a/rich/cells.py +++ b/rich/cells.py @@ -1,5 +1,4 @@ from functools import lru_cache -from logging import StrFormatStyle from typing import Dict, List from ._cell_widths import CELL_WIDTHS diff --git a/rich/color.py b/rich/color.py index 95cad68c..e48bb434 100644 --- a/rich/color.py +++ b/rich/color.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, NamedTuple, Optional, Tuple from ._palettes import EIGHT_BIT_PALETTE, STANDARD_PALETTE, WINDOWS_PALETTE from .color_triplet import ColorTriplet -from .repr import rich_repr, RichReprResult +from .repr import rich_repr, Result from .terminal_theme import DEFAULT_TERMINAL_THEME if TYPE_CHECKING: # pragma: no cover @@ -288,7 +288,7 @@ class Color(NamedTuple): " >", ) - def __rich_repr__(self) -> RichReprResult: + def __rich_repr__(self) -> Result: yield self.name yield self.type yield "number", self.number, None diff --git a/rich/console.py b/rich/console.py index 0056a007..6acefc28 100644 --- a/rich/console.py +++ b/rich/console.py @@ -10,10 +10,10 @@ from dataclasses import dataclass, field from datetime import datetime from functools import wraps from getpass import getpass +from inspect import isclass from itertools import islice from time import monotonic from types import FrameType, TracebackType -from inspect import isclass from typing import ( IO, TYPE_CHECKING, @@ -28,12 +28,10 @@ from typing import ( TextIO, Tuple, Type, - Tuple, Union, cast, ) - if sys.version_info >= (3, 8): from typing import Literal, Protocol, runtime_checkable else: @@ -43,7 +41,6 @@ else: runtime_checkable, ) # pragma: no cover - from . import errors, themes from ._emoji_replace import _emoji_replace from ._log_render import FormatTimeCallable, LogRender @@ -408,7 +405,7 @@ class ScreenContext: """ if renderables: self.screen.renderable = ( - RenderGroup(*renderables) if len(renderables) > 1 else renderables[0] + Group(*renderables) if len(renderables) > 1 else renderables[0] ) if style is not None: self.screen.style = style @@ -432,7 +429,7 @@ class ScreenContext: self.console.show_cursor(True) -class RenderGroup: +class Group: """Takes a group of renderables and returns a renderable object that renders the group. Args: @@ -465,7 +462,10 @@ class RenderGroup: yield from self.renderables -def render_group(fit: bool = True) -> Callable[..., Callable[..., RenderGroup]]: +RenderGroup = Group # TODO: deprecate at some point + + +def group(fit: bool = True) -> Callable[..., Callable[..., Group]]: """A decorator that turns an iterable of renderables in to a group. Args: @@ -474,19 +474,22 @@ def render_group(fit: bool = True) -> Callable[..., Callable[..., RenderGroup]]: def decorator( method: Callable[..., Iterable[RenderableType]] - ) -> Callable[..., RenderGroup]: + ) -> Callable[..., Group]: """Convert a method that returns an iterable of renderables in to a RenderGroup.""" @wraps(method) - def _replace(*args: Any, **kwargs: Any) -> RenderGroup: + def _replace(*args: Any, **kwargs: Any) -> Group: renderables = method(*args, **kwargs) - return RenderGroup(*renderables, fit=fit) + return Group(*renderables, fit=fit) return _replace return decorator +render_group = group + + def _is_jupyter() -> bool: # pragma: no cover """Check if we're running in a Jupyter notebook.""" try: diff --git a/rich/layout.py b/rich/layout.py index 3f93534d..e25ced67 100644 --- a/rich/layout.py +++ b/rich/layout.py @@ -20,7 +20,7 @@ from .console import Console, ConsoleOptions, RenderableType, RenderResult from .highlighter import ReprHighlighter from .panel import Panel from .pretty import Pretty -from .repr import rich_repr, RichReprResult +from .repr import rich_repr, Result from .region import Region from .segment import Segment from .style import StyleType @@ -175,7 +175,7 @@ class Layout: self._render_map: RenderMap = {} self._lock = RLock() - def __rich_repr__(self) -> RichReprResult: + def __rich_repr__(self) -> Result: yield "name", self.name, None yield "size", self.size, None yield "minimum_size", self.minimum_size, 1 diff --git a/rich/markup.py b/rich/markup.py index 67692498..473f86ae 100644 --- a/rich/markup.py +++ b/rich/markup.py @@ -15,6 +15,8 @@ RE_TAGS = re.compile( re.VERBOSE, ) +RE_HANDLER = re.compile(r"^([\w\.]*?)(\(.*?\))?$") + class Tag(NamedTuple): """A tag in console markup.""" @@ -167,17 +169,34 @@ def render( if open_tag.name.startswith("@"): if open_tag.parameters: + handler_name = "" + parameters = open_tag.parameters.strip() + handler_match = RE_HANDLER.match(parameters) + if handler_match is not None: + handler_name, match_parameters = handler_match.groups() + parameters = ( + "()" if match_parameters is None else match_parameters + ) + try: - meta_params = literal_eval(open_tag.parameters) + meta_params = literal_eval(parameters) except SyntaxError as error: raise MarkupError( - f"error parsing {open_tag.parameters!r}; {error.msg}" + f"error parsing {parameters!r} in {open_tag.parameters!r}; {error.msg}" ) except Exception as error: raise MarkupError( f"error parsing {open_tag.parameters!r}; {error}" ) from None + if handler_name: + meta_params = ( + handler_name, + meta_params + if isinstance(meta_params, tuple) + else (meta_params,), + ) + else: meta_params = () @@ -206,22 +225,20 @@ def render( if __name__ == "__main__": # pragma: no cover - from rich.console import Console - from rich.text import Text - - console = Console(highlight=True) - - t = render("[b]Hello[/b] [@click='view.toggle', 'left']World[/]") - console.print(t) - console.print(t._spans) - - console.print("Hello [1], [1,2,3] ['hello']") - console.print("foo") - console.print("Hello [link=https://www.willmcgugan.com]W[b red]o[/]rld[/]!") + MARKUP = [ + "[red]Hello World[/red]", + "[magenta]Hello [b]World[/b]", + "[bold]Bold[italic] bold and italic [/bold]italic[/italic]", + "Click [link=https://www.willmcgugan.com]here[/link] to visit my Blog", + ":warning-emoji: [bold red blink] DANGER![/]", + ] + from rich.table import Table from rich import print - print(escape("[red]")) - print(escape(r"\[red]")) - print(escape(r"\\[red]")) - print(escape(r"\\\[red]")) + grid = Table("Markup", "Result", padding=(0, 1)) + + for markup in MARKUP: + grid.add_row(Text(markup), markup) + + print(grid) diff --git a/rich/progress.py b/rich/progress.py index e6962013..85b68941 100644 --- a/rich/progress.py +++ b/rich/progress.py @@ -25,7 +25,7 @@ from typing import ( ) from . import filesize, get_console -from .console import Console, JustifyMethod, RenderableType, RenderGroup +from .console import Console, JustifyMethod, RenderableType, Group from .highlighter import Highlighter from .jupyter import JupyterMixin from .live import Live @@ -866,7 +866,7 @@ class Progress(JupyterMixin): def get_renderable(self) -> RenderableType: """Get a renderable for the progress display.""" - renderable = RenderGroup(*self.get_renderables()) + renderable = Group(*self.get_renderables()) return renderable def get_renderables(self) -> Iterable[RenderableType]: diff --git a/rich/repr.py b/rich/repr.py index c7efac1a..5fd2f436 100644 --- a/rich/repr.py +++ b/rich/repr.py @@ -18,7 +18,8 @@ from typing import ( T = TypeVar("T") -RichReprResult = Iterable[Union[Any, Tuple[Any], Tuple[str, Any], Tuple[str, Any, Any]]] +Result = Iterable[Union[Any, Tuple[Any], Tuple[str, Any], Tuple[str, Any, Any]]] +RichReprResult = Result class ReprError(Exception): @@ -66,7 +67,7 @@ def auto( else: return f"{self.__class__.__name__}({', '.join(repr_str)})" - def auto_rich_repr(self: Type[T]) -> RichReprResult: + def auto_rich_repr(self: Type[T]) -> Result: """Auto generate __rich_rep__ from signature of __init__""" try: signature = inspect.signature(self.__init__) ## type: ignore @@ -125,7 +126,7 @@ if __name__ == "__main__": @auto class Foo: - def __rich_repr__(self) -> RichReprResult: + def __rich_repr__(self) -> Result: yield "foo" yield "bar", {"shopping": ["eggs", "ham", "pineapple"]} yield "buy", "hand sanitizer" diff --git a/rich/screen.py b/rich/screen.py index e1245467..a2f6a387 100644 --- a/rich/screen.py +++ b/rich/screen.py @@ -12,7 +12,7 @@ if TYPE_CHECKING: ConsoleOptions, RenderResult, RenderableType, - RenderGroup, + Group, ) @@ -32,9 +32,9 @@ class Screen: style: Optional[StyleType] = None, application_mode: bool = False, ) -> None: - from rich.console import RenderGroup + from rich.console import Group - self.renderable = RenderGroup(*renderables) + self.renderable = Group(*renderables) self.style = style self.application_mode = application_mode diff --git a/rich/segment.py b/rich/segment.py index 9c1385b4..97ddc8d0 100644 --- a/rich/segment.py +++ b/rich/segment.py @@ -1,15 +1,23 @@ from enum import IntEnum -from logging import getLogger -from typing import Dict, NamedTuple, Optional - -from .repr import rich_repr, RichReprResult -from .cells import cell_len, set_cell_size, get_character_cell_size -from .style import Style - +from functools import lru_cache from itertools import filterfalse +from logging import getLogger from operator import attrgetter -from typing import cast, Iterable, List, Sequence, Union, Tuple, TYPE_CHECKING +from typing import ( + TYPE_CHECKING, + Dict, + Iterable, + List, + NamedTuple, + Optional, + Sequence, + Tuple, + Union, +) +from .cells import cell_len, get_character_cell_size, set_cell_size +from .repr import Result, rich_repr +from .style import Style if TYPE_CHECKING: from .console import Console, ConsoleOptions, RenderResult @@ -60,7 +68,7 @@ class Segment(NamedTuple): control: Optional[Sequence[ControlCode]] = None """Optional sequence of control codes.""" - def __rich_repr__(self) -> RichReprResult: + def __rich_repr__(self) -> Result: yield self.text if self.control is None: if self.style is not None: @@ -83,22 +91,17 @@ class Segment(NamedTuple): """Check if the segment contains control codes.""" return self.control is not None - def split_cells(self, cut: int) -> Tuple["Segment", "Segment"]: # type: ignore - """Split segment in to two segments at the specified column. + @classmethod + @lru_cache(1024 * 16) + def _split_cells(cls, segment: "Segment", cut: int) -> Tuple["Segment", "Segment"]: # type: ignore - If the cut point falls in the middle of a 2-cell wide character then it is replaced - by two spaces, to preserve the display width of the parent segment. - - Returns: - Tuple[Segment, Segment]: Two segments. - """ - text, style, control = self - assert cut >= 0 + text, style, control = segment _Segment = Segment - if cut >= self.cell_length: - return self, _Segment("", style, control) + if cut >= segment.cell_length: + return segment, _Segment("", style, control) - if len(text) == self.cell_length: + if len(text) == segment.cell_length: + # Fast path with all 1 cell characters return ( _Segment(text[:cut], style, control), _Segment(text[cut:], style, control), @@ -106,7 +109,7 @@ class Segment(NamedTuple): cell_size = get_character_cell_size - pos = int((cut / self.cell_length) * len(text)) + pos = int((cut / segment.cell_length) * len(text)) before = text[:pos] cell_pos = cell_len(before) @@ -131,6 +134,17 @@ class Segment(NamedTuple): _Segment(" " + text[pos:], style, control), ) + def split_cells(self, cut: int) -> Tuple["Segment", "Segment"]: + """Split segment in to two segments at the specified column. + + If the cut point falls in the middle of a 2-cell wide character then it is replaced + by two spaces, to preserve the display width of the parent segment. + + Returns: + Tuple[Segment, Segment]: Two segments. + """ + return self._split_cells(self, cut) + @classmethod def line(cls) -> "Segment": """Make a new line segment.""" @@ -574,9 +588,9 @@ class SegmentLines: if __name__ == "__main__": if __name__ == "__main__": # pragma: no cover + from rich.console import Console from rich.syntax import Syntax from rich.text import Text - from rich.console import Console code = """from rich.console import Console console = Console() diff --git a/rich/style.py b/rich/style.py index d92df0f6..0787c331 100644 --- a/rich/style.py +++ b/rich/style.py @@ -1,13 +1,12 @@ import sys from functools import lru_cache -from marshal import loads as marshal_loads, dumps as marshal_dumps +from marshal import loads, dumps from random import randint -from time import time from typing import Any, cast, Dict, Iterable, List, Optional, Type, Union from . import errors from .color import Color, ColorParseError, ColorSystem, blend_rgb -from .repr import rich_repr, RichReprResult +from .repr import rich_repr, Result from .terminal_theme import DEFAULT_TERMINAL_THEME, TerminalTheme @@ -96,6 +95,31 @@ class Style: 12: "53", } + STYLE_ATTRIBUTES = { + "dim": "dim", + "d": "dim", + "bold": "bold", + "b": "bold", + "italic": "italic", + "i": "italic", + "underline": "underline", + "u": "underline", + "blink": "blink", + "blink2": "blink2", + "reverse": "reverse", + "r": "reverse", + "conceal": "conceal", + "c": "conceal", + "strike": "strike", + "s": "strike", + "underline2": "underline2", + "uu": "underline2", + "frame": "frame", + "encircle": "encircle", + "overline": "overline", + "o": "overline", + } + def __init__( self, *, @@ -165,8 +189,8 @@ class Style: ) self._link = link - self._link_id = f"{time()}-{randint(0, 999999)}" if link else "" - self._meta = None if meta is None else marshal_dumps(meta) + self._link_id = f"{randint(0, 999999)}" if link else "" + self._meta = None if meta is None else dumps(meta) self._hash = hash( ( self._color, @@ -211,11 +235,60 @@ class Style: None, None, None, + None, ) ) style._null = not (color or bgcolor) return style + @classmethod + def from_meta(cls, meta: Optional[Dict[str, Any]]) -> "Style": + """Create a new style with meta data. + + Returns: + meta (Optional[Dict[str, Any]]): A dictionary of meta data. Defaults to None. + """ + style: Style = cls.__new__(Style) + style._ansi = None + style._style_definition = None + style._color = None + style._bgcolor = None + style._set_attributes = 0 + style._attributes = 0 + style._link = None + style._link_id = "" + style._meta = dumps(meta) + style._hash = hash( + ( + None, + None, + None, + None, + None, + style._meta, + ) + ) + style._null = not (meta) + return style + + @classmethod + def on(cls, meta: Optional[Dict[str, Any]] = None, **handlers: Any) -> "Style": + """Create a blank style with meta information. + + Example: + style = Style.on(click=self.on_click) + + Args: + meta (Optiona[Dict[str, Any]], optional): An optional dict of meta information. + **handlers (Any): Keyword arguments are translated in to handlers. + + Returns: + Style: A Style with meta information attached. + """ + meta = {} if meta is None else meta + meta.update({f"@{key}": value for key, value in handlers.items()}) + return cls.from_meta(meta) + bold = _Bit(0) dim = _Bit(1) italic = _Bit(2) @@ -352,7 +425,7 @@ class Style: return value raise ValueError("expected at least one non-None style") - def __rich_repr__(self) -> RichReprResult: + def __rich_repr__(self) -> Result: yield "color", self.color, None yield "bgcolor", self.bgcolor, None yield "bold", self.bold, None, @@ -414,11 +487,7 @@ class Style: @property def meta(self) -> Dict[str, Any]: """Get meta information (can not be changed after construction).""" - return ( - {} - if self._meta is None - else cast(Dict[str, Any], marshal_loads(self._meta)) - ) + return {} if self._meta is None else cast(Dict[str, Any], loads(self._meta)) @property def without_color(self) -> "Style": @@ -433,7 +502,7 @@ class Style: style._attributes = self._attributes style._set_attributes = self._set_attributes style._link = self._link - style._link_id = f"{time()}-{randint(0, 999999)}" if self._link else "" + style._link_id = f"{randint(0, 999999)}" if self._link else "" style._hash = self._hash style._null = False style._meta = None @@ -456,30 +525,7 @@ class Style: if style_definition.strip() == "none" or not style_definition: return cls.null() - style_attributes = { - "dim": "dim", - "d": "dim", - "bold": "bold", - "b": "bold", - "italic": "italic", - "i": "italic", - "underline": "underline", - "u": "underline", - "blink": "blink", - "blink2": "blink2", - "reverse": "reverse", - "r": "reverse", - "conceal": "conceal", - "c": "conceal", - "strike": "strike", - "s": "strike", - "underline2": "underline2", - "uu": "underline2", - "frame": "frame", - "encircle": "encircle", - "overline": "overline", - "o": "overline", - } + STYLE_ATTRIBUTES = cls.STYLE_ATTRIBUTES color: Optional[str] = None bgcolor: Optional[str] = None attributes: Dict[str, Optional[Any]] = {} @@ -502,7 +548,7 @@ class Style: elif word == "not": word = next(words, "") - attribute = style_attributes.get(word) + attribute = STYLE_ATTRIBUTES.get(word) if attribute is None: raise errors.StyleSyntaxError( f"expected style attribute after 'not', found {word!r}" @@ -515,8 +561,8 @@ class Style: raise errors.StyleSyntaxError("URL expected after 'link'") link = word - elif word in style_attributes: - attributes[style_attributes[word]] = True + elif word in STYLE_ATTRIBUTES: + attributes[STYLE_ATTRIBUTES[word]] = True else: try: @@ -608,7 +654,7 @@ class Style: style._attributes = self._attributes style._set_attributes = self._set_attributes style._link = self._link - style._link_id = f"{time()}-{randint(0, 999999)}" if self._link else "" + style._link_id = f"{randint(0, 999999)}" if self._link else "" style._hash = self._hash style._null = False style._meta = self._meta @@ -631,7 +677,7 @@ class Style: style._attributes = self._attributes style._set_attributes = self._set_attributes style._link = link - style._link_id = f"{time()}-{randint(0, 999999)}" if link else "" + style._link_id = f"{randint(0, 999999)}" if link else "" style._hash = self._hash style._null = False style._meta = self._meta @@ -696,7 +742,7 @@ class Style: new_style._hash = style._hash new_style._null = self._null or style._null if self._meta and style._meta: - new_style._meta = marshal_dumps({**self.meta, **style.meta}) + new_style._meta = dumps({**self.meta, **style.meta}) else: new_style._meta = self._meta or style._meta return new_style diff --git a/rich/text.py b/rich/text.py index 22d69104..cce93274 100644 --- a/rich/text.py +++ b/rich/text.py @@ -278,6 +278,7 @@ class Text(JupyterMixin): no_wrap: Optional[bool] = None, end: str = "\n", tab_size: int = 8, + meta: Optional[Dict[str, Any]] = None, ) -> "Text": """Construct a text instance by combining a sequence of strings with optional styles. The positional arguments should be either strings, or a tuple of string + style. @@ -288,6 +289,7 @@ class Text(JupyterMixin): overflow (str, optional): Overflow method: "crop", "fold", "ellipsis". Defaults to None. end (str, optional): Character to end text with. Defaults to "\\\\n". tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8. + meta (Dict[str, Any], optional). Meta data to apply to text, or None for no meta data. Default to None Returns: Text: A new text instance. @@ -307,6 +309,8 @@ class Text(JupyterMixin): append(part) else: append(*part) + if meta: + text.apply_meta(meta) return text @property @@ -390,6 +394,40 @@ class Text(JupyterMixin): return self._spans.append(Span(start, min(length, end), style)) + def apply_meta( + self, meta: Dict[str, Any], start: int = 0, end: Optional[int] = None + ) -> None: + """Apply meta data to the text, or a portion of the text. + + Args: + meta (Dict[str, Any]): A dict of meta information. + start (int): Start offset (negative indexing is supported). Defaults to 0. + end (Optional[int], optional): End offset (negative indexing is supported), or None for end of text. Defaults to None. + + """ + style = Style.from_meta(meta) + self.stylize(style, start=start, end=end) + + def on(self, meta: Optional[Dict[str, Any]] = None, **handlers: Any) -> "Text": + """Apply event handlers (used by Textual project). + + Example: + >>> from rich.text import Text + >>> text = Text("hello world") + >>> text.on(click="view.toggle('world')") + + Args: + meta (Dict[str, Any]): Mapping of meta information. + **handlers: Keyword args are prefixed with "@" to defined handlers. + + Returns: + Text: Self is returned to method may be chained. + """ + meta = {} if meta is None else meta + meta.update({f"@{key}": value for key, value in handlers.items()}) + self.stylize(Style.from_meta(meta)) + return self + def remove_suffix(self, suffix: str) -> None: """Remove a suffix if it exists. diff --git a/rich/traceback.py b/rich/traceback.py index 3182a3ec..4f9012d7 100644 --- a/rich/traceback.py +++ b/rich/traceback.py @@ -21,7 +21,7 @@ from .console import ( ConsoleOptions, ConsoleRenderable, RenderResult, - render_group, + group, ) from .constrain import Constrain from .highlighter import RegexHighlighter, ReprHighlighter @@ -466,7 +466,7 @@ class Traceback: "\n[i]During handling of the above exception, another exception occurred:\n", ) - @render_group() + @group() def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult: highlighter = ReprHighlighter() path_highlighter = PathHighlighter() @@ -481,7 +481,7 @@ class Traceback: syntax_error_text = highlighter(syntax_error.line.rstrip()) syntax_error_text.no_wrap = True offset = min(syntax_error.offset - 1, len(syntax_error_text)) - syntax_error_text.stylize("bold underline", offset, offset + 1) + syntax_error_text.stylize("bold underline", offset, offset) syntax_error_text += Text.from_markup( "\n" + " " * offset + "[traceback.offset]▲[/]", style="pygments.text", @@ -504,7 +504,7 @@ class Traceback: ) return lexer_name - @render_group() + @group() def _render_stack(self, stack: Stack) -> RenderResult: path_highlighter = PathHighlighter() theme = self.theme diff --git a/rich/tree.py b/rich/tree.py index 38c9fa17..5fd46fd4 100644 --- a/rich/tree.py +++ b/rich/tree.py @@ -188,7 +188,7 @@ class Tree(JupyterMixin): if __name__ == "__main__": # pragma: no cover - from rich.console import RenderGroup + from rich.console import Group from rich.markdown import Markdown from rich.panel import Panel from rich.syntax import Syntax @@ -226,17 +226,17 @@ class Segment(NamedTuple): node = root.add(":file_folder: Renderables", guide_style="red") simple_node = node.add(":file_folder: [bold yellow]Atomic", guide_style="uu green") - simple_node.add(RenderGroup("📄 Syntax", syntax)) - simple_node.add(RenderGroup("📄 Markdown", Panel(markdown, border_style="green"))) + simple_node.add(Group("📄 Syntax", syntax)) + simple_node.add(Group("📄 Markdown", Panel(markdown, border_style="green"))) containers_node = node.add( ":file_folder: [bold magenta]Containers", guide_style="bold magenta" ) containers_node.expanded = True panel = Panel.fit("Just a panel", border_style="red") - containers_node.add(RenderGroup("📄 Panels", panel)) + containers_node.add(Group("📄 Panels", panel)) - containers_node.add(RenderGroup("📄 [b magenta]Table", table)) + containers_node.add(Group("📄 [b magenta]Table", table)) console = Console() console.print(root) diff --git a/tests/test_console.py b/tests/test_console.py index 3874d450..a3666a87 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -14,7 +14,7 @@ from rich.console import ( Console, ConsoleDimensions, ConsoleOptions, - render_group, + group, ScreenUpdate, ) from rich.control import Control @@ -467,7 +467,7 @@ def test_out() -> None: def test_render_group() -> None: - @render_group(fit=False) + @group(fit=False) def renderable(): yield "one" yield "two" @@ -481,7 +481,7 @@ def test_render_group() -> None: def test_render_group_fit() -> None: - @render_group() + @group() def renderable(): yield "one" yield "two" diff --git a/tests/test_markup.py b/tests/test_markup.py index 4cc13ac8..24b0c441 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -165,7 +165,28 @@ def test_events(): def test_events_broken(): with pytest.raises(MarkupError): - render("[@click=sdfwer]foo[/]") + render("[@click=sdfwer(sfs)]foo[/]") with pytest.raises(MarkupError): render("[@click='view.toggle]foo[/]") + + +def test_render_meta(): + console = Console() + text = render("foo[@click=close]bar[/]baz") + assert text.get_style_at_offset(console, 3).meta == {"@click": ("close", ())} + + text = render("foo[@click=close()]bar[/]baz") + assert text.get_style_at_offset(console, 3).meta == {"@click": ("close", ())} + + text = render("foo[@click=close('dialog')]bar[/]baz") + assert text.get_style_at_offset(console, 3).meta == { + "@click": ("close", ("dialog",)) + } + text = render("foo[@click=close('dialog', 3)]bar[/]baz") + assert text.get_style_at_offset(console, 3).meta == { + "@click": ("close", ("dialog", 3)) + } + + text = render("foo[@click=(1, 2, 3)]bar[/]baz") + assert text.get_style_at_offset(console, 3).meta == {"@click": (1, 2, 3)} diff --git a/tests/test_style.py b/tests/test_style.py index d64d3783..7c17dc3c 100644 --- a/tests/test_style.py +++ b/tests/test_style.py @@ -219,3 +219,14 @@ def test_meta(): assert style.meta == {"foo": "bar", "egg": "baz"} assert repr(style) == "Style(bold=True, meta={'foo': 'bar', 'egg': 'baz'})" + + +def test_from_meta(): + style = Style.from_meta({"foo": "bar"}) + assert style.color is None + assert style.bold is None + + +def test_on(): + style = Style.on({"foo": "bar"}, click="CLICK") + Style(color="red") + assert style.meta == {"foo": "bar", "@click": "CLICK"} diff --git a/tests/test_text.py b/tests/test_text.py index 5c5a30d7..3727d160 100644 --- a/tests/test_text.py +++ b/tests/test_text.py @@ -542,6 +542,14 @@ def test_assemble(): assert text._spans == [Span(3, 6, "bold")] +def test_assemble_meta(): + text = Text.assemble("foo", ("bar", "bold"), meta={"foo": "bar"}) + assert str(text) == "foobar" + assert text._spans == [Span(3, 6, "bold"), Span(0, 6, Style(meta={"foo": "bar"}))] + console = Console() + assert text.get_style_at_offset(console, 0).meta == {"foo": "bar"} + + def test_styled(): text = Text.styled("foo", "bold red") assert text.style == "" @@ -676,3 +684,24 @@ def test_wrap_invalid_style(): console = Console(width=100, color_system="truecolor") a = "[#######.................] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx [#######.................]" console.print(a, justify="full") + + +def test_apply_meta(): + text = Text("foobar") + text.apply_meta({"foo": "bar"}, 1, 3) + + console = Console() + assert text.get_style_at_offset(console, 0).meta == {} + assert text.get_style_at_offset(console, 1).meta == {"foo": "bar"} + assert text.get_style_at_offset(console, 2).meta == {"foo": "bar"} + assert text.get_style_at_offset(console, 3).meta == {} + + +def test_on(): + console = Console() + text = Text("foo") + text.on({"foo": "bar"}, click="CLICK") + expected = {"foo": "bar", "@click": "CLICK"} + assert text.get_style_at_offset(console, 0).meta == expected + assert text.get_style_at_offset(console, 1).meta == expected + assert text.get_style_at_offset(console, 2).meta == expected