mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Rich log (#3046)
* log * tests * snapshot tests * change to richlog * keep raw lines * disable highlighting by default * simplify * superfluous test * optimization * update cell length * add refresh * write method * version bump * doc fix link * makes lines private * docstring * relax dev dependancy * remove superfluous code [skip ci] * added FAQ [skipci] * fix code in faq [skipci] * fix typo * max lines fix
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [0.32.0] - 2023-08-03
|
||||
|
||||
### Added
|
||||
|
||||
- Added widgets.Log
|
||||
- Added Widget.is_vertical_scroll_end, Widget.is_horizontal_scroll_end, Widget.is_vertical_scrollbar_grabbed, Widget.is_horizontal_scrollbar_grabbed
|
||||
|
||||
### Changed
|
||||
|
||||
- Breaking change: Renamed TextLog to RichLog
|
||||
|
||||
## [0.31.0] - 2023-08-01
|
||||
|
||||
### Added
|
||||
|
||||
28
FAQ.md
28
FAQ.md
@@ -7,8 +7,9 @@
|
||||
- [How can I select and copy text in a Textual app?](#how-can-i-select-and-copy-text-in-a-textual-app)
|
||||
- [How can I set a translucent app background?](#how-can-i-set-a-translucent-app-background)
|
||||
- [How do I center a widget in a screen?](#how-do-i-center-a-widget-in-a-screen)
|
||||
- [How do I fixed WorkerDeclarationError?](#how-do-i-fixed-workerdeclarationerror)
|
||||
- [How do I fix WorkerDeclarationError?](#how-do-i-fix-workerdeclarationerror)
|
||||
- [How do I pass arguments to an app?](#how-do-i-pass-arguments-to-an-app)
|
||||
- [No widget called TextLog](#no-widget-called-textlog)
|
||||
- [Why do some key combinations never make it to my app?](#why-do-some-key-combinations-never-make-it-to-my-app)
|
||||
- [Why doesn't Textual look good on macOS?](#why-doesn't-textual-look-good-on-macos)
|
||||
- [Why doesn't Textual support ANSI themes?](#why-doesn't-textual-support-ansi-themes)
|
||||
@@ -145,8 +146,8 @@ if __name__ == "__main__":
|
||||
ButtonApp().run()
|
||||
```
|
||||
|
||||
<a name="how-do-i-fixed-workerdeclarationerror"></a>
|
||||
## How do I fixed WorkerDeclarationError?
|
||||
<a name="how-do-i-fix-workerdeclarationerror"></a>
|
||||
## How do I fix WorkerDeclarationError?
|
||||
|
||||
Textual version 0.31.0 requires that you set `thread=True` on the `@work` decorator if you want to run a threaded worker.
|
||||
|
||||
@@ -158,15 +159,15 @@ def run_in_background():
|
||||
...
|
||||
```
|
||||
|
||||
If you *don't* want a worker, you should make your work function `async`:
|
||||
If you *don't* want a threaded worker, you should make your work function `async`:
|
||||
|
||||
```python
|
||||
@work():
|
||||
@work()
|
||||
async def run_in_background():
|
||||
...
|
||||
```
|
||||
|
||||
This change was made because it was easy to accidentally created a threaded worker, which may produce unexpected results.
|
||||
This change was made because it was too easy to accidentally create a threaded worker, which may produce unexpected results.
|
||||
|
||||
<a name="how-do-i-pass-arguments-to-an-app"></a>
|
||||
## How do I pass arguments to an app?
|
||||
@@ -195,13 +196,26 @@ Then the app can be run, passing in various arguments; for example:
|
||||
# Running with default arguments.
|
||||
Greetings().run()
|
||||
|
||||
# Running with a keyword arguyment.
|
||||
# Running with a keyword argument.
|
||||
Greetings(to_greet="davep").run()
|
||||
|
||||
# Running with both positional arguments.
|
||||
Greetings("Well hello", "there").run()
|
||||
```
|
||||
|
||||
<a name="no-widget-called-textlog"></a>
|
||||
## No widget called TextLog
|
||||
|
||||
The `TextLog` widget was renamed to `RichLog` in Textual 0.32.0.
|
||||
You will need to replace all references to `TextLog` in your code, with `RichLog`.
|
||||
Most IDEs will have a search and replace function which will help you do this.
|
||||
|
||||
Here's how you should import RichLog:
|
||||
|
||||
```python
|
||||
from textual.widgets import RichLog
|
||||
```
|
||||
|
||||
<a name="why-do-some-key-combinations-never-make-it-to-my-app"></a>
|
||||
## Why do some key combinations never make it to my app?
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import TextLog
|
||||
from textual import events
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
class InputApp(App):
|
||||
"""App to display key events."""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
yield RichLog()
|
||||
|
||||
def on_key(self, event: events.Key) -> None:
|
||||
self.query_one(TextLog).write(event)
|
||||
self.query_one(RichLog).write(event)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import TextLog
|
||||
from textual import events
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
class InputApp(App):
|
||||
"""App to display key events."""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
yield RichLog()
|
||||
|
||||
def on_key(self, event: events.Key) -> None:
|
||||
self.query_one(TextLog).write(event)
|
||||
self.query_one(RichLog).write(event)
|
||||
|
||||
def key_space(self) -> None:
|
||||
self.bell()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import TextLog
|
||||
from textual import events
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
class KeyLogger(TextLog):
|
||||
class KeyLogger(RichLog):
|
||||
def on_key(self, event: events.Key) -> None:
|
||||
self.write(event)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from textual import events
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Container
|
||||
from textual.widgets import Static, TextLog
|
||||
from textual.widgets import RichLog, Static
|
||||
|
||||
|
||||
class PlayArea(Container):
|
||||
@@ -9,7 +9,7 @@ class PlayArea(Container):
|
||||
self.capture_mouse()
|
||||
|
||||
def on_mouse_move(self, event: events.MouseMove) -> None:
|
||||
self.screen.query_one(TextLog).write(event)
|
||||
self.screen.query_one(RichLog).write(event)
|
||||
self.query_one(Ball).offset = event.offset - (8, 2)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class MouseApp(App):
|
||||
CSS_PATH = "mouse01.css"
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
yield RichLog()
|
||||
yield PlayArea(Ball("Textual"))
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Horizontal
|
||||
from textual.reactive import reactive
|
||||
from textual.widgets import Button, TextLog
|
||||
from textual.widgets import Button, RichLog
|
||||
|
||||
|
||||
class ValidateApp(App):
|
||||
@@ -23,14 +23,14 @@ class ValidateApp(App):
|
||||
Button("-1", id="minus", variant="error"),
|
||||
id="buttons",
|
||||
)
|
||||
yield TextLog(highlight=True)
|
||||
yield RichLog(highlight=True)
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id == "plus":
|
||||
self.count += 1
|
||||
else:
|
||||
self.count -= 1
|
||||
self.query_one(TextLog).write(f"{self.count=}")
|
||||
self.query_one(RichLog).write(f"{self.count=}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
28
docs/examples/widgets/log.py
Normal file
28
docs/examples/widgets/log.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Log
|
||||
|
||||
TEXT = """I must not fear.
|
||||
Fear is the mind-killer.
|
||||
Fear is the little-death that brings total obliteration.
|
||||
I will face my fear.
|
||||
I will permit it to pass over me and through me.
|
||||
And when it has gone past, I will turn the inner eye to see its path.
|
||||
Where the fear has gone there will be nothing. Only I will remain."""
|
||||
|
||||
|
||||
class LogApp(App):
|
||||
"""An app with a simple log."""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Log()
|
||||
|
||||
def on_ready(self) -> None:
|
||||
log = self.query_one(Log)
|
||||
log.write_line("Hello, World!")
|
||||
for _ in range(10):
|
||||
log.write_line(TEXT)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = LogApp()
|
||||
app.run()
|
||||
@@ -1,13 +1,12 @@
|
||||
import csv
|
||||
import io
|
||||
|
||||
from rich.table import Table
|
||||
from rich.syntax import Syntax
|
||||
from rich.table import Table
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual import events
|
||||
from textual.widgets import TextLog
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import RichLog
|
||||
|
||||
CSV = """lane,swimmer,country,time
|
||||
4,Joseph Schooling,Singapore,50.39
|
||||
@@ -37,13 +36,13 @@ def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]:
|
||||
'''
|
||||
|
||||
|
||||
class TextLogApp(App):
|
||||
class RichLogApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog(highlight=True, markup=True)
|
||||
yield RichLog(highlight=True, markup=True)
|
||||
|
||||
def on_ready(self) -> None:
|
||||
"""Called when the DOM is ready."""
|
||||
text_log = self.query_one(TextLog)
|
||||
text_log = self.query_one(RichLog)
|
||||
|
||||
text_log.write(Syntax(CODE, "python", indent_guides=True))
|
||||
|
||||
@@ -57,10 +56,10 @@ class TextLogApp(App):
|
||||
|
||||
def on_key(self, event: events.Key) -> None:
|
||||
"""Write Key events to log."""
|
||||
text_log = self.query_one(TextLog)
|
||||
text_log = self.query_one(RichLog)
|
||||
text_log.write(event)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TextLogApp()
|
||||
app = RichLogApp()
|
||||
app.run()
|
||||
@@ -23,7 +23,7 @@ The most fundamental way to receive input is via [Key][textual.events.Key] event
|
||||
```{.textual path="docs/examples/guide/input/key01.py", press="T,e,x,t,u,a,l,!"}
|
||||
```
|
||||
|
||||
When you press a key, the app will receive the event and write it to a [TextLog](../widgets/text_log.md) widget. Try pressing a few keys to see what happens.
|
||||
When you press a key, the app will receive the event and write it to a [RichLog](../widgets/rich_log.md) widget. Try pressing a few keys to see what happens.
|
||||
|
||||
!!! tip
|
||||
|
||||
@@ -105,7 +105,7 @@ The following example shows how focus works in practice.
|
||||
```{.textual path="docs/examples/guide/input/key03.py", press="H,e,l,l,o,tab,W,o,r,l,d,!"}
|
||||
```
|
||||
|
||||
The app splits the screen in to quarters, with a `TextLog` widget in each quarter. If you click any of the text logs, you should see that it is highlighted to show that the widget has focus. Key events will be sent to the focused widget only.
|
||||
The app splits the screen in to quarters, with a `RichLog` widget in each quarter. If you click any of the text logs, you should see that it is highlighted to show that the widget has focus. Key events will be sent to the focused widget only.
|
||||
|
||||
!!! tip
|
||||
|
||||
|
||||
@@ -434,7 +434,7 @@ You should find that if you move the mouse over the widget now, it will highligh
|
||||
The following builtin widgets use the Line API. If you are building advanced widgets, it may be worth looking through the code for inspiration!
|
||||
|
||||
- [DataTable](https://github.com/Textualize/textual/blob/main/src/textual/widgets/_data_table.py)
|
||||
- [TextLog](https://github.com/Textualize/textual/blob/main/src/textual/widgets/_text_log.py)
|
||||
- [RichLog](https://github.com/Textualize/textual/blob/main/src/textual/widgets/_rich_log.py)
|
||||
- [Tree](https://github.com/Textualize/textual/blob/main/src/textual/widgets/_tree.py)
|
||||
|
||||
## Compound widgets
|
||||
|
||||
@@ -118,6 +118,15 @@ Display an animation while data is loading.
|
||||
```{.textual path="docs/examples/widgets/loading_indicator.py"}
|
||||
```
|
||||
|
||||
## Log
|
||||
|
||||
Display and update lines of text (such as from a file).
|
||||
|
||||
[Log reference](./widgets/log.md){ .md-button .md-button--primary }
|
||||
|
||||
```{.textual path="docs/examples/widgets/log.py"}
|
||||
```
|
||||
|
||||
## MarkdownViewer
|
||||
|
||||
Display and interact with a Markdown document (adds a table of contents and browser-like navigation to Markdown).
|
||||
@@ -198,6 +207,15 @@ A collection of radio buttons, that enforces uniqueness.
|
||||
```{.textual path="docs/examples/widgets/radio_set.py"}
|
||||
```
|
||||
|
||||
## RichLog
|
||||
|
||||
Display and update text in a scrolling panel.
|
||||
|
||||
[RichLog reference](./widgets/rich_log.md){ .md-button .md-button--primary }
|
||||
|
||||
```{.textual path="docs/examples/widgets/rich_log.py" press="H,i"}
|
||||
```
|
||||
|
||||
## Select
|
||||
|
||||
Select from a number of possible options.
|
||||
@@ -260,14 +278,6 @@ A Combination of Tabs and ContentSwitcher to navigate static content.
|
||||
```{.textual path="docs/examples/widgets/tabbed_content.py" press="j"}
|
||||
```
|
||||
|
||||
## TextLog
|
||||
|
||||
Display and update text in a scrolling panel.
|
||||
|
||||
[TextLog reference](./widgets/text_log.md){ .md-button .md-button--primary }
|
||||
|
||||
```{.textual path="docs/examples/widgets/text_log.py" press="H,i"}
|
||||
```
|
||||
|
||||
## Tree
|
||||
|
||||
|
||||
51
docs/widgets/log.md
Normal file
51
docs/widgets/log.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Log
|
||||
|
||||
!!! tip "Added in version 0.32.0"
|
||||
|
||||
A Log widget displays lines of text which may be appended to in realtime.
|
||||
|
||||
Call [Log.write_line][textual.widgets.Log.write_line] to write a line at a time, or [Log.write_lines][textual.widgets.Log.write_lines] to write multiple lines at once. Call [Log.clear][textual.widgets.Log.clear] to clear the Log widget.
|
||||
|
||||
!!! tip
|
||||
|
||||
See also [RichLog](../widgets/rich_log.md) which can write more than just text, and supports a number of advanced features.
|
||||
|
||||
- [X] Focusable
|
||||
- [ ] Container
|
||||
|
||||
## Example
|
||||
|
||||
The example below shows how to write text to a `Log` widget:
|
||||
|
||||
=== "Output"
|
||||
|
||||
```{.textual path="docs/examples/widgets/log.py"}
|
||||
```
|
||||
|
||||
=== "log.py"
|
||||
|
||||
```python
|
||||
--8<-- "docs/examples/widgets/log.py"
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Reactive Attributes
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| ------------- | ------ | ------- | ------------------------------------------------------------ |
|
||||
| `max_lines` | `int` | `None` | Maximum number of lines in the log or `None` for no maximum. |
|
||||
| `auto_scroll` | `bool` | `False` | Scroll to end of log when new lines are added. |
|
||||
|
||||
|
||||
## Messages
|
||||
|
||||
This widget sends no messages.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
::: textual.widgets.Log
|
||||
options:
|
||||
heading_level: 2
|
||||
@@ -1,25 +1,29 @@
|
||||
# TextLog
|
||||
# RichLog
|
||||
|
||||
A TextLog is a widget which displays scrollable content that may be appended to in realtime.
|
||||
A RichLog is a widget which displays scrollable content that may be appended to in realtime.
|
||||
|
||||
Call [TextLog.write][textual.widgets.TextLog.write] with a string or [Rich Renderable](https://rich.readthedocs.io/en/latest/protocol.html) to write content to the end of the TextLog. Call [TextLog.clear][textual.widgets.TextLog.clear] to clear the content.
|
||||
Call [RichLog.write][textual.widgets.RichLog.write] with a string or [Rich Renderable](https://rich.readthedocs.io/en/latest/protocol.html) to write content to the end of the RichLog. Call [RichLog.clear][textual.widgets.RichLog.clear] to clear the content.
|
||||
|
||||
!!! tip
|
||||
|
||||
See also [Log](../widgets/log.md) which is an alternative to `RichLog` but specialized for simple text.
|
||||
|
||||
- [X] Focusable
|
||||
- [ ] Container
|
||||
|
||||
## Example
|
||||
|
||||
The example below shows an application showing a `TextLog` with different kinds of data logged.
|
||||
The example below shows an application showing a `RichLog` with different kinds of data logged.
|
||||
|
||||
=== "Output"
|
||||
|
||||
```{.textual path="docs/examples/widgets/text_log.py" press="H,i"}
|
||||
```{.textual path="docs/examples/widgets/rich_log.py" press="H,i"}
|
||||
```
|
||||
|
||||
=== "text_log.py"
|
||||
=== "rich_log.py"
|
||||
|
||||
```python
|
||||
--8<-- "docs/examples/widgets/text_log.py"
|
||||
--8<-- "docs/examples/widgets/rich_log.py"
|
||||
```
|
||||
|
||||
|
||||
@@ -42,6 +46,6 @@ This widget sends no messages.
|
||||
---
|
||||
|
||||
|
||||
::: textual.widgets.TextLog
|
||||
::: textual.widgets.RichLog
|
||||
options:
|
||||
heading_level: 2
|
||||
405
mkdocs-nav.yml
405
mkdocs-nav.yml
@@ -1,203 +1,204 @@
|
||||
nav:
|
||||
- Introduction:
|
||||
- "index.md"
|
||||
- "getting_started.md"
|
||||
- "help.md"
|
||||
- "tutorial.md"
|
||||
- Guide:
|
||||
- "guide/index.md"
|
||||
- "guide/devtools.md"
|
||||
- "guide/app.md"
|
||||
- "guide/styles.md"
|
||||
- "guide/CSS.md"
|
||||
- "guide/design.md"
|
||||
- "guide/queries.md"
|
||||
- "guide/layout.md"
|
||||
- "guide/events.md"
|
||||
- "guide/input.md"
|
||||
- "guide/actions.md"
|
||||
- "guide/reactivity.md"
|
||||
- "guide/widgets.md"
|
||||
- "guide/animation.md"
|
||||
- "guide/screens.md"
|
||||
- "guide/workers.md"
|
||||
- "widget_gallery.md"
|
||||
- Reference:
|
||||
- "reference/index.md"
|
||||
- CSS Types:
|
||||
- "css_types/index.md"
|
||||
- "css_types/border.md"
|
||||
- "css_types/color.md"
|
||||
- "css_types/horizontal.md"
|
||||
- "css_types/integer.md"
|
||||
- "css_types/name.md"
|
||||
- "css_types/number.md"
|
||||
- "css_types/overflow.md"
|
||||
- "css_types/percentage.md"
|
||||
- "css_types/scalar.md"
|
||||
- "css_types/text_align.md"
|
||||
- "css_types/text_style.md"
|
||||
- "css_types/vertical.md"
|
||||
- Events:
|
||||
- "events/index.md"
|
||||
- "events/blur.md"
|
||||
- "events/descendant_blur.md"
|
||||
- "events/descendant_focus.md"
|
||||
- "events/enter.md"
|
||||
- "events/focus.md"
|
||||
- "events/hide.md"
|
||||
- "events/key.md"
|
||||
- "events/leave.md"
|
||||
- "events/load.md"
|
||||
- "events/mount.md"
|
||||
- "events/mouse_capture.md"
|
||||
- "events/click.md"
|
||||
- "events/mouse_down.md"
|
||||
- "events/mouse_move.md"
|
||||
- "events/mouse_release.md"
|
||||
- "events/mouse_scroll_down.md"
|
||||
- "events/mouse_scroll_up.md"
|
||||
- "events/mouse_up.md"
|
||||
- "events/paste.md"
|
||||
- "events/resize.md"
|
||||
- "events/screen_resume.md"
|
||||
- "events/screen_suspend.md"
|
||||
- "events/show.md"
|
||||
- Styles:
|
||||
- "styles/align.md"
|
||||
- "styles/background.md"
|
||||
- "styles/border.md"
|
||||
- "styles/border_subtitle_align.md"
|
||||
- "styles/border_subtitle_background.md"
|
||||
- "styles/border_subtitle_color.md"
|
||||
- "styles/border_subtitle_style.md"
|
||||
- "styles/border_title_align.md"
|
||||
- "styles/border_title_background.md"
|
||||
- "styles/border_title_color.md"
|
||||
- "styles/border_title_style.md"
|
||||
- "styles/box_sizing.md"
|
||||
- "styles/color.md"
|
||||
- "styles/content_align.md"
|
||||
- "styles/display.md"
|
||||
- "styles/dock.md"
|
||||
- "styles/index.md"
|
||||
- Grid:
|
||||
- "styles/grid/index.md"
|
||||
- "styles/grid/column_span.md"
|
||||
- "styles/grid/grid_columns.md"
|
||||
- "styles/grid/grid_gutter.md"
|
||||
- "styles/grid/grid_rows.md"
|
||||
- "styles/grid/grid_size.md"
|
||||
- "styles/grid/row_span.md"
|
||||
- "styles/height.md"
|
||||
- "styles/layer.md"
|
||||
- "styles/layers.md"
|
||||
- "styles/layout.md"
|
||||
- Links:
|
||||
- "styles/links/index.md"
|
||||
- "styles/links/link_background.md"
|
||||
- "styles/links/link_color.md"
|
||||
- "styles/links/link_hover_background.md"
|
||||
- "styles/links/link_hover_color.md"
|
||||
- "styles/links/link_hover_style.md"
|
||||
- "styles/links/link_style.md"
|
||||
- "styles/margin.md"
|
||||
- "styles/max_height.md"
|
||||
- "styles/max_width.md"
|
||||
- "styles/min_height.md"
|
||||
- "styles/min_width.md"
|
||||
- "styles/offset.md"
|
||||
- "styles/opacity.md"
|
||||
- "styles/outline.md"
|
||||
- "styles/overflow.md"
|
||||
- "styles/padding.md"
|
||||
- Scrollbar colors:
|
||||
- "styles/scrollbar_colors/index.md"
|
||||
- "styles/scrollbar_colors/scrollbar_background.md"
|
||||
- "styles/scrollbar_colors/scrollbar_background_active.md"
|
||||
- "styles/scrollbar_colors/scrollbar_background_hover.md"
|
||||
- "styles/scrollbar_colors/scrollbar_color.md"
|
||||
- "styles/scrollbar_colors/scrollbar_color_active.md"
|
||||
- "styles/scrollbar_colors/scrollbar_color_hover.md"
|
||||
- "styles/scrollbar_colors/scrollbar_corner_color.md"
|
||||
- "styles/scrollbar_gutter.md"
|
||||
- "styles/scrollbar_size.md"
|
||||
- "styles/text_align.md"
|
||||
- "styles/text_opacity.md"
|
||||
- "styles/text_style.md"
|
||||
- "styles/tint.md"
|
||||
- "styles/visibility.md"
|
||||
- "styles/width.md"
|
||||
- Widgets:
|
||||
- "widgets/button.md"
|
||||
- "widgets/checkbox.md"
|
||||
- "widgets/content_switcher.md"
|
||||
- "widgets/data_table.md"
|
||||
- "widgets/directory_tree.md"
|
||||
- "widgets/footer.md"
|
||||
- "widgets/header.md"
|
||||
- "widgets/index.md"
|
||||
- "widgets/input.md"
|
||||
- "widgets/label.md"
|
||||
- "widgets/list_item.md"
|
||||
- "widgets/list_view.md"
|
||||
- "widgets/loading_indicator.md"
|
||||
- "widgets/markdown_viewer.md"
|
||||
- "widgets/markdown.md"
|
||||
- "widgets/option_list.md"
|
||||
- "widgets/placeholder.md"
|
||||
- "widgets/pretty.md"
|
||||
- "widgets/progress_bar.md"
|
||||
- "widgets/radiobutton.md"
|
||||
- "widgets/radioset.md"
|
||||
- "widgets/select.md"
|
||||
- "widgets/selection_list.md"
|
||||
- "widgets/sparkline.md"
|
||||
- "widgets/static.md"
|
||||
- "widgets/switch.md"
|
||||
- "widgets/tabbed_content.md"
|
||||
- "widgets/tabs.md"
|
||||
- "widgets/text_log.md"
|
||||
- "widgets/tree.md"
|
||||
- API:
|
||||
- "api/index.md"
|
||||
- "api/app.md"
|
||||
- "api/await_remove.md"
|
||||
- "api/binding.md"
|
||||
- "api/color.md"
|
||||
- "api/containers.md"
|
||||
- "api/coordinate.md"
|
||||
- "api/dom_node.md"
|
||||
- "api/events.md"
|
||||
- "api/errors.md"
|
||||
- "api/filter.md"
|
||||
- "api/geometry.md"
|
||||
- "api/logger.md"
|
||||
- "api/logging.md"
|
||||
- "api/map_geometry.md"
|
||||
- "api/message_pump.md"
|
||||
- "api/message.md"
|
||||
- "api/on.md"
|
||||
- "api/pilot.md"
|
||||
- "api/query.md"
|
||||
- "api/reactive.md"
|
||||
- "api/screen.md"
|
||||
- "api/scrollbar.md"
|
||||
- "api/scroll_view.md"
|
||||
- "api/strip.md"
|
||||
- "api/suggester.md"
|
||||
- "api/timer.md"
|
||||
- "api/types.md"
|
||||
- "api/validation.md"
|
||||
- "api/walk.md"
|
||||
- "api/widget.md"
|
||||
- "api/work.md"
|
||||
- "api/worker.md"
|
||||
- "api/worker_manager.md"
|
||||
- "How To":
|
||||
- "how-to/index.md"
|
||||
- "how-to/center-things.md"
|
||||
- "how-to/design-a-layout.md"
|
||||
- "roadmap.md"
|
||||
- "Blog":
|
||||
- blog/index.md
|
||||
- Introduction:
|
||||
- "index.md"
|
||||
- "getting_started.md"
|
||||
- "help.md"
|
||||
- "tutorial.md"
|
||||
- Guide:
|
||||
- "guide/index.md"
|
||||
- "guide/devtools.md"
|
||||
- "guide/app.md"
|
||||
- "guide/styles.md"
|
||||
- "guide/CSS.md"
|
||||
- "guide/design.md"
|
||||
- "guide/queries.md"
|
||||
- "guide/layout.md"
|
||||
- "guide/events.md"
|
||||
- "guide/input.md"
|
||||
- "guide/actions.md"
|
||||
- "guide/reactivity.md"
|
||||
- "guide/widgets.md"
|
||||
- "guide/animation.md"
|
||||
- "guide/screens.md"
|
||||
- "guide/workers.md"
|
||||
- "widget_gallery.md"
|
||||
- Reference:
|
||||
- "reference/index.md"
|
||||
- CSS Types:
|
||||
- "css_types/index.md"
|
||||
- "css_types/border.md"
|
||||
- "css_types/color.md"
|
||||
- "css_types/horizontal.md"
|
||||
- "css_types/integer.md"
|
||||
- "css_types/name.md"
|
||||
- "css_types/number.md"
|
||||
- "css_types/overflow.md"
|
||||
- "css_types/percentage.md"
|
||||
- "css_types/scalar.md"
|
||||
- "css_types/text_align.md"
|
||||
- "css_types/text_style.md"
|
||||
- "css_types/vertical.md"
|
||||
- Events:
|
||||
- "events/index.md"
|
||||
- "events/blur.md"
|
||||
- "events/descendant_blur.md"
|
||||
- "events/descendant_focus.md"
|
||||
- "events/enter.md"
|
||||
- "events/focus.md"
|
||||
- "events/hide.md"
|
||||
- "events/key.md"
|
||||
- "events/leave.md"
|
||||
- "events/load.md"
|
||||
- "events/mount.md"
|
||||
- "events/mouse_capture.md"
|
||||
- "events/click.md"
|
||||
- "events/mouse_down.md"
|
||||
- "events/mouse_move.md"
|
||||
- "events/mouse_release.md"
|
||||
- "events/mouse_scroll_down.md"
|
||||
- "events/mouse_scroll_up.md"
|
||||
- "events/mouse_up.md"
|
||||
- "events/paste.md"
|
||||
- "events/resize.md"
|
||||
- "events/screen_resume.md"
|
||||
- "events/screen_suspend.md"
|
||||
- "events/show.md"
|
||||
- Styles:
|
||||
- "styles/align.md"
|
||||
- "styles/background.md"
|
||||
- "styles/border.md"
|
||||
- "styles/border_subtitle_align.md"
|
||||
- "styles/border_subtitle_background.md"
|
||||
- "styles/border_subtitle_color.md"
|
||||
- "styles/border_subtitle_style.md"
|
||||
- "styles/border_title_align.md"
|
||||
- "styles/border_title_background.md"
|
||||
- "styles/border_title_color.md"
|
||||
- "styles/border_title_style.md"
|
||||
- "styles/box_sizing.md"
|
||||
- "styles/color.md"
|
||||
- "styles/content_align.md"
|
||||
- "styles/display.md"
|
||||
- "styles/dock.md"
|
||||
- "styles/index.md"
|
||||
- Grid:
|
||||
- "styles/grid/index.md"
|
||||
- "styles/grid/column_span.md"
|
||||
- "styles/grid/grid_columns.md"
|
||||
- "styles/grid/grid_gutter.md"
|
||||
- "styles/grid/grid_rows.md"
|
||||
- "styles/grid/grid_size.md"
|
||||
- "styles/grid/row_span.md"
|
||||
- "styles/height.md"
|
||||
- "styles/layer.md"
|
||||
- "styles/layers.md"
|
||||
- "styles/layout.md"
|
||||
- Links:
|
||||
- "styles/links/index.md"
|
||||
- "styles/links/link_background.md"
|
||||
- "styles/links/link_color.md"
|
||||
- "styles/links/link_hover_background.md"
|
||||
- "styles/links/link_hover_color.md"
|
||||
- "styles/links/link_hover_style.md"
|
||||
- "styles/links/link_style.md"
|
||||
- "styles/margin.md"
|
||||
- "styles/max_height.md"
|
||||
- "styles/max_width.md"
|
||||
- "styles/min_height.md"
|
||||
- "styles/min_width.md"
|
||||
- "styles/offset.md"
|
||||
- "styles/opacity.md"
|
||||
- "styles/outline.md"
|
||||
- "styles/overflow.md"
|
||||
- "styles/padding.md"
|
||||
- Scrollbar colors:
|
||||
- "styles/scrollbar_colors/index.md"
|
||||
- "styles/scrollbar_colors/scrollbar_background.md"
|
||||
- "styles/scrollbar_colors/scrollbar_background_active.md"
|
||||
- "styles/scrollbar_colors/scrollbar_background_hover.md"
|
||||
- "styles/scrollbar_colors/scrollbar_color.md"
|
||||
- "styles/scrollbar_colors/scrollbar_color_active.md"
|
||||
- "styles/scrollbar_colors/scrollbar_color_hover.md"
|
||||
- "styles/scrollbar_colors/scrollbar_corner_color.md"
|
||||
- "styles/scrollbar_gutter.md"
|
||||
- "styles/scrollbar_size.md"
|
||||
- "styles/text_align.md"
|
||||
- "styles/text_opacity.md"
|
||||
- "styles/text_style.md"
|
||||
- "styles/tint.md"
|
||||
- "styles/visibility.md"
|
||||
- "styles/width.md"
|
||||
- Widgets:
|
||||
- "widgets/button.md"
|
||||
- "widgets/checkbox.md"
|
||||
- "widgets/content_switcher.md"
|
||||
- "widgets/data_table.md"
|
||||
- "widgets/directory_tree.md"
|
||||
- "widgets/footer.md"
|
||||
- "widgets/header.md"
|
||||
- "widgets/index.md"
|
||||
- "widgets/input.md"
|
||||
- "widgets/label.md"
|
||||
- "widgets/list_item.md"
|
||||
- "widgets/list_view.md"
|
||||
- "widgets/loading_indicator.md"
|
||||
- "widgets/log.md"
|
||||
- "widgets/markdown_viewer.md"
|
||||
- "widgets/markdown.md"
|
||||
- "widgets/option_list.md"
|
||||
- "widgets/placeholder.md"
|
||||
- "widgets/pretty.md"
|
||||
- "widgets/progress_bar.md"
|
||||
- "widgets/radiobutton.md"
|
||||
- "widgets/radioset.md"
|
||||
- "widgets/rich_log.md"
|
||||
- "widgets/select.md"
|
||||
- "widgets/selection_list.md"
|
||||
- "widgets/sparkline.md"
|
||||
- "widgets/static.md"
|
||||
- "widgets/switch.md"
|
||||
- "widgets/tabbed_content.md"
|
||||
- "widgets/tabs.md"
|
||||
- "widgets/tree.md"
|
||||
- API:
|
||||
- "api/index.md"
|
||||
- "api/app.md"
|
||||
- "api/await_remove.md"
|
||||
- "api/binding.md"
|
||||
- "api/color.md"
|
||||
- "api/containers.md"
|
||||
- "api/coordinate.md"
|
||||
- "api/dom_node.md"
|
||||
- "api/events.md"
|
||||
- "api/errors.md"
|
||||
- "api/filter.md"
|
||||
- "api/geometry.md"
|
||||
- "api/logger.md"
|
||||
- "api/logging.md"
|
||||
- "api/map_geometry.md"
|
||||
- "api/message_pump.md"
|
||||
- "api/message.md"
|
||||
- "api/on.md"
|
||||
- "api/pilot.md"
|
||||
- "api/query.md"
|
||||
- "api/reactive.md"
|
||||
- "api/screen.md"
|
||||
- "api/scrollbar.md"
|
||||
- "api/scroll_view.md"
|
||||
- "api/strip.md"
|
||||
- "api/suggester.md"
|
||||
- "api/timer.md"
|
||||
- "api/types.md"
|
||||
- "api/validation.md"
|
||||
- "api/walk.md"
|
||||
- "api/widget.md"
|
||||
- "api/work.md"
|
||||
- "api/worker.md"
|
||||
- "api/worker_manager.md"
|
||||
- "How To":
|
||||
- "how-to/index.md"
|
||||
- "how-to/center-things.md"
|
||||
- "how-to/design-a-layout.md"
|
||||
- "roadmap.md"
|
||||
- "Blog":
|
||||
- blog/index.md
|
||||
|
||||
356
poetry.lock
generated
356
poetry.lock
generated
@@ -2,99 +2,99 @@
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.8.4"
|
||||
version = "3.8.5"
|
||||
description = "Async http client/server framework (asyncio)"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5ce45967538fb747370308d3145aa68a074bdecb4f3a300869590f725ced69c1"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b744c33b6f14ca26b7544e8d8aadff6b765a80ad6164fb1a430bbadd593dfb1a"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a45865451439eb320784918617ba54b7a377e3501fb70402ab84d38c2cd891b"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a86d42d7cba1cec432d47ab13b6637bee393a10f664c425ea7b305d1301ca1a3"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee3c36df21b5714d49fc4580247947aa64bcbe2939d1b77b4c8dcb8f6c9faecc"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:176a64b24c0935869d5bbc4c96e82f89f643bcdf08ec947701b9dbb3c956b7dd"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c844fd628851c0bc309f3c801b3a3d58ce430b2ce5b359cd918a5a76d0b20cb5"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5393fb786a9e23e4799fec788e7e735de18052f83682ce2dfcabaf1c00c2c08e"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e4b09863aae0dc965c3ef36500d891a3ff495a2ea9ae9171e4519963c12ceefd"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:adfbc22e87365a6e564c804c58fc44ff7727deea782d175c33602737b7feadb6"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:147ae376f14b55f4f3c2b118b95be50a369b89b38a971e80a17c3fd623f280c9"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:eafb3e874816ebe2a92f5e155f17260034c8c341dad1df25672fb710627c6949"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6cc15d58053c76eacac5fa9152d7d84b8d67b3fde92709195cb984cfb3475ea"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-win32.whl", hash = "sha256:59f029a5f6e2d679296db7bee982bb3d20c088e52a2977e3175faf31d6fb75d1"},
|
||||
{file = "aiohttp-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:fe7ba4a51f33ab275515f66b0a236bcde4fb5561498fe8f898d4e549b2e4509f"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d8ef1a630519a26d6760bc695842579cb09e373c5f227a21b67dc3eb16cfea4"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b3f2e06a512e94722886c0827bee9807c86a9f698fac6b3aee841fab49bbfb4"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a80464982d41b1fbfe3154e440ba4904b71c1a53e9cd584098cd41efdb188ef"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b631e26df63e52f7cce0cce6507b7a7f1bc9b0c501fcde69742130b32e8782f"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f43255086fe25e36fd5ed8f2ee47477408a73ef00e804cb2b5cba4bf2ac7f5e"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d347a172f866cd1d93126d9b239fcbe682acb39b48ee0873c73c933dd23bd0f"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3fec6a4cb5551721cdd70473eb009d90935b4063acc5f40905d40ecfea23e05"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80a37fe8f7c1e6ce8f2d9c411676e4bc633a8462844e38f46156d07a7d401654"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d1e6a862b76f34395a985b3cd39a0d949ca80a70b6ebdea37d3ab39ceea6698a"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cd468460eefef601ece4428d3cf4562459157c0f6523db89365202c31b6daebb"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:618c901dd3aad4ace71dfa0f5e82e88b46ef57e3239fc7027773cb6d4ed53531"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:652b1bff4f15f6287550b4670546a2947f2a4575b6c6dff7760eafb22eacbf0b"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80575ba9377c5171407a06d0196b2310b679dc752d02a1fcaa2bc20b235dbf24"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-win32.whl", hash = "sha256:bbcf1a76cf6f6dacf2c7f4d2ebd411438c275faa1dc0c68e46eb84eebd05dd7d"},
|
||||
{file = "aiohttp-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:6e74dd54f7239fcffe07913ff8b964e28b712f09846e20de78676ce2a3dc0bfc"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:880e15bb6dad90549b43f796b391cfffd7af373f4646784795e20d92606b7a51"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb96fa6b56bb536c42d6a4a87dfca570ff8e52de2d63cabebfd6fb67049c34b6"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a6cadebe132e90cefa77e45f2d2f1a4b2ce5c6b1bfc1656c1ddafcfe4ba8131"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f352b62b45dff37b55ddd7b9c0c8672c4dd2eb9c0f9c11d395075a84e2c40f75"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ab43061a0c81198d88f39aaf90dae9a7744620978f7ef3e3708339b8ed2ef01"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9cb1565a7ad52e096a6988e2ee0397f72fe056dadf75d17fa6b5aebaea05622"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:1b3ea7edd2d24538959c1c1abf97c744d879d4e541d38305f9bd7d9b10c9ec41"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7c7837fe8037e96b6dd5cfcf47263c1620a9d332a87ec06a6ca4564e56bd0f36"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3b90467ebc3d9fa5b0f9b6489dfb2c304a1db7b9946fa92aa76a831b9d587e99"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:cab9401de3ea52b4b4c6971db5fb5c999bd4260898af972bf23de1c6b5dd9d71"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d1f9282c5f2b5e241034a009779e7b2a1aa045f667ff521e7948ea9b56e0c5ff"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-win32.whl", hash = "sha256:5e14f25765a578a0a634d5f0cd1e2c3f53964553a00347998dfdf96b8137f777"},
|
||||
{file = "aiohttp-3.8.4-cp36-cp36m-win_amd64.whl", hash = "sha256:4c745b109057e7e5f1848c689ee4fb3a016c8d4d92da52b312f8a509f83aa05e"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:aede4df4eeb926c8fa70de46c340a1bc2c6079e1c40ccf7b0eae1313ffd33519"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ddaae3f3d32fc2cb4c53fab020b69a05c8ab1f02e0e59665c6f7a0d3a5be54f"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4eb3b82ca349cf6fadcdc7abcc8b3a50ab74a62e9113ab7a8ebc268aad35bb9"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bcb89336efa095ea21b30f9e686763f2be4478f1b0a616969551982c4ee4c3b"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c08e8ed6fa3d477e501ec9db169bfac8140e830aa372d77e4a43084d8dd91ab"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6cd05ea06daca6ad6a4ca3ba7fe7dc5b5de063ff4daec6170ec0f9979f6c332"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7a00a9ed8d6e725b55ef98b1b35c88013245f35f68b1b12c5cd4100dddac333"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:de04b491d0e5007ee1b63a309956eaed959a49f5bb4e84b26c8f5d49de140fa9"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:40653609b3bf50611356e6b6554e3a331f6879fa7116f3959b20e3528783e699"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dbf3a08a06b3f433013c143ebd72c15cac33d2914b8ea4bea7ac2c23578815d6"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854f422ac44af92bfe172d8e73229c270dc09b96535e8a548f99c84f82dde241"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-win32.whl", hash = "sha256:aeb29c84bb53a84b1a81c6c09d24cf33bb8432cc5c39979021cc0f98c1292a1a"},
|
||||
{file = "aiohttp-3.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:db3fc6120bce9f446d13b1b834ea5b15341ca9ff3f335e4a951a6ead31105480"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fabb87dd8850ef0f7fe2b366d44b77d7e6fa2ea87861ab3844da99291e81e60f"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91f6d540163f90bbaef9387e65f18f73ffd7c79f5225ac3d3f61df7b0d01ad15"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d265f09a75a79a788237d7f9054f929ced2e69eb0bb79de3798c468d8a90f945"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d89efa095ca7d442a6d0cbc755f9e08190ba40069b235c9886a8763b03785da"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dac314662f4e2aa5009977b652d9b8db7121b46c38f2073bfeed9f4049732cd"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe11310ae1e4cd560035598c3f29d86cef39a83d244c7466f95c27ae04850f10"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ddb2a2026c3f6a68c3998a6c47ab6795e4127315d2e35a09997da21865757f8"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e75b89ac3bd27d2d043b234aa7b734c38ba1b0e43f07787130a0ecac1e12228a"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6e601588f2b502c93c30cd5a45bfc665faaf37bbe835b7cfd461753068232074"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a5d794d1ae64e7753e405ba58e08fcfa73e3fad93ef9b7e31112ef3c9a0efb52"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a1f4689c9a1462f3df0a1f7e797791cd6b124ddbee2b570d34e7f38ade0e2c71"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3032dcb1c35bc330134a5b8a5d4f68c1a87252dfc6e1262c65a7e30e62298275"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8189c56eb0ddbb95bfadb8f60ea1b22fcfa659396ea36f6adcc521213cd7b44d"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-win32.whl", hash = "sha256:33587f26dcee66efb2fff3c177547bd0449ab7edf1b73a7f5dea1e38609a0c54"},
|
||||
{file = "aiohttp-3.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:e595432ac259af2d4630008bf638873d69346372d38255774c0e286951e8b79f"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a7bdf9e57126dc345b683c3632e8ba317c31d2a41acd5800c10640387d193ed"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:22f6eab15b6db242499a16de87939a342f5a950ad0abaf1532038e2ce7d31567"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7235604476a76ef249bd64cb8274ed24ccf6995c4a8b51a237005ee7a57e8643"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea9eb976ffdd79d0e893869cfe179a8f60f152d42cb64622fca418cd9b18dc2a"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92c0cea74a2a81c4c76b62ea1cac163ecb20fb3ba3a75c909b9fa71b4ad493cf"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:493f5bc2f8307286b7799c6d899d388bbaa7dfa6c4caf4f97ef7521b9cb13719"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a63f03189a6fa7c900226e3ef5ba4d3bd047e18f445e69adbd65af433add5a2"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10c8cefcff98fd9168cdd86c4da8b84baaa90bf2da2269c6161984e6737bf23e"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bca5f24726e2919de94f047739d0a4fc01372801a3672708260546aa2601bf57"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:03baa76b730e4e15a45f81dfe29a8d910314143414e528737f8589ec60cf7391"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8c29c77cc57e40f84acef9bfb904373a4e89a4e8b74e71aa8075c021ec9078c2"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:03543dcf98a6619254b409be2d22b51f21ec66272be4ebda7b04e6412e4b2e14"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17b79c2963db82086229012cff93ea55196ed31f6493bb1ccd2c62f1724324e4"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-win32.whl", hash = "sha256:34ce9f93a4a68d1272d26030655dd1b58ff727b3ed2a33d80ec433561b03d67a"},
|
||||
{file = "aiohttp-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:41a86a69bb63bb2fc3dc9ad5ea9f10f1c9c8e282b471931be0268ddd09430b04"},
|
||||
{file = "aiohttp-3.8.4.tar.gz", hash = "sha256:bf2e1a9162c1e441bf805a1fd166e249d574ca04e03b34f97e2928769e91ab5c"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a94159871304770da4dd371f4291b20cac04e8c94f11bdea1c3478e557fbe0d8"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13bf85afc99ce6f9ee3567b04501f18f9f8dbbb2ea11ed1a2e079670403a7c84"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ce2ac5708501afc4847221a521f7e4b245abf5178cf5ddae9d5b3856ddb2f3a"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96943e5dcc37a6529d18766597c491798b7eb7a61d48878611298afc1fca946c"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ad5c3c4590bb3cc28b4382f031f3783f25ec223557124c68754a2231d989e2b"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c413c633d0512df4dc7fd2373ec06cc6a815b7b6d6c2f208ada7e9e93a5061d"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df72ac063b97837a80d80dec8d54c241af059cc9bb42c4de68bd5b61ceb37caa"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c48c5c0271149cfe467c0ff8eb941279fd6e3f65c9a388c984e0e6cf57538e14"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:368a42363c4d70ab52c2c6420a57f190ed3dfaca6a1b19afda8165ee16416a82"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7607ec3ce4993464368505888af5beb446845a014bc676d349efec0e05085905"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0d21c684808288a98914e5aaf2a7c6a3179d4df11d249799c32d1808e79503b5"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:312fcfbacc7880a8da0ae8b6abc6cc7d752e9caa0051a53d217a650b25e9a691"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad093e823df03bb3fd37e7dec9d4670c34f9e24aeace76808fc20a507cace825"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-win32.whl", hash = "sha256:33279701c04351a2914e1100b62b2a7fdb9a25995c4a104259f9a5ead7ed4802"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:6e4a280e4b975a2e7745573e3fc9c9ba0d1194a3738ce1cbaa80626cc9b4f4df"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae871a964e1987a943d83d6709d20ec6103ca1eaf52f7e0d36ee1b5bebb8b9b9"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:461908b2578955045efde733719d62f2b649c404189a09a632d245b445c9c975"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72a860c215e26192379f57cae5ab12b168b75db8271f111019509a1196dfc780"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc14be025665dba6202b6a71cfcdb53210cc498e50068bc088076624471f8bb9"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af740fc2711ad85f1a5c034a435782fbd5b5f8314c9a3ef071424a8158d7f6b"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:841cd8233cbd2111a0ef0a522ce016357c5e3aff8a8ce92bcfa14cef890d698f"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed1c46fb119f1b59304b5ec89f834f07124cd23ae5b74288e364477641060ff"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84f8ae3e09a34f35c18fa57f015cc394bd1389bce02503fb30c394d04ee6b938"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62360cb771707cb70a6fd114b9871d20d7dd2163a0feafe43fd115cfe4fe845e"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23fb25a9f0a1ca1f24c0a371523546366bb642397c94ab45ad3aedf2941cec6a"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0ba0d15164eae3d878260d4c4df859bbdc6466e9e6689c344a13334f988bb53"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5d20003b635fc6ae3f96d7260281dfaf1894fc3aa24d1888a9b2628e97c241e5"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0175d745d9e85c40dcc51c8f88c74bfbaef9e7afeeeb9d03c37977270303064c"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-win32.whl", hash = "sha256:2e1b1e51b0774408f091d268648e3d57f7260c1682e7d3a63cb00d22d71bb945"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:043d2299f6dfdc92f0ac5e995dfc56668e1587cea7f9aa9d8a78a1b6554e5755"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cae533195e8122584ec87531d6df000ad07737eaa3c81209e85c928854d2195c"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f21e83f355643c345177a5d1d8079f9f28b5133bcd154193b799d380331d5d3"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a75ef35f2df54ad55dbf4b73fe1da96f370e51b10c91f08b19603c64004acc"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e2e9839e14dd5308ee773c97115f1e0a1cb1d75cbeeee9f33824fa5144c7634"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44e65da1de4403d0576473e2344828ef9c4c6244d65cf4b75549bb46d40b8dd"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d847e4cde6ecc19125ccbc9bfac4a7ab37c234dd88fbb3c5c524e8e14da543"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c7a815258e5895d8900aec4454f38dca9aed71085f227537208057853f9d13f2"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:8b929b9bd7cd7c3939f8bcfffa92fae7480bd1aa425279d51a89327d600c704d"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5db3a5b833764280ed7618393832e0853e40f3d3e9aa128ac0ba0f8278d08649"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:a0215ce6041d501f3155dc219712bc41252d0ab76474615b9700d63d4d9292af"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fd1ed388ea7fbed22c4968dd64bab0198de60750a25fe8c0c9d4bef5abe13824"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-win32.whl", hash = "sha256:6e6783bcc45f397fdebc118d772103d751b54cddf5b60fbcc958382d7dd64f3e"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:b5411d82cddd212644cf9360879eb5080f0d5f7d809d03262c50dad02f01421a"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:01d4c0c874aa4ddfb8098e85d10b5e875a70adc63db91f1ae65a4b04d3344cda"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5980a746d547a6ba173fd5ee85ce9077e72d118758db05d229044b469d9029a"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a482e6da906d5e6e653be079b29bc173a48e381600161c9932d89dfae5942ef"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80bd372b8d0715c66c974cf57fe363621a02f359f1ec81cba97366948c7fc873"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1161b345c0a444ebcf46bf0a740ba5dcf50612fd3d0528883fdc0eff578006a"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd56db019015b6acfaaf92e1ac40eb8434847d9bf88b4be4efe5bfd260aee692"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:153c2549f6c004d2754cc60603d4668899c9895b8a89397444a9c4efa282aaf4"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4a01951fabc4ce26ab791da5f3f24dca6d9a6f24121746eb19756416ff2d881b"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bfb9162dcf01f615462b995a516ba03e769de0789de1cadc0f916265c257e5d8"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7dde0009408969a43b04c16cbbe252c4f5ef4574ac226bc8815cd7342d2028b6"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4149d34c32f9638f38f544b3977a4c24052042affa895352d3636fa8bffd030a"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-win32.whl", hash = "sha256:68c5a82c8779bdfc6367c967a4a1b2aa52cd3595388bf5961a62158ee8a59e22"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2cf57fb50be5f52bda004b8893e63b48530ed9f0d6c96c84620dc92fe3cd9b9d"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:eca4bf3734c541dc4f374ad6010a68ff6c6748f00451707f39857f429ca36ced"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1274477e4c71ce8cfe6c1ec2f806d57c015ebf84d83373676036e256bc55d690"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c543e54710d6158fc6f439296c7865b29e0b616629767e685a7185fab4a6b9"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910bec0c49637d213f5d9877105d26e0c4a4de2f8b1b29405ff37e9fc0ad52b8"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5443910d662db951b2e58eb70b0fbe6b6e2ae613477129a5805d0b66c54b6cb7"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e460be6978fc24e3df83193dc0cc4de46c9909ed92dd47d349a452ef49325b7"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb1558def481d84f03b45888473fc5a1f35747b5f334ef4e7a571bc0dfcb11f8"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34dd0c107799dcbbf7d48b53be761a013c0adf5571bf50c4ecad5643fe9cfcd0"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa1990247f02a54185dc0dff92a6904521172a22664c863a03ff64c42f9b5410"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0e584a10f204a617d71d359fe383406305a4b595b333721fa50b867b4a0a1548"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a3cf433f127efa43fee6b90ea4c6edf6c4a17109d1d037d1a52abec84d8f2e42"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c11f5b099adafb18e65c2c997d57108b5bbeaa9eeee64a84302c0978b1ec948b"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:84de26ddf621d7ac4c975dbea4c945860e08cccde492269db4e1538a6a6f3c35"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-win32.whl", hash = "sha256:ab88bafedc57dd0aab55fa728ea10c1911f7e4d8b43e1d838a1739f33712921c"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:5798a9aad1879f626589f3df0f8b79b3608a92e9beab10e5fda02c8a2c60db2e"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a6ce61195c6a19c785df04e71a4537e29eaa2c50fe745b732aa937c0c77169f3"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:773dd01706d4db536335fcfae6ea2440a70ceb03dd3e7378f3e815b03c97ab51"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f83a552443a526ea38d064588613aca983d0ee0038801bc93c0c916428310c28"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f7372f7341fcc16f57b2caded43e81ddd18df53320b6f9f042acad41f8e049a"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea353162f249c8097ea63c2169dd1aa55de1e8fecbe63412a9bc50816e87b761"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d47ae48db0b2dcf70bc8a3bc72b3de86e2a590fc299fdbbb15af320d2659de"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d827176898a2b0b09694fbd1088c7a31836d1a505c243811c87ae53a3f6273c1"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3562b06567c06439d8b447037bb655ef69786c590b1de86c7ab81efe1c9c15d8"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4e874cbf8caf8959d2adf572a78bba17cb0e9d7e51bb83d86a3697b686a0ab4d"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6809a00deaf3810e38c628e9a33271892f815b853605a936e2e9e5129762356c"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:33776e945d89b29251b33a7e7d006ce86447b2cfd66db5e5ded4e5cd0340585c"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eaeed7abfb5d64c539e2db173f63631455f1196c37d9d8d873fc316470dfbacd"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e91d635961bec2d8f19dfeb41a539eb94bd073f075ca6dae6c8dc0ee89ad6f91"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-win32.whl", hash = "sha256:00ad4b6f185ec67f3e6562e8a1d2b69660be43070bd0ef6fcec5211154c7df67"},
|
||||
{file = "aiohttp-3.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:c0a9034379a37ae42dea7ac1e048352d96286626251862e448933c0f59cbd79c"},
|
||||
{file = "aiohttp-3.8.5.tar.gz", hash = "sha256:b9552ec52cc147dbf1944ac7ac98af7602e51ea2dcd076ed194ca3c0d1c7d0bc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -263,14 +263,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2023.5.7"
|
||||
version = "2023.7.22"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"},
|
||||
{file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"},
|
||||
{file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
|
||||
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -372,14 +372,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.5"
|
||||
version = "8.1.6"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.5-py3-none-any.whl", hash = "sha256:e576aa487d679441d7d30abb87e1b43d24fc53bffb8758443b1a9e1cee504548"},
|
||||
{file = "click-8.1.5.tar.gz", hash = "sha256:4be4b1af8d665c6d942909916d31a213a106800c47d0eeba73d34da3cbc11367"},
|
||||
{file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"},
|
||||
{file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -836,20 +836,21 @@ test = ["coverage", "pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "3.3.7"
|
||||
description = "Python implementation of Markdown."
|
||||
version = "3.4.4"
|
||||
description = "Python implementation of John Gruber's Markdown."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"},
|
||||
{file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"},
|
||||
{file = "Markdown-3.4.4-py3-none-any.whl", hash = "sha256:a4c1b65c0957b4bd9e7d86ddc7b3c9868fb9670660f6f99f6d1bca8954d5a941"},
|
||||
{file = "Markdown-3.4.4.tar.gz", hash = "sha256:225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.0)", "mkdocs-nature (>=0.4)"]
|
||||
testing = ["coverage", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
@@ -986,14 +987,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs"
|
||||
version = "1.4.3"
|
||||
version = "1.5.2"
|
||||
description = "Project documentation with Markdown."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "mkdocs-1.4.3-py3-none-any.whl", hash = "sha256:6ee46d309bda331aac915cd24aab882c179a933bd9e77b80ce7d2eaaa3f689dd"},
|
||||
{file = "mkdocs-1.4.3.tar.gz", hash = "sha256:5955093bbd4dd2e9403c5afaf57324ad8b04f16886512a3ee6ef828956481c57"},
|
||||
{file = "mkdocs-1.5.2-py3-none-any.whl", hash = "sha256:60a62538519c2e96fe8426654a67ee177350451616118a41596ae7c876bb7eac"},
|
||||
{file = "mkdocs-1.5.2.tar.gz", hash = "sha256:70d0da09c26cff288852471be03c23f0f521fc15cf16ac89c7a3bfb9ae8d24f9"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1002,9 +1003,12 @@ colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""}
|
||||
ghp-import = ">=1.0"
|
||||
importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
|
||||
jinja2 = ">=2.11.1"
|
||||
markdown = ">=3.2.1,<3.4"
|
||||
markdown = ">=3.2.1"
|
||||
markupsafe = ">=2.0.1"
|
||||
mergedeep = ">=1.3.4"
|
||||
packaging = ">=20.5"
|
||||
pathspec = ">=0.11.1"
|
||||
platformdirs = ">=2.2.0"
|
||||
pyyaml = ">=5.1"
|
||||
pyyaml-env-tag = ">=0.1"
|
||||
typing-extensions = {version = ">=3.10", markers = "python_version < \"3.8\""}
|
||||
@@ -1012,7 +1016,7 @@ watchdog = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
i18n = ["babel (>=2.9.0)"]
|
||||
min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"]
|
||||
min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-autorefs"
|
||||
@@ -1046,21 +1050,21 @@ mkdocs = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-material"
|
||||
version = "9.1.18"
|
||||
version = "9.1.21"
|
||||
description = "Documentation that simply works"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "mkdocs_material-9.1.18-py3-none-any.whl", hash = "sha256:5bcf8fb79ac2f253c0ffe93fa181cba87718c6438f459dc4180ac7418cc9a450"},
|
||||
{file = "mkdocs_material-9.1.18.tar.gz", hash = "sha256:981dd39979723d4cda7cfc77bbbe5e54922d5761a7af23fb8ba9edb52f114b13"},
|
||||
{file = "mkdocs_material-9.1.21-py3-none-any.whl", hash = "sha256:58bb2f11ef240632e176d6f0f7d1cff06be1d11c696a5a1b553b808b4280ed47"},
|
||||
{file = "mkdocs_material-9.1.21.tar.gz", hash = "sha256:71940cdfca84ab296b6362889c25395b1621273fb16c93deda257adb7ff44ec8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = ">=0.4"
|
||||
jinja2 = ">=3.0"
|
||||
markdown = ">=3.2"
|
||||
mkdocs = ">=1.4.2"
|
||||
mkdocs = ">=1.5.0"
|
||||
mkdocs-material-extensions = ">=1.1"
|
||||
pygments = ">=2.14"
|
||||
pymdown-extensions = ">=9.9.1"
|
||||
@@ -1389,34 +1393,34 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.11.1"
|
||||
version = "0.11.2"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"},
|
||||
{file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"},
|
||||
{file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
|
||||
{file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "3.9.1"
|
||||
version = "3.10.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "platformdirs-3.9.1-py3-none-any.whl", hash = "sha256:ad8291ae0ae5072f66c16945166cb11c63394c7a3ad1b1bc9828ca3162da8c2f"},
|
||||
{file = "platformdirs-3.9.1.tar.gz", hash = "sha256:1b42b450ad933e981d56e59f1b97495428c9bd60698baab9f3eb3d00d5822421"},
|
||||
{file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"},
|
||||
{file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=4.6.3", markers = "python_version < \"3.8\""}
|
||||
typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
@@ -1620,52 +1624,52 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0"
|
||||
version = "6.0.1"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
|
||||
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
|
||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1823,14 +1827,14 @@ idna2008 = ["idna"]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.4.2"
|
||||
version = "13.5.2"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "rich-13.4.2-py3-none-any.whl", hash = "sha256:8f87bc7ee54675732fa66a05ebfe489e27264caeeff3728c945d25971b6485ec"},
|
||||
{file = "rich-13.4.2.tar.gz", hash = "sha256:d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898"},
|
||||
{file = "rich-13.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"},
|
||||
{file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1912,21 +1916,21 @@ pytest = ">=5.1.0,<8.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "textual-dev"
|
||||
version = "1.0.1"
|
||||
version = "1.1.0"
|
||||
description = "Development tools for working with Textual"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
files = [
|
||||
{file = "textual_dev-1.0.1-py3-none-any.whl", hash = "sha256:419fc426c120f04f89ab0cb1aa88f7873dd7cdb9c21618e709175c8eaff6b566"},
|
||||
{file = "textual_dev-1.0.1.tar.gz", hash = "sha256:9f4c40655cbb56af7ee92805ef14fa24ae98ff8b0ae778c59de7222f1caa7281"},
|
||||
{file = "textual_dev-1.1.0-py3-none-any.whl", hash = "sha256:c57320636098e31fa5d5c29fc3bc60829bb420da3c76bfed24db6eacf178dbc6"},
|
||||
{file = "textual_dev-1.1.0.tar.gz", hash = "sha256:e2f8ce4e1c18a16b80282f3257cd2feb49a7ede289a78908c9063ce071bb77ce"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = ">=3.8.1"
|
||||
click = ">=8.1.2"
|
||||
msgpack = ">=1.0.3"
|
||||
textual = ">=0.29.0"
|
||||
textual = ">=0.32.0"
|
||||
typing-extensions = ">=4.4.0,<5.0.0"
|
||||
|
||||
[[package]]
|
||||
@@ -2124,14 +2128,14 @@ test = ["coverage", "pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.0.3"
|
||||
version = "2.0.4"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"},
|
||||
{file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"},
|
||||
{file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"},
|
||||
{file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -2142,25 +2146,25 @@ zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.24.0"
|
||||
version = "20.24.2"
|
||||
description = "Virtual Python Environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "virtualenv-20.24.0-py3-none-any.whl", hash = "sha256:18d1b37fc75cc2670625702d76849a91ebd383768b4e91382a8d51be3246049e"},
|
||||
{file = "virtualenv-20.24.0.tar.gz", hash = "sha256:e2a7cef9da880d693b933db7654367754f14e20650dc60e8ee7385571f8593a3"},
|
||||
{file = "virtualenv-20.24.2-py3-none-any.whl", hash = "sha256:43a3052be36080548bdee0b42919c88072037d50d56c28bd3f853cbe92b953ff"},
|
||||
{file = "virtualenv-20.24.2.tar.gz", hash = "sha256:fd8a78f46f6b99a67b7ec5cf73f92357891a7b3a40fd97637c27f854aae3b9e0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
distlib = ">=0.3.6,<1"
|
||||
filelock = ">=3.12,<4"
|
||||
distlib = ">=0.3.7,<1"
|
||||
filelock = ">=3.12.2,<4"
|
||||
importlib-metadata = {version = ">=6.6", markers = "python_version < \"3.8\""}
|
||||
platformdirs = ">=3.5.1,<4"
|
||||
platformdirs = ">=3.9.1,<4"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"]
|
||||
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
||||
|
||||
[[package]]
|
||||
name = "watchdog"
|
||||
@@ -2310,4 +2314,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "652f05ae0b8df5f39af720c34bfb222e514e1b17afbf0eb10738946651c293e1"
|
||||
content-hash = "5ac8aef69083d16bc38af16f22cc94ad14b8b70b5cff61e0c7d462c1d1a8a42c"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "textual"
|
||||
version = "0.31.0"
|
||||
version = "0.32.0"
|
||||
homepage = "https://github.com/Textualize/textual"
|
||||
description = "Modern Text User Interface framework"
|
||||
authors = ["Will McGugan <will@textualize.io>"]
|
||||
@@ -57,9 +57,9 @@ time-machine = "^2.6.0"
|
||||
mkdocs-rss-plugin = "^1.5.0"
|
||||
httpx = "^0.23.1"
|
||||
types-setuptools = "^67.2.0.1"
|
||||
textual-dev = ">=1.0.0"
|
||||
textual-dev = "^1.1.0"
|
||||
pytest-asyncio = "*"
|
||||
pytest-textual-snapshot = ">=0.1.0"
|
||||
pytest-textual-snapshot = "0.2.0"
|
||||
|
||||
[tool.black]
|
||||
includes = "src"
|
||||
|
||||
16
questions/no-widget-called-textlog.question.md
Normal file
16
questions/no-widget-called-textlog.question.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "No widget called TextLog"
|
||||
alt_titles:
|
||||
- "ImportError with TextLog"
|
||||
- "TextLog does not exist"
|
||||
---
|
||||
|
||||
The `TextLog` widget was renamed to `RichLog` in Textual 0.32.0.
|
||||
You will need to replace all references to `TextLog` in your code, with `RichLog`.
|
||||
Most IDEs will have a search and replace function which will help you do this.
|
||||
|
||||
Here's how you should import RichLog:
|
||||
|
||||
```python
|
||||
from textual.widgets import RichLog
|
||||
```
|
||||
@@ -30,7 +30,7 @@ Then the app can be run, passing in various arguments; for example:
|
||||
# Running with default arguments.
|
||||
Greetings().run()
|
||||
|
||||
# Running with a keyword arguyment.
|
||||
# Running with a keyword argument.
|
||||
Greetings(to_greet="davep").run()
|
||||
|
||||
# Running with both positional arguments.
|
||||
|
||||
26
questions/worker-thread-error.question.md
Normal file
26
questions/worker-thread-error.question.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "How do I fix WorkerDeclarationError?"
|
||||
alt_titles:
|
||||
- "Thread=True on Worker"
|
||||
- "Problem with threaded workers"
|
||||
---
|
||||
|
||||
Textual version 0.31.0 requires that you set `thread=True` on the `@work` decorator if you want to run a threaded worker.
|
||||
|
||||
If you want a threaded worker, you would declare it in the following way:
|
||||
|
||||
```python
|
||||
@work(thread=True)
|
||||
def run_in_background():
|
||||
...
|
||||
```
|
||||
|
||||
If you *don't* want a threaded worker, you should make your work function `async`:
|
||||
|
||||
```python
|
||||
@work()
|
||||
async def run_in_background():
|
||||
...
|
||||
```
|
||||
|
||||
This change was made because it was too easy to accidentally create a threaded worker, which may produce unexpected results.
|
||||
@@ -177,6 +177,21 @@ class LRUCache(Generic[CacheKey, CacheValue]):
|
||||
def __contains__(self, key: CacheKey) -> bool:
|
||||
return key in self._cache
|
||||
|
||||
def discard(self, key: CacheKey) -> None:
|
||||
"""Discard item in cache from key.
|
||||
|
||||
Args:
|
||||
key: Cache key.
|
||||
"""
|
||||
link = self._cache.get(key)
|
||||
if link is None:
|
||||
return
|
||||
# Remove link from list
|
||||
link[0][1] = link[1] # type: ignore[index]
|
||||
link[1][0] = link[0] # type: ignore[index]
|
||||
# Remove link from cache
|
||||
del self._cache[key]
|
||||
|
||||
|
||||
class FIFOCache(Generic[CacheKey, CacheValue]):
|
||||
"""A simple cache that discards the first added key when full (First In First Out).
|
||||
|
||||
23
src/textual/_line_split.py
Normal file
23
src/textual/_line_split.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
# Pre-compile the regular expression and store it in a global constant
|
||||
LINE_AND_ENDING_PATTERN = re.compile(r"(.*?)(\r\n|\r|\n|$)", re.S)
|
||||
|
||||
|
||||
def line_split(input_string: str) -> list[tuple[str, str]]:
|
||||
r"""
|
||||
Splits an arbitrary string into a list of tuples, where each tuple contains a line of text and its line ending.
|
||||
|
||||
Args:
|
||||
input_string (str): The string to split.
|
||||
|
||||
Returns:
|
||||
list[tuple[str, str]]: A list of tuples, where each tuple contains a line of text and its line ending.
|
||||
|
||||
Example:
|
||||
split_string_to_lines_and_endings("Hello\r\nWorld\nThis is a test\rLast line")
|
||||
>>> [('Hello', '\r\n'), ('World', '\n'), ('This is a test', '\r'), ('Last line', '')]
|
||||
"""
|
||||
return LINE_AND_ENDING_PATTERN.findall(input_string)[:-1] if input_string else []
|
||||
@@ -131,7 +131,7 @@ Screen>Container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
TextLog {
|
||||
RichLog {
|
||||
background: $surface;
|
||||
color: $text;
|
||||
height: 50vh;
|
||||
@@ -144,11 +144,11 @@ TextLog {
|
||||
}
|
||||
|
||||
|
||||
TextLog:focus {
|
||||
RichLog:focus {
|
||||
offset: 0 0 !important;
|
||||
}
|
||||
|
||||
TextLog.-hidden {
|
||||
RichLog.-hidden {
|
||||
offset-y: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ from textual.widgets import (
|
||||
Footer,
|
||||
Header,
|
||||
Input,
|
||||
RichLog,
|
||||
Static,
|
||||
Switch,
|
||||
TextLog,
|
||||
)
|
||||
|
||||
from_markup = Text.from_markup
|
||||
@@ -284,21 +284,21 @@ class DemoApp(App[None]):
|
||||
("ctrl+b", "toggle_sidebar", "Sidebar"),
|
||||
("ctrl+t", "app.toggle_dark", "Toggle Dark mode"),
|
||||
("ctrl+s", "app.screenshot()", "Screenshot"),
|
||||
("f1", "app.toggle_class('TextLog', '-hidden')", "Notes"),
|
||||
("f1", "app.toggle_class('RichLog', '-hidden')", "Notes"),
|
||||
Binding("ctrl+c,ctrl+q", "app.quit", "Quit", show=True),
|
||||
]
|
||||
|
||||
show_sidebar = reactive(False)
|
||||
|
||||
def add_note(self, renderable: RenderableType) -> None:
|
||||
self.query_one(TextLog).write(renderable)
|
||||
self.query_one(RichLog).write(renderable)
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
example_css = Path(self.css_path[0]).read_text()
|
||||
yield Container(
|
||||
Sidebar(classes="-hidden"),
|
||||
Header(show_clock=False),
|
||||
TextLog(classes="-hidden", wrap=False, highlight=True, markup=True),
|
||||
RichLog(classes="-hidden", wrap=False, highlight=True, markup=True),
|
||||
Body(
|
||||
QuickAccess(
|
||||
LocationLink("TOP", ".location-top"),
|
||||
|
||||
@@ -7,8 +7,9 @@ from __future__ import annotations
|
||||
from rich.console import RenderableType
|
||||
|
||||
from ._animator import EasingFunction
|
||||
from ._types import CallbackType
|
||||
from .containers import ScrollableContainer
|
||||
from .geometry import Size
|
||||
from .geometry import Region, Size
|
||||
|
||||
|
||||
class ScrollView(ScrollableContainer):
|
||||
@@ -142,3 +143,16 @@ class ScrollView(ScrollableContainer):
|
||||
force=force,
|
||||
on_complete=on_complete,
|
||||
)
|
||||
|
||||
def refresh_lines(self, y_start: int, line_count: int = 1) -> None:
|
||||
"""Refresh one or more lines.
|
||||
|
||||
Args:
|
||||
y_start: First line to refresh.
|
||||
line_count: Total number of lines to refresh.
|
||||
"""
|
||||
|
||||
width = self.size.width
|
||||
scroll_x, scroll_y = self.scroll_offset
|
||||
refresh_region = Region(scroll_x, y_start - scroll_y, width, line_count)
|
||||
self.refresh(refresh_region)
|
||||
|
||||
@@ -69,6 +69,8 @@ class Strip:
|
||||
"_style_cache",
|
||||
"_filter_cache",
|
||||
"_render_cache",
|
||||
"_line_length_cache",
|
||||
"_crop_extend_cache",
|
||||
"_link_ids",
|
||||
]
|
||||
|
||||
@@ -81,6 +83,14 @@ class Strip:
|
||||
self._crop_cache: FIFOCache[tuple[int, int], Strip] = FIFOCache(16)
|
||||
self._style_cache: FIFOCache[Style, Strip] = FIFOCache(16)
|
||||
self._filter_cache: FIFOCache[tuple[LineFilter, Color], Strip] = FIFOCache(4)
|
||||
self._line_length_cache: FIFOCache[
|
||||
tuple[int, Style | None],
|
||||
Strip,
|
||||
] = FIFOCache(4)
|
||||
self._crop_extend_cache: FIFOCache[
|
||||
tuple[int, int, Style | None],
|
||||
Strip,
|
||||
] = FIFOCache(4)
|
||||
self._render_cache: str | None = None
|
||||
self._link_ids: set[str] | None = None
|
||||
|
||||
@@ -222,6 +232,11 @@ class Strip:
|
||||
A new strip with the supplied cell length.
|
||||
"""
|
||||
|
||||
cache_key = (cell_length, style)
|
||||
cached_strip = self._line_length_cache.get(cache_key)
|
||||
if cached_strip is not None:
|
||||
return cached_strip
|
||||
|
||||
new_line: list[Segment]
|
||||
line = self._segments
|
||||
current_cell_length = self.cell_length
|
||||
@@ -233,6 +248,7 @@ class Strip:
|
||||
new_line = line + [
|
||||
_Segment(" " * (cell_length - current_cell_length), style)
|
||||
]
|
||||
strip = Strip(new_line, cell_length)
|
||||
|
||||
elif current_cell_length > cell_length:
|
||||
# Cell length is shorter so we need to truncate.
|
||||
@@ -249,11 +265,14 @@ class Strip:
|
||||
text = set_cell_size(text, cell_length - line_length)
|
||||
append(_Segment(text, segment_style))
|
||||
break
|
||||
strip = Strip(new_line, cell_length)
|
||||
else:
|
||||
# Strip is already the required cell length, so return self.
|
||||
return self
|
||||
strip = self
|
||||
|
||||
return Strip(new_line, cell_length)
|
||||
self._line_length_cache[cache_key] = strip
|
||||
|
||||
return strip
|
||||
|
||||
def simplify(self) -> Strip:
|
||||
"""Simplify the segments (join segments with same style)
|
||||
@@ -312,6 +331,25 @@ class Strip:
|
||||
]
|
||||
return Strip(segments, self._cell_length)
|
||||
|
||||
def crop_extend(self, start: int, end: int, style: Style | None) -> Strip:
|
||||
"""Crop between two points, extending the length if required.
|
||||
|
||||
Args:
|
||||
start: Start offset of crop.
|
||||
end: End offset of crop.
|
||||
style: Style of additional padding.
|
||||
|
||||
Returns:
|
||||
New cropped Strip.
|
||||
"""
|
||||
cache_key = (start, end, style)
|
||||
cached_result = self._crop_extend_cache.get(cache_key)
|
||||
if cached_result is not None:
|
||||
return cached_result
|
||||
strip = self.extend_cell_length(end).crop(start, end)
|
||||
self._crop_extend_cache[cache_key] = strip
|
||||
return strip
|
||||
|
||||
def crop(self, start: int, end: int | None = None) -> Strip:
|
||||
"""Crop a strip between two cell positions.
|
||||
|
||||
|
||||
@@ -1146,6 +1146,26 @@ class Widget(DOMNode):
|
||||
- (self.container_size.height - self.scrollbar_size_horizontal),
|
||||
)
|
||||
|
||||
@property
|
||||
def is_vertical_scroll_end(self) -> bool:
|
||||
"""Is the vertical scroll position at the maximum?"""
|
||||
return self.scroll_offset.y == self.max_scroll_y
|
||||
|
||||
@property
|
||||
def is_horizontal_scroll_end(self) -> bool:
|
||||
"""Is the horizontal scroll position at the maximum?"""
|
||||
return self.scroll_offset.x == self.max_scroll_x
|
||||
|
||||
@property
|
||||
def is_vertical_scrollbar_grabbed(self) -> bool:
|
||||
"""Is the user dragging the vertical scrollbar?"""
|
||||
return bool(self._vertical_scrollbar and self._vertical_scrollbar.grabbed)
|
||||
|
||||
@property
|
||||
def is_horizontal_scrollbar_grabbed(self) -> bool:
|
||||
"""Is the user dragging the vertical scrollbar?"""
|
||||
return bool(self._horizontal_scrollbar and self._horizontal_scrollbar.grabbed)
|
||||
|
||||
@property
|
||||
def scrollbar_corner(self) -> ScrollBarCorner:
|
||||
"""The scrollbar corner.
|
||||
|
||||
@@ -22,6 +22,7 @@ if typing.TYPE_CHECKING:
|
||||
from ._list_item import ListItem
|
||||
from ._list_view import ListView
|
||||
from ._loading_indicator import LoadingIndicator
|
||||
from ._log import Log
|
||||
from ._markdown import Markdown, MarkdownViewer
|
||||
from ._option_list import OptionList
|
||||
from ._placeholder import Placeholder
|
||||
@@ -29,6 +30,7 @@ if typing.TYPE_CHECKING:
|
||||
from ._progress_bar import ProgressBar
|
||||
from ._radio_button import RadioButton
|
||||
from ._radio_set import RadioSet
|
||||
from ._rich_log import RichLog
|
||||
from ._select import Select
|
||||
from ._selection_list import SelectionList
|
||||
from ._sparkline import Sparkline
|
||||
@@ -36,7 +38,6 @@ if typing.TYPE_CHECKING:
|
||||
from ._switch import Switch
|
||||
from ._tabbed_content import TabbedContent, TabPane
|
||||
from ._tabs import Tab, Tabs
|
||||
from ._text_log import TextLog
|
||||
from ._tooltip import Tooltip
|
||||
from ._tree import Tree
|
||||
from ._welcome import Welcome
|
||||
@@ -55,6 +56,7 @@ __all__ = [
|
||||
"ListItem",
|
||||
"ListView",
|
||||
"LoadingIndicator",
|
||||
"Log",
|
||||
"Markdown",
|
||||
"MarkdownViewer",
|
||||
"OptionList",
|
||||
@@ -72,7 +74,7 @@ __all__ = [
|
||||
"TabbedContent",
|
||||
"TabPane",
|
||||
"Tabs",
|
||||
"TextLog",
|
||||
"RichLog",
|
||||
"Tooltip",
|
||||
"Tree",
|
||||
"Welcome",
|
||||
|
||||
@@ -11,6 +11,7 @@ from ._label import Label as Label
|
||||
from ._list_item import ListItem as ListItem
|
||||
from ._list_view import ListView as ListView
|
||||
from ._loading_indicator import LoadingIndicator as LoadingIndicator
|
||||
from ._log import Log as Log
|
||||
from ._markdown import Markdown as Markdown
|
||||
from ._markdown import MarkdownViewer as MarkdownViewer
|
||||
from ._option_list import OptionList as OptionList
|
||||
@@ -19,6 +20,7 @@ from ._pretty import Pretty as Pretty
|
||||
from ._progress_bar import ProgressBar as ProgressBar
|
||||
from ._radio_button import RadioButton as RadioButton
|
||||
from ._radio_set import RadioSet as RadioSet
|
||||
from ._rich_log import RichLog as RichLog
|
||||
from ._select import Select as Select
|
||||
from ._selection_list import SelectionList as SelectionList
|
||||
from ._sparkline import Sparkline as Sparkline
|
||||
@@ -28,7 +30,6 @@ from ._tabbed_content import TabbedContent as TabbedContent
|
||||
from ._tabbed_content import TabPane as TabPane
|
||||
from ._tabs import Tab as Tab
|
||||
from ._tabs import Tabs as Tabs
|
||||
from ._text_log import TextLog as TextLog
|
||||
from ._tooltip import Tooltip as Tooltip
|
||||
from ._tree import Tree as Tree
|
||||
from ._welcome import Welcome as Welcome
|
||||
|
||||
309
src/textual/widgets/_log.py
Normal file
309
src/textual/widgets/_log.py
Normal file
@@ -0,0 +1,309 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import TYPE_CHECKING, Iterable, Optional, Sequence
|
||||
|
||||
from rich.cells import cell_len
|
||||
from rich.highlighter import ReprHighlighter
|
||||
from rich.segment import Segment
|
||||
from rich.style import Style
|
||||
from rich.text import Text
|
||||
|
||||
from .. import work
|
||||
from .._cache import LRUCache
|
||||
from .._line_split import line_split
|
||||
from ..geometry import Size
|
||||
from ..reactive import var
|
||||
from ..scroll_view import ScrollView
|
||||
from ..strip import Strip
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Self
|
||||
|
||||
_sub_escape = re.compile("[\u0000-\u0014]").sub
|
||||
|
||||
|
||||
class Log(ScrollView, can_focus=True):
|
||||
"""A widget to log text."""
|
||||
|
||||
DEFAULT_CSS = """
|
||||
Log {
|
||||
background: $surface;
|
||||
color: $text;
|
||||
overflow: scroll;
|
||||
}
|
||||
"""
|
||||
|
||||
max_lines: var[int | None] = var[Optional[int]](None)
|
||||
"""Maximum number of lines to show"""
|
||||
|
||||
auto_scroll: var[bool] = var(True)
|
||||
"""Automatically scroll to new lines."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
highlight: bool = False,
|
||||
max_lines: int | None = None,
|
||||
auto_scroll: bool = True,
|
||||
name: str | None = None,
|
||||
id: str | None = None,
|
||||
classes: str | None = None,
|
||||
disabled: bool = False,
|
||||
) -> None:
|
||||
"""Create a Log widget.
|
||||
|
||||
Args:
|
||||
highlight: Enable highlighting.
|
||||
max_lines: Maximum number of lines to display.
|
||||
auto_scroll: Scroll to end on new lines.
|
||||
name: The name of the text log.
|
||||
id: The ID of the text log in the DOM.
|
||||
classes: The CSS classes of the text log.
|
||||
disabled: Whether the text log is disabled or not.
|
||||
"""
|
||||
self.highlight = highlight
|
||||
"""Enable highlighting."""
|
||||
self.max_lines = max_lines
|
||||
self.auto_scroll = auto_scroll
|
||||
self._lines: list[str] = []
|
||||
self._width = 0
|
||||
self._updates = 0
|
||||
self._render_line_cache: LRUCache[int, Strip] = LRUCache(1024)
|
||||
self.highlighter = ReprHighlighter()
|
||||
"""The Rich Highlighter object to use, if `highlight=True`"""
|
||||
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
|
||||
|
||||
@property
|
||||
def lines(self) -> Sequence[str]:
|
||||
"""The raw lines in the TextLog.
|
||||
|
||||
Note that this attribute is read only.
|
||||
Changing the lines will not update the Log's contents.
|
||||
|
||||
"""
|
||||
return self._lines
|
||||
|
||||
def notify_style_update(self) -> None:
|
||||
"""Called by Textual when styles update."""
|
||||
self._render_line_cache.clear()
|
||||
|
||||
def _update_maximum_width(self, updates: int, size: int) -> None:
|
||||
"""Update the virtual size width.
|
||||
|
||||
Args:
|
||||
updates: A counter of updates.
|
||||
size: Maximum size of new lines.
|
||||
"""
|
||||
if updates == self._updates:
|
||||
self._width = max(size, self._width)
|
||||
self.virtual_size = Size(self._width, self.line_count)
|
||||
|
||||
@property
|
||||
def line_count(self) -> int:
|
||||
"""Number of lines of content."""
|
||||
if self._lines:
|
||||
return len(self._lines) - (self._lines[-1] == "")
|
||||
return 0
|
||||
|
||||
@classmethod
|
||||
def _process_line(cls, line: str) -> str:
|
||||
"""Process a line before it is rendered to remove control codes.
|
||||
|
||||
Args:
|
||||
line: A string.
|
||||
|
||||
Returns:
|
||||
New string with no control codes.
|
||||
"""
|
||||
return _sub_escape("<EFBFBD>", line.expandtabs())
|
||||
|
||||
@work(thread=True)
|
||||
def _update_size(self, updates: int, lines: list[str]) -> None:
|
||||
"""A thread worker to update the width in the background.
|
||||
|
||||
Args:
|
||||
updates: The update index at the time of invocation.
|
||||
lines: Lines that were added.
|
||||
"""
|
||||
if lines:
|
||||
_process_line = self._process_line
|
||||
max_length = max(cell_len(_process_line(line)) for line in lines)
|
||||
self.app.call_from_thread(self._update_maximum_width, updates, max_length)
|
||||
|
||||
def _prune_max_lines(self) -> None:
|
||||
"""Prune lines if there are more than the maximum."""
|
||||
if self.max_lines is None:
|
||||
return
|
||||
remove_lines = len(self._lines) - self.max_lines
|
||||
if remove_lines > 0:
|
||||
_cache = self._render_line_cache
|
||||
# We've removed some lines, which means the y values in the cache are out of sync
|
||||
# Calculated a new dict of cache values
|
||||
updated_cache = {
|
||||
y - remove_lines: _cache[y] for y in _cache.keys() if y > remove_lines
|
||||
}
|
||||
# Clear the cache
|
||||
_cache.clear()
|
||||
# Update the cache with previously calculated values
|
||||
for y, line in updated_cache.items():
|
||||
_cache[y] = line
|
||||
del self._lines[:remove_lines]
|
||||
|
||||
def write(
|
||||
self,
|
||||
data: str,
|
||||
scroll_end: bool | None = None,
|
||||
) -> Self:
|
||||
"""Write to the log.
|
||||
|
||||
Args:
|
||||
data: Data to write.
|
||||
scroll_end: Scroll to the end after writing, or `None` to use `self.auto_scroll`.
|
||||
|
||||
Returns:
|
||||
The `Log` instance.
|
||||
"""
|
||||
|
||||
if data:
|
||||
if not self._lines:
|
||||
self._lines.append("")
|
||||
for line, ending in line_split(data):
|
||||
self._lines[-1] += line
|
||||
self._width = max(
|
||||
self._width, cell_len(self._process_line(self._lines[-1]))
|
||||
)
|
||||
self.refresh_lines(len(self._lines) - 1)
|
||||
if ending:
|
||||
self._lines.append("")
|
||||
self.virtual_size = Size(self._width, self.line_count)
|
||||
|
||||
if self.max_lines is not None and len(self._lines) > self.max_lines:
|
||||
self._prune_max_lines()
|
||||
|
||||
auto_scroll = self.auto_scroll if scroll_end is None else scroll_end
|
||||
if auto_scroll and not self.is_vertical_scrollbar_grabbed:
|
||||
self.scroll_end(animate=False)
|
||||
return self
|
||||
|
||||
def write_line(self, line: str) -> Self:
|
||||
"""Write content on a new line.
|
||||
|
||||
Args:
|
||||
line: String to write to the log.
|
||||
|
||||
Returns:
|
||||
The `Log` instance.
|
||||
"""
|
||||
self.write_lines([line])
|
||||
return self
|
||||
|
||||
def write_lines(
|
||||
self,
|
||||
lines: Iterable[str],
|
||||
scroll_end: bool | None = None,
|
||||
) -> Self:
|
||||
"""Write an iterable of lines.
|
||||
|
||||
Args:
|
||||
lines: An iterable of strings to write.
|
||||
scroll_end: Scroll to the end after writing, or `None` to use `self.auto_scroll`.
|
||||
|
||||
Returns:
|
||||
The `Log` instance.
|
||||
"""
|
||||
auto_scroll = self.auto_scroll if scroll_end is None else scroll_end
|
||||
new_lines = []
|
||||
for line in lines:
|
||||
new_lines.extend(line.splitlines())
|
||||
start_line = len(self._lines)
|
||||
self._lines.extend(new_lines)
|
||||
if self.max_lines is not None and len(self._lines) > self.max_lines:
|
||||
self._prune_max_lines()
|
||||
self.virtual_size = Size(self._width, len(self._lines))
|
||||
self._update_size(self._updates, new_lines)
|
||||
self.refresh_lines(start_line, len(new_lines))
|
||||
if auto_scroll and not self.is_vertical_scrollbar_grabbed:
|
||||
self.scroll_end(animate=False)
|
||||
else:
|
||||
self.refresh()
|
||||
return self
|
||||
|
||||
def clear(self) -> Self:
|
||||
"""Clear the Log.
|
||||
|
||||
Returns:
|
||||
The `Log` instance.
|
||||
"""
|
||||
self._lines.clear()
|
||||
self._width = 0
|
||||
self._render_line_cache.clear()
|
||||
self._updates += 1
|
||||
self.virtual_size = Size(0, 0)
|
||||
return self
|
||||
|
||||
def render_line(self, y: int) -> Strip:
|
||||
"""Render a line of content.
|
||||
|
||||
Args:
|
||||
y: Y Coordinate of line.
|
||||
|
||||
Returns:
|
||||
A rendered line.
|
||||
"""
|
||||
scroll_x, scroll_y = self.scroll_offset
|
||||
strip = self._render_line(scroll_y + y, scroll_x, self.size.width)
|
||||
return strip
|
||||
|
||||
def _render_line(self, y: int, scroll_x: int, width: int) -> Strip:
|
||||
"""Render a line in to a cropped strip.
|
||||
|
||||
Args:
|
||||
y: Y offset of line.
|
||||
scroll_x: Current horizontal scroll.
|
||||
width: Width of the widget.
|
||||
|
||||
Returns:
|
||||
A Strip suitable for rendering.
|
||||
"""
|
||||
rich_style = self.rich_style
|
||||
if y >= len(self._lines):
|
||||
return Strip.blank(width, rich_style)
|
||||
|
||||
line = self._render_line_strip(y, rich_style)
|
||||
assert line._cell_length is not None
|
||||
line = line.crop_extend(scroll_x, scroll_x + width, rich_style)
|
||||
return line
|
||||
|
||||
def _render_line_strip(self, y: int, rich_style: Style) -> Strip:
|
||||
"""Render a line in to a Strip.
|
||||
|
||||
Args:
|
||||
y: Y offset of line.
|
||||
rich_style: Rich style of line.
|
||||
|
||||
Returns:
|
||||
An uncropped Strip.
|
||||
"""
|
||||
if y in self._render_line_cache:
|
||||
return self._render_line_cache[y]
|
||||
|
||||
_line = self._process_line(self._lines[y])
|
||||
|
||||
if self.highlight:
|
||||
line_text = self.highlighter(Text(_line, style=rich_style, no_wrap=True))
|
||||
line = Strip(line_text.render(self.app.console), cell_len(_line))
|
||||
else:
|
||||
line = Strip([Segment(_line, rich_style)], cell_len(_line))
|
||||
self._render_line_cache[y] = line
|
||||
return line
|
||||
|
||||
def refresh_lines(self, y_start: int, line_count: int = 1) -> None:
|
||||
"""Refresh one or more lines.
|
||||
|
||||
Args:
|
||||
y_start: First line to refresh.
|
||||
line_count: Total number of lines to refresh.
|
||||
"""
|
||||
for y in range(y_start, y_start + line_count):
|
||||
self._render_line_cache.discard(y)
|
||||
super().refresh_lines(y_start, line_count=line_count)
|
||||
@@ -22,11 +22,11 @@ if TYPE_CHECKING:
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
class TextLog(ScrollView, can_focus=True):
|
||||
class RichLog(ScrollView, can_focus=True):
|
||||
"""A widget for logging text."""
|
||||
|
||||
DEFAULT_CSS = """
|
||||
TextLog{
|
||||
RichLog{
|
||||
background: $surface;
|
||||
color: $text;
|
||||
overflow-y: scroll;
|
||||
@@ -54,7 +54,7 @@ class TextLog(ScrollView, can_focus=True):
|
||||
classes: str | None = None,
|
||||
disabled: bool = False,
|
||||
) -> None:
|
||||
"""Create a TextLog widget.
|
||||
"""Create a RichLog widget.
|
||||
|
||||
Args:
|
||||
max_lines: Maximum number of lines in the log or `None` for no maximum.
|
||||
@@ -137,7 +137,7 @@ class TextLog(ScrollView, can_focus=True):
|
||||
scroll_end: Enable automatic scroll to end, or `None` to use `self.auto_scroll`.
|
||||
|
||||
Returns:
|
||||
The `TextLog` instance.
|
||||
The `RichLog` instance.
|
||||
"""
|
||||
|
||||
auto_scroll = self.auto_scroll if scroll_end is None else scroll_end
|
||||
@@ -192,7 +192,7 @@ class TextLog(ScrollView, can_focus=True):
|
||||
"""Clear the text log.
|
||||
|
||||
Returns:
|
||||
The `TextLog` instance.
|
||||
The `RichLog` instance.
|
||||
"""
|
||||
self.lines.clear()
|
||||
self._line_cache.clear()
|
||||
@@ -215,7 +215,7 @@ class TextLog(ScrollView, can_focus=True):
|
||||
crop: Region within visible area to.
|
||||
|
||||
Returns:
|
||||
A list of list of segments
|
||||
A list of list of segments.
|
||||
"""
|
||||
lines = self._styles_cache.render_widget(self, crop)
|
||||
return lines
|
||||
@@ -228,11 +228,7 @@ class TextLog(ScrollView, can_focus=True):
|
||||
if key in self._line_cache:
|
||||
return self._line_cache[key]
|
||||
|
||||
line = (
|
||||
self.lines[y]
|
||||
.adjust_cell_length(max(self.max_width, width), self.rich_style)
|
||||
.crop(scroll_x, scroll_x + width)
|
||||
)
|
||||
line = self.lines[y].crop_extend(scroll_x, scroll_x + width, self.rich_style)
|
||||
|
||||
self._line_cache[key] = line
|
||||
return line
|
||||
File diff suppressed because one or more lines are too long
@@ -1,10 +1,10 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual import events
|
||||
from textual.widgets import TextLog
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
class PrintLogger(TextLog):
|
||||
"""A TextLog which captures printed text."""
|
||||
class PrintLogger(RichLog):
|
||||
"""A RichLog which captures printed text."""
|
||||
|
||||
def on_print(self, event: events.Print) -> None:
|
||||
self.write(event.text)
|
||||
@@ -15,10 +15,10 @@ class CaptureApp(App):
|
||||
yield PrintLogger()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.query_one(TextLog).write("TextLog")
|
||||
self.query_one(TextLog).begin_capture_print()
|
||||
self.query_one(RichLog).write("RichLog")
|
||||
self.query_one(RichLog).begin_capture_print()
|
||||
print("This will be captured!")
|
||||
self.query_one(TextLog).end_capture_print()
|
||||
self.query_one(RichLog).end_capture_print()
|
||||
print("This will *not* be captured")
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from rich.text import Text
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import VerticalScroll
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import TextLog
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
class MyWidget(Widget):
|
||||
@@ -22,7 +22,7 @@ class ScrollViewApp(App):
|
||||
align: center middle;
|
||||
}
|
||||
|
||||
TextLog {
|
||||
RichLog {
|
||||
width:13;
|
||||
height:10;
|
||||
}
|
||||
@@ -41,11 +41,11 @@ class ScrollViewApp(App):
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
yield RichLog()
|
||||
yield VerticalScroll(MyWidget())
|
||||
|
||||
def on_ready(self) -> None:
|
||||
self.query_one(TextLog).write("\n".join(f"{n} 0123456789" for n in range(20)))
|
||||
self.query_one(RichLog).write("\n".join(f"{n} 0123456789" for n in range(20)))
|
||||
self.query_one(VerticalScroll).scroll_end(animate=False)
|
||||
|
||||
|
||||
|
||||
22
tests/snapshot_tests/snapshot_apps/log_write.py
Normal file
22
tests/snapshot_tests/snapshot_apps/log_write.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Log
|
||||
from textual.containers import Horizontal
|
||||
|
||||
|
||||
class LogApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Log()
|
||||
|
||||
def on_ready(self) -> None:
|
||||
log = self.query_one(Log)
|
||||
log.write("Hello,")
|
||||
log.write(" World")
|
||||
log.write("!\nWhat's up?")
|
||||
log.write("")
|
||||
log.write("\n")
|
||||
log.write("FOO")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = LogApp()
|
||||
app.run()
|
||||
38
tests/snapshot_tests/snapshot_apps/log_write_lines.py
Normal file
38
tests/snapshot_tests/snapshot_apps/log_write_lines.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Log
|
||||
from textual.containers import Horizontal
|
||||
|
||||
TEXT = (
|
||||
"""I must not fear.
|
||||
Fear is the mind-killer.
|
||||
Fear is the little-death that brings total obliteration.
|
||||
I will face my fear.
|
||||
I will permit it to pass over me and through me.
|
||||
And when it has gone past, I will turn the inner eye to see its path.
|
||||
Where the fear has gone there will be nothing. Only I will remain.
|
||||
"""
|
||||
* 20
|
||||
)
|
||||
|
||||
|
||||
class LogApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
with Horizontal():
|
||||
yield Log(id="log1", auto_scroll=False)
|
||||
yield Log(id="log2", auto_scroll=True)
|
||||
yield Log(id="log3")
|
||||
yield Log(id="log4", max_lines=6)
|
||||
|
||||
def on_ready(self) -> None:
|
||||
self.query_one("#log1", Log).write_line(TEXT)
|
||||
self.query_one("#log2", Log).write_line(TEXT)
|
||||
self.query_one("#log3", Log).write_line(TEXT)
|
||||
self.query_one("#log4", Log).write_line(TEXT)
|
||||
|
||||
self.query_one("#log3", Log).clear()
|
||||
self.query_one("#log3", Log).write_line("Hello, World")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = LogApp()
|
||||
app.run()
|
||||
@@ -1,20 +1,20 @@
|
||||
from textual.app import App
|
||||
from textual.widgets import TextLog
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
class TextLogLines(App):
|
||||
class RichLogLines(App):
|
||||
count = 0
|
||||
|
||||
def compose(self):
|
||||
yield TextLog(max_lines=3)
|
||||
yield RichLog(max_lines=3)
|
||||
|
||||
async def on_key(self):
|
||||
self.count += 1
|
||||
log_widget = self.query_one(TextLog)
|
||||
log_widget = self.query_one(RichLog)
|
||||
log_widget.write(f"Key press #{self.count}")
|
||||
|
||||
|
||||
app = TextLogLines()
|
||||
app = RichLogLines()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -1,11 +1,11 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import TextLog
|
||||
from textual.widgets import RichLog
|
||||
from textual.containers import Horizontal
|
||||
|
||||
|
||||
class TextLogScrollApp(App):
|
||||
class RichLogScrollApp(App):
|
||||
CSS = """
|
||||
TextLog{
|
||||
RichLog{
|
||||
width: 1fr;
|
||||
height: 10;
|
||||
}
|
||||
@@ -14,22 +14,22 @@ class TextLogScrollApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
with Horizontal():
|
||||
# Don't scroll on write
|
||||
yield TextLog(id="textlog1", auto_scroll=False)
|
||||
yield RichLog(id="richlog1", auto_scroll=False)
|
||||
# Scroll on write
|
||||
yield TextLog(id="textlog2", auto_scroll=True)
|
||||
yield RichLog(id="richlog2", auto_scroll=True)
|
||||
# Scroll on write, but disabled on write()
|
||||
yield TextLog(id="textlog3", auto_scroll=True)
|
||||
yield RichLog(id="richlog3", auto_scroll=True)
|
||||
|
||||
def on_ready(self) -> None:
|
||||
lines = [f"Line {n}" for n in range(20)]
|
||||
for line in lines:
|
||||
self.query_one("#textlog1", TextLog).write(line)
|
||||
self.query_one("#richlog1", RichLog).write(line)
|
||||
for line in lines:
|
||||
self.query_one("#textlog2", TextLog).write(line)
|
||||
self.query_one("#richlog2", RichLog).write(line)
|
||||
for line in lines:
|
||||
self.query_one("#textlog3", TextLog).write(line, scroll_end=False)
|
||||
self.query_one("#richlog3", RichLog).write(line, scroll_end=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TextLogScrollApp()
|
||||
app = RichLogScrollApp()
|
||||
app.run()
|
||||
@@ -1,18 +1,18 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import TextLog
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
class TextLogApp(App):
|
||||
class RichLogApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
yield RichLog()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
tl = self.query_one(TextLog)
|
||||
tl = self.query_one(RichLog)
|
||||
tl.write("Hello")
|
||||
tl.write("")
|
||||
tl.write("World")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = TextLogApp()
|
||||
app = RichLogApp()
|
||||
app.run()
|
||||
|
||||
@@ -162,8 +162,16 @@ def test_list_view(snap_compare):
|
||||
)
|
||||
|
||||
|
||||
def test_textlog_max_lines(snap_compare):
|
||||
assert snap_compare("snapshot_apps/textlog_max_lines.py", press=[*"abcde"])
|
||||
def test_richlog_max_lines(snap_compare):
|
||||
assert snap_compare("snapshot_apps/richlog_max_lines.py", press=[*"abcde"])
|
||||
|
||||
|
||||
def test_log_write_lines(snap_compare):
|
||||
assert snap_compare("snapshot_apps/log_write_lines.py")
|
||||
|
||||
|
||||
def test_log_write(snap_compare):
|
||||
assert snap_compare("snapshot_apps/log_write.py")
|
||||
|
||||
|
||||
def test_fr_units(snap_compare):
|
||||
@@ -421,8 +429,8 @@ def test_table_markup(snap_compare):
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "table_markup.py")
|
||||
|
||||
|
||||
def test_textlog_scroll(snap_compare):
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "textlog_scroll.py")
|
||||
def test_richlog_scroll(snap_compare):
|
||||
assert snap_compare(SNAPSHOT_APPS_DIR / "richlog_scroll.py")
|
||||
|
||||
|
||||
def test_tabs_invalidate(snap_compare):
|
||||
|
||||
@@ -240,3 +240,22 @@ def test_fifo_cache_hits():
|
||||
assert cache.misses == 2
|
||||
|
||||
assert str(cache) == "<FIFOCache maxsize=4 hits=3 misses=2>"
|
||||
|
||||
|
||||
def test_discard():
|
||||
cache = LRUCache(maxsize=3)
|
||||
cache.set("key1", "value1")
|
||||
cache.set("key2", "value2")
|
||||
cache.set("key3", "value3")
|
||||
|
||||
assert len(cache) == 3
|
||||
assert cache.get("key1") == "value1"
|
||||
|
||||
cache.discard("key1")
|
||||
|
||||
assert len(cache) == 2
|
||||
assert cache.get("key1") is None
|
||||
|
||||
cache.discard("key4") # key that does not exist
|
||||
|
||||
assert len(cache) == 2 # size should not change
|
||||
|
||||
@@ -3,7 +3,7 @@ from threading import Thread
|
||||
import pytest
|
||||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import TextLog
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
def test_call_from_thread_app_not_running():
|
||||
@@ -27,7 +27,7 @@ def test_call_from_thread():
|
||||
def run(self) -> None:
|
||||
def write_stuff(text: str) -> None:
|
||||
"""Write stuff to a widget."""
|
||||
self.app.query_one(TextLog).write(text)
|
||||
self.app.query_one(RichLog).write(text)
|
||||
|
||||
self.app.call_from_thread(write_stuff, "Hello")
|
||||
# Exit the app with a code we can assert
|
||||
@@ -37,7 +37,7 @@ def test_call_from_thread():
|
||||
"""Trivial app with a single widget."""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
yield RichLog()
|
||||
|
||||
def on_ready(self) -> None:
|
||||
"""Launch a thread which will modify the app."""
|
||||
|
||||
@@ -18,9 +18,9 @@ from textual.widgets import (
|
||||
OptionList,
|
||||
RadioButton,
|
||||
RadioSet,
|
||||
RichLog,
|
||||
Select,
|
||||
Switch,
|
||||
TextLog,
|
||||
Tree,
|
||||
)
|
||||
|
||||
@@ -37,7 +37,7 @@ class DisableApp(App[None]):
|
||||
Input(),
|
||||
ListView(),
|
||||
Switch(),
|
||||
TextLog(),
|
||||
RichLog(),
|
||||
Tree("Test"),
|
||||
Markdown(),
|
||||
MarkdownViewer(),
|
||||
|
||||
26
tests/test_line_split.py
Normal file
26
tests/test_line_split.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from textual._line_split import line_split
|
||||
|
||||
|
||||
def test_split_string_to_lines_and_endings():
|
||||
# Test with different line endings
|
||||
assert line_split("Hello\r\nWorld\n") == [("Hello", "\r\n"), ("World", "\n")]
|
||||
assert line_split("Hello\rWorld\r\n") == [("Hello", "\r"), ("World", "\r\n")]
|
||||
assert line_split("Hello\nWorld\r") == [("Hello", "\n"), ("World", "\r")]
|
||||
|
||||
# Test with no line endings
|
||||
assert line_split("Hello World") == [("Hello World", "")]
|
||||
|
||||
# Test with empty string
|
||||
assert line_split("") == []
|
||||
|
||||
# Test with multiple lines having same line endings
|
||||
assert line_split("Hello\nWorld\nHow\nAre\nYou\n") == [
|
||||
("Hello", "\n"),
|
||||
("World", "\n"),
|
||||
("How", "\n"),
|
||||
("Are", "\n"),
|
||||
("You", "\n"),
|
||||
]
|
||||
|
||||
# Test with a single character
|
||||
assert line_split("a") == [("a", "")]
|
||||
8
tests/test_log.py
Normal file
8
tests/test_log.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from textual.widgets import Log
|
||||
|
||||
|
||||
def test_process_line():
|
||||
log = Log()
|
||||
assert log._process_line("foo") == "foo"
|
||||
assert log._process_line("foo\t") == "foo "
|
||||
assert log._process_line("\0foo") == "<EFBFBD>foo"
|
||||
@@ -108,14 +108,14 @@ URL](https://example.com)\
|
||||
assert isinstance(paragraph, MD.MarkdownParagraph)
|
||||
assert paragraph._text.plain == "My site has this URL"
|
||||
expected_spans = [
|
||||
Span(0, 8, Style()),
|
||||
Span(8, 11, Style(meta={"@click": "link('https://example.com')"})),
|
||||
Span(11, 12, Style(meta={"@click": "link('https://example.com')"})),
|
||||
Span(12, 16, Style(meta={"@click": "link('https://example.com')"})),
|
||||
Span(16, 17, Style(meta={"@click": "link('https://example.com')"})),
|
||||
Span(17, 20, Style(meta={"@click": "link('https://example.com')"})),
|
||||
]
|
||||
assert paragraph._text.spans == expected_spans
|
||||
|
||||
assert paragraph._text.spans == expected_spans
|
||||
|
||||
|
||||
async def test_load_non_existing_file() -> None:
|
||||
|
||||
@@ -11,7 +11,7 @@ from textual.app import (
|
||||
UnknownModeError,
|
||||
)
|
||||
from textual.screen import ModalScreen, Screen
|
||||
from textual.widgets import Footer, Header, Label, TextLog
|
||||
from textual.widgets import Footer, Header, Label, RichLog
|
||||
|
||||
FRUITS = cycle("apple mango strawberry banana peach pear melon watermelon".split())
|
||||
|
||||
@@ -61,7 +61,7 @@ class FruitModal(ModalScreen[str], ScreenBindingsMixin):
|
||||
|
||||
class FruitsScreen(ScreenBindingsMixin):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
yield RichLog()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from rich.text import Text
|
||||
|
||||
from textual.widgets import TextLog
|
||||
from textual.widgets import RichLog
|
||||
|
||||
|
||||
def test_make_renderable_expand_tabs():
|
||||
# Regression test for https://github.com/Textualize/textual/issues/3007
|
||||
text_log = TextLog()
|
||||
text_log = RichLog()
|
||||
renderable = text_log._make_renderable("\tfoo")
|
||||
assert isinstance(renderable, Text)
|
||||
assert renderable.plain == " foo"
|
||||
|
||||
Reference in New Issue
Block a user