From a9afc1cf7207340c706e50e6c4d2d6f91d7fc262 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 9 Oct 2024 19:41:32 +0100 Subject: [PATCH 001/104] new demo WIP --- src/textual/demo2/__main__.py | 5 ++ src/textual/demo2/demo_app.py | 41 ++++++++++++ src/textual/demo2/page.py | 11 ++++ src/textual/demo2/projects.py | 89 ++++++++++++++++++++++++++ src/textual/demo2/welcome.py | 115 ++++++++++++++++++++++++++++++++++ src/textual/widget.py | 18 ++++++ 6 files changed, 279 insertions(+) create mode 100644 src/textual/demo2/__main__.py create mode 100644 src/textual/demo2/demo_app.py create mode 100644 src/textual/demo2/page.py create mode 100644 src/textual/demo2/projects.py create mode 100644 src/textual/demo2/welcome.py diff --git a/src/textual/demo2/__main__.py b/src/textual/demo2/__main__.py new file mode 100644 index 0000000000..d254f85c41 --- /dev/null +++ b/src/textual/demo2/__main__.py @@ -0,0 +1,5 @@ +from textual.demo2.demo_app import DemoApp + +if __name__ == "__main__": + app = DemoApp() + app.run() diff --git a/src/textual/demo2/demo_app.py b/src/textual/demo2/demo_app.py new file mode 100644 index 0000000000..383d325b6d --- /dev/null +++ b/src/textual/demo2/demo_app.py @@ -0,0 +1,41 @@ +from textual.app import App, ComposeResult +from textual.binding import Binding +from textual.demo2.page import Page +from textual.demo2.projects import ProjectsPage +from textual.demo2.welcome import WelcomePage +from textual.screen import Screen +from textual.widgets import Footer + + +class DemoScreen(Screen): + DEFAULT_CSS = """ + DemoScreen { + layout: horizontal; + } + """ + + BINDINGS = [ + Binding("1", "page(1)", "page 1", tooltip="Go to page 1"), + Binding("2", "page(2)", "page 2", tooltip="Go to page 2"), + Binding("3", "page(3)", "page 3", tooltip="Go to page 3"), + Binding("4", "page(4)", "page 4", tooltip="Go to page 4"), + ] + + def compose(self) -> ComposeResult: + yield Footer() + yield WelcomePage(id="page1") + yield ProjectsPage(id="page2") + yield Page(id="page3") + yield Page(id="page4") + + @property + def allow_horizontal_scroll(self) -> bool: + return True + + def action_page(self, page: int) -> None: + self.query_one(f"#page{page}").scroll_visible() + + +class DemoApp(App): + def get_default_screen(self) -> Screen: + return DemoScreen() diff --git a/src/textual/demo2/page.py b/src/textual/demo2/page.py new file mode 100644 index 0000000000..f38837e172 --- /dev/null +++ b/src/textual/demo2/page.py @@ -0,0 +1,11 @@ +from textual.containers import Vertical + + +class Page(Vertical): + DEFAULT_CSS = """ + Page { + width: 100%; + height: 1fr; + overflow-y: auto; + } + """ diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py new file mode 100644 index 0000000000..24fbeede5f --- /dev/null +++ b/src/textual/demo2/projects.py @@ -0,0 +1,89 @@ +from dataclasses import dataclass + +from textual.app import ComposeResult +from textual.containers import Grid, Horizontal, Vertical +from textual.demo2.page import Page +from textual.widgets import Label, Static + + +@dataclass +class ProjectInfo: + title: str + url: str + description: str + + +PROJECTS = [ + ProjectInfo( + "Posting", + "https://posting.sh/", + "Posting is a beautiful open-source terminal app for developing and testing APIs.", + ) +] * 4 + + +class Project(Vertical, can_focus=True): + DEFAULT_CSS = """ + Project { + + # margin: 1 2; + width: 1fr; + + padding: 1 2; + # background: $boost; + + border: tall transparent; + + &:focus-within { + border: tall $accent; + background: $boost; + } + #title { + text-style: bold; + } + .stars { + color: $secondary; + text-align: right; + width: 1fr; + } + } + """ + + def __init__(self, project_info: ProjectInfo) -> None: + self.project_info = project_info + super().__init__() + + def compose(self) -> ComposeResult: + with Horizontal(): + yield Label(self.project_info.title, id="title") + yield Label("★ 23K", classes="stars") + yield Static(self.project_info.description) + + +class ProjectsPage(Page): + DEFAULT_CSS = """ + ProjectsPage { + layout: grid; + + margin: 1; + Grid { + + margin: 1 2; + padding: 1 2; + background: $boost; + width: 1fr; + height: 1fr; + grid-size: 2; + grid-gutter: 1 1; + + + # keyline:thin $success; + + } + } + """ + + def compose(self) -> ComposeResult: + with Grid(): + for project in PROJECTS: + yield Project(project) diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/welcome.py new file mode 100644 index 0000000000..65b4b68d2e --- /dev/null +++ b/src/textual/demo2/welcome.py @@ -0,0 +1,115 @@ +from importlib.metadata import version + +import httpx + +from textual import work +from textual.app import ComposeResult +from textual.containers import Center, Horizontal, Vertical +from textual.demo2.page import Page +from textual.reactive import reactive +from textual.widgets import Digits, Label, Markdown + +WHAT_IS_TEXTUAL = """\ +### Turbo charge your developers! + +* Build sophisticated applications — fast! +* No front-end skills required. +* Elegant Python API from the developer of [Rich](https://github.com/Textualize/rich). + + +* Deploy Textual as a terminal application, over SSH, *or* a [web application](https://github.com/Textualize/textual-web)! +""" + + +class StarCount(Vertical): + DEFAULT_CSS = """ + StarCount { + dock: top; + height: 5; + border-bottom: hkey $background; + border-top: hkey $background; + layout: horizontal; + Layout { + margin-top: 1; + } + background: $boost; + padding: 0 1; + color: $warning; + Label { + text-style: bold; + } + LoadingIndicator { + background: transparent !important; + } + } + """ + stars = reactive(25251, recompose=True) + forks = reactive(776, recompose=True) + + @work + async def get_stars(self): + try: + async with httpx.AsyncClient() as client: + repository_json = ( + await client.get("https://api.github.com/repos/textualize/textual") + ).json() + self.stars = repository_json["stargazers_count"] + self.forks = repository_json["forks"] + except Exception: + self.notify( + "Unable to get star count (maybe rate-limited)", + title="GitHub stars", + severity="error", + ) + + self.loading = False + + def on_mount(self) -> None: + self.loading = True + self.get_stars() + + def compose(self) -> ComposeResult: + def thousands(number: int) -> str: + if number > 2000: + return f"{number / 1000:.1f}K " + return str(number) + + with Horizontal(): + yield Label("GitHub ★ ") + yield Digits(thousands(self.stars)).with_tooltip( + f"{self.stars} GitHub stars" + ) + yield Label("Forks ") + yield Digits(str(self.forks)).with_tooltip(f"{self.forks} Forks") + + +class WelcomePage(Page): + DEFAULT_CSS = """ + WelcomePage { + align: center middle; + Digits { + width: auto; + + } + Collapsible { + margin: 2 4; + } + Markdown { + background: $boost; + margin: 2 2; + padding: 1 2 0 2; + max-width: 80; + } + } + """ + + def compose(self) -> ComposeResult: + yield StarCount() + with Center(): + yield Label("Textual") + with Center(): + yield Digits(version("textual")) + with Center(): + yield Label("The [i]lean application[/i] framework") + with Center(): + yield Markdown(WHAT_IS_TEXTUAL) diff --git a/src/textual/widget.py b/src/textual/widget.py index 989d9762ce..711aec057d 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -665,6 +665,24 @@ def tooltip(self, tooltip: RenderableType | None): except NoScreen: pass + def with_tooltip(self, tooltip: RenderableType | None) -> Self: + """Chainable method to set a tooltip. + + Example: + ```python + def compose(self) -> ComposeResult: + yield Label("Hello").with_tooltip("A greeting") + ``` + + Args: + tooltip: New tooltip, or `None` to clear the tooltip. + + Returns: + Self. + """ + self.tooltip = tooltip + return self + def allow_focus(self) -> bool: """Check if the widget is permitted to focus. From 42489797371b4958dc578594adca7058f848c09c Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 10 Oct 2024 17:24:15 +0100 Subject: [PATCH 002/104] projects page --- src/textual/app.py | 8 ++- src/textual/css/_styles_builder.py | 14 ++++- src/textual/css/constants.py | 1 + src/textual/demo2/demo_app.py | 54 +++++++------------ src/textual/demo2/page.py | 84 +++++++++++++++++++++++++++++- src/textual/demo2/projects.py | 80 ++++++++++++++++++++-------- src/textual/demo2/welcome.py | 31 ++++++----- src/textual/dom.py | 9 +++- src/textual/widget.py | 8 +++ 9 files changed, 211 insertions(+), 78 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index b49cca30d8..0ac65b9fbf 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -372,6 +372,7 @@ class App(Generic[ReturnType], DOMNode): align: center middle; .-maximized { dock: initial !important; + margin: 2 4; } } /* Fade the header title when app is blurred */ @@ -413,6 +414,9 @@ class MyApp(App[None]): ... ``` """ + DEFAULT_MODE: ClassVar[str] = "_default" + """Name of the default mode.""" + SCREENS: ClassVar[dict[str, Callable[[], Screen[Any]]]] = {} """Screens associated with the app for the lifetime of the app.""" @@ -574,9 +578,9 @@ def __init__( self._workers = WorkerManager(self) self.error_console = Console(markup=False, highlight=False, stderr=True) self.driver_class = driver_class or self.get_driver_class() - self._screen_stacks: dict[str, list[Screen[Any]]] = {"_default": []} + self._screen_stacks: dict[str, list[Screen[Any]]] = {self.DEFAULT_MODE: []} """A stack of screens per mode.""" - self._current_mode: str = "_default" + self._current_mode: str = self.DEFAULT_MODE """The current mode the app is in.""" self._sync_available = False diff --git a/src/textual/css/_styles_builder.py b/src/textual/css/_styles_builder.py index 6d1baf45e8..b11cc4810d 100644 --- a/src/textual/css/_styles_builder.py +++ b/src/textual/css/_styles_builder.py @@ -540,10 +540,11 @@ def process_outline_left(self, name: str, tokens: list[Token]) -> None: def process_keyline(self, name: str, tokens: list[Token]) -> None: if not tokens: return - if len(tokens) > 2: + if len(tokens) > 3: self.error(name, tokens[0], keyline_help_text()) keyline_style = "none" keyline_color = Color.parse("green") + keyline_alpha = 1.0 for token in tokens: if token.name == "color": try: @@ -562,7 +563,16 @@ def process_keyline(self, name: str, tokens: list[Token]) -> None: if keyline_style not in VALID_KEYLINE: self.error(name, token, keyline_help_text()) - self.styles._rules["keyline"] = (keyline_style, keyline_color) + elif token.name == "scalar": + alpha_scalar = Scalar.parse(token.value) + if alpha_scalar.unit != Unit.PERCENT: + self.error(name, token, "alpha must be given as a percentage.") + keyline_alpha = alpha_scalar.value / 100.0 + + self.styles._rules["keyline"] = ( + keyline_style, + keyline_color.multiply_alpha(keyline_alpha), + ) def process_offset(self, name: str, tokens: list[Token]) -> None: def offset_error(name: str, token: Token) -> None: diff --git a/src/textual/css/constants.py b/src/textual/css/constants.py index 026e78be45..9189efa0ea 100644 --- a/src/textual/css/constants.py +++ b/src/textual/css/constants.py @@ -69,6 +69,7 @@ "focus-within", "focus", "hover", + "hover-within", "inline", "light", "nocolor", diff --git a/src/textual/demo2/demo_app.py b/src/textual/demo2/demo_app.py index 383d325b6d..09f31e3cf8 100644 --- a/src/textual/demo2/demo_app.py +++ b/src/textual/demo2/demo_app.py @@ -1,41 +1,27 @@ -from textual.app import App, ComposeResult +from textual.app import App from textual.binding import Binding -from textual.demo2.page import Page -from textual.demo2.projects import ProjectsPage -from textual.demo2.welcome import WelcomePage -from textual.screen import Screen -from textual.widgets import Footer +from textual.demo2.projects import ProjectsScreen +from textual.demo2.welcome import WelcomeScreen -class DemoScreen(Screen): - DEFAULT_CSS = """ - DemoScreen { - layout: horizontal; - } - """ +class DemoApp(App): + MODES = {"welcome": WelcomeScreen, "projects": ProjectsScreen} + # DEFAULT_MODE = "welcome" BINDINGS = [ - Binding("1", "page(1)", "page 1", tooltip="Go to page 1"), - Binding("2", "page(2)", "page 2", tooltip="Go to page 2"), - Binding("3", "page(3)", "page 3", tooltip="Go to page 3"), - Binding("4", "page(4)", "page 4", tooltip="Go to page 4"), + Binding( + "w", + "app.switch_mode('welcome')", + "welcome", + tooltip="Show the welcome screen", + ), + Binding( + "p", + "app.switch_mode('projects')", + "projects", + tooltip="A selection of Textual projects", + ), ] - def compose(self) -> ComposeResult: - yield Footer() - yield WelcomePage(id="page1") - yield ProjectsPage(id="page2") - yield Page(id="page3") - yield Page(id="page4") - - @property - def allow_horizontal_scroll(self) -> bool: - return True - - def action_page(self, page: int) -> None: - self.query_one(f"#page{page}").scroll_visible() - - -class DemoApp(App): - def get_default_screen(self) -> Screen: - return DemoScreen() + def on_mount(self) -> None: + self.switch_mode("welcome") diff --git a/src/textual/demo2/page.py b/src/textual/demo2/page.py index f38837e172..b3463b0a27 100644 --- a/src/textual/demo2/page.py +++ b/src/textual/demo2/page.py @@ -1,7 +1,51 @@ -from textual.containers import Vertical +import inspect +from rich.syntax import Syntax -class Page(Vertical): +from textual import work +from textual.app import ComposeResult +from textual.binding import Binding +from textual.containers import ScrollableContainer +from textual.screen import ModalScreen, Screen +from textual.widgets import Static + + +class CodeScreen(ModalScreen): + DEFAULT_CSS = """ + CodeScreen { + #code { + border: heavy $accent; + margin: 2 4; + scrollbar-gutter: stable; + Static { + width: auto; + } + } + } + """ + BINDINGS = [("escape", "dismiss", "Dismiss code")] + + def __init__(self, title: str, code: str) -> None: + super().__init__() + self.code = code + self.title = title + + def compose(self) -> ComposeResult: + with ScrollableContainer(id="code"): + yield Static( + Syntax( + self.code, lexer="python", indent_guides=True, line_numbers=True + ), + expand=True, + ) + + def on_mount(self): + code_widget = self.query_one("#code") + code_widget.border_title = self.title + code_widget.border_subtitle = "Escape to close" + + +class PageScreen(Screen): DEFAULT_CSS = """ Page { width: 100%; @@ -9,3 +53,39 @@ class Page(Vertical): overflow-y: auto; } """ + BINDINGS = [ + Binding( + "c", + "show_code", + "show code", + tooltip="Show the code used to generate this screen", + ) + ] + + @work(thread=True) + def get_code(self, source_file: str) -> str | None: + try: + with open(source_file, "rt") as file_: + return file_.read() + except Exception: + return None + + async def action_show_code(self): + source_file = inspect.getsourcefile(self.__class__) + if source_file is None: + self.notify( + "Could not get the code for this page", + title="Show code", + severity="error", + ) + return + + code = await self.get_code(source_file).wait() + if code is None: + self.notify( + "Could not get the code for this page", + title="Show code", + severity="error", + ) + else: + self.app.push_screen(CodeScreen("Code for this page", code)) diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index 24fbeede5f..7043a7b5b5 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -1,9 +1,10 @@ from dataclasses import dataclass +from textual import events, on from textual.app import ComposeResult -from textual.containers import Grid, Horizontal, Vertical -from textual.demo2.page import Page -from textual.widgets import Label, Static +from textual.containers import Grid, Horizontal, VerticalScroll +from textual.demo2.page import PageScreen +from textual.widgets import Footer, Label, Static @dataclass @@ -19,55 +20,90 @@ class ProjectInfo: "https://posting.sh/", "Posting is a beautiful open-source terminal app for developing and testing APIs.", ) -] * 4 +] * 5 -class Project(Vertical, can_focus=True): +class Link(Label): + """The link in the Project widget.""" + + DEFAULT_CSS = """ + Link { + color: $accent; + text-style: underline; + &:hover { text-style: reverse;} + } + """ + + def __init__(self, url: str) -> None: + super().__init__(url) + self.url = url + self.tooltip = "Click to open Repository URL" + + def on_click(self) -> None: + self.app.open_url(self.url) + + +class Project(VerticalScroll, can_focus=True): + ALLOW_MAXIMIZE = True DEFAULT_CSS = """ Project { - - # margin: 1 2; width: 1fr; - - padding: 1 2; - # background: $boost; - + min-height: 8; + padding: 0 1; border: tall transparent; + opacity: 0.5; &:focus-within { border: tall $accent; background: $boost; + opacity: 1.0; } - #title { - text-style: bold; - } + #title { text-style: bold; } .stars { color: $secondary; text-align: right; width: 1fr; } + .header { height: 1; } + .link { + color: $accent; + text-style: underline; + } + .description { color: $text-muted; } + &.-hover { opacity: 1; } } """ + BINDINGS = [("enter", "open_repository", "open repo")] + def __init__(self, project_info: ProjectInfo) -> None: self.project_info = project_info super().__init__() def compose(self) -> ComposeResult: - with Horizontal(): + with Horizontal(classes="header"): yield Label(self.project_info.title, id="title") yield Label("★ 23K", classes="stars") - yield Static(self.project_info.description) + yield Link(self.project_info.url) + yield Static(self.project_info.description, classes="description") + @on(events.Enter) + @on(events.Leave) + def on_enter(self, event: events.Enter): + event.stop() + self.set_class(self.is_mouse_over, "-hover") -class ProjectsPage(Page): + def action_open_repository(self) -> None: + self.app.open_url(self.project_info.url) + + +class ProjectsScreen(PageScreen): DEFAULT_CSS = """ - ProjectsPage { + ProjectsScreen { layout: grid; margin: 1; Grid { - margin: 1 2; padding: 1 2; background: $boost; @@ -75,9 +111,8 @@ class ProjectsPage(Page): height: 1fr; grid-size: 2; grid-gutter: 1 1; - - - # keyline:thin $success; + hatch: right $primary 50%; + keyline:heavy $background; } } @@ -87,3 +122,4 @@ def compose(self) -> ComposeResult: with Grid(): for project in PROJECTS: yield Project(project) + yield Footer() diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/welcome.py index 65b4b68d2e..c4b473f5fb 100644 --- a/src/textual/demo2/welcome.py +++ b/src/textual/demo2/welcome.py @@ -5,9 +5,9 @@ from textual import work from textual.app import ComposeResult from textual.containers import Center, Horizontal, Vertical -from textual.demo2.page import Page +from textual.demo2.page import PageScreen from textual.reactive import reactive -from textual.widgets import Digits, Label, Markdown +from textual.widgets import Digits, Footer, Label, Markdown WHAT_IS_TEXTUAL = """\ ### Turbo charge your developers! @@ -29,18 +29,12 @@ class StarCount(Vertical): border-bottom: hkey $background; border-top: hkey $background; layout: horizontal; - Layout { - margin-top: 1; - } + Layout { margin-top: 1; } background: $boost; padding: 0 1; color: $warning; - Label { - text-style: bold; - } - LoadingIndicator { - background: transparent !important; - } + Label { text-style: bold; } + LoadingIndicator { background: transparent !important; } } """ stars = reactive(25251, recompose=True) @@ -66,7 +60,6 @@ async def get_stars(self): def on_mount(self) -> None: self.loading = True - self.get_stars() def compose(self) -> ComposeResult: def thousands(number: int) -> str: @@ -81,11 +74,20 @@ def thousands(number: int) -> str: ) yield Label("Forks ") yield Digits(str(self.forks)).with_tooltip(f"{self.forks} Forks") + self.call_later(self.get_stars) + + def on_click(self) -> None: + self.loading = True + self.refresh(recompose=True) + self.notify( + "Refreshing GitHub stats from API", + title="GitHub stats", + ) -class WelcomePage(Page): +class WelcomeScreen(PageScreen): DEFAULT_CSS = """ - WelcomePage { + WelcomeScreen { align: center middle; Digits { width: auto; @@ -113,3 +115,4 @@ def compose(self) -> ComposeResult: yield Label("The [i]lean application[/i] framework") with Center(): yield Markdown(WHAT_IS_TEXTUAL) + yield Footer() diff --git a/src/textual/dom.py b/src/textual/dom.py index 8cc4e4437b..724505826d 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -735,8 +735,13 @@ def screen(self) -> "Screen[object]": from textual.screen import Screen node: MessagePump | None = self - while node is not None and not isinstance(node, Screen): - node = node._parent + try: + while node is not None and not isinstance(node, Screen): + node = node._parent + except AttributeError: + raise RuntimeError( + "Widget is missing attributes; have you called the constructor in your widget class?" + ) from None if not isinstance(node, Screen): raise NoScreen("node has no screen") return node diff --git a/src/textual/widget.py b/src/textual/widget.py index 711aec057d..dc8f7515db 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -340,6 +340,9 @@ class Widget(DOMNode): mouse_hover: Reactive[bool] = Reactive(False, repaint=False) """Is the mouse over this widget? Read only.""" + mouse_hover_within: Reactive[bool] = Reactive(False, repaint=False) + """Is the mouse over this widget or a descendant? Read only.""" + scroll_x: Reactive[float] = Reactive(0.0, repaint=False, layout=False) """The scroll position on the X axis.""" @@ -3259,6 +3262,8 @@ def get_pseudo_classes(self) -> Iterable[str]: app = self.app if self.mouse_hover: yield "hover" + if self.mouse_hover_within: + yield "hover-within" if self.has_focus: yield "focus" else: @@ -3356,6 +3361,9 @@ def post_render(self, renderable: RenderableType) -> ConsoleRenderable: def watch_mouse_hover(self, value: bool) -> None: """Update from CSS if mouse over state changes.""" + for node in self.ancestors_with_self: + if isinstance(node, Widget): + node.mouse_hover_within = value if self._has_hover_style: self._update_styles() From dcea48406749e81129c4df2e68c2f1c67fe516af Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 10 Oct 2024 17:48:27 +0100 Subject: [PATCH 003/104] simplify --- src/textual/demo2/projects.py | 10 ++++++++- src/textual/demo2/welcome.py | 41 +++++++++++++---------------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index 7043a7b5b5..693c0de4f1 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -2,6 +2,7 @@ from textual import events, on from textual.app import ComposeResult +from textual.binding import Binding from textual.containers import Grid, Horizontal, VerticalScroll from textual.demo2.page import PageScreen from textual.widgets import Footer, Label, Static @@ -74,7 +75,14 @@ class Project(VerticalScroll, can_focus=True): } """ - BINDINGS = [("enter", "open_repository", "open repo")] + BINDINGS = [ + Binding( + "enter", + "open_repository", + "open repo", + tooltip="Open the GitHub repository in your browser", + ) + ] def __init__(self, project_info: ProjectInfo) -> None: self.project_info = project_info diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/welcome.py index c4b473f5fb..08116898dc 100644 --- a/src/textual/demo2/welcome.py +++ b/src/textual/demo2/welcome.py @@ -9,7 +9,7 @@ from textual.reactive import reactive from textual.widgets import Digits, Footer, Label, Markdown -WHAT_IS_TEXTUAL = """\ +WHAT_IS_TEXTUAL_MD = """\ ### Turbo charge your developers! * Build sophisticated applications — fast! @@ -22,6 +22,8 @@ class StarCount(Vertical): + """Widget to get and display GitHub star count.""" + DEFAULT_CSS = """ StarCount { dock: top; @@ -29,12 +31,12 @@ class StarCount(Vertical): border-bottom: hkey $background; border-top: hkey $background; layout: horizontal; - Layout { margin-top: 1; } background: $boost; padding: 0 1; color: $warning; Label { text-style: bold; } LoadingIndicator { background: transparent !important; } + Digits { margin-right: 1; } } """ stars = reactive(25251, recompose=True) @@ -42,6 +44,7 @@ class StarCount(Vertical): @work async def get_stars(self): + """Worker to get stars from GitHub API.""" try: async with httpx.AsyncClient() as client: repository_json = ( @@ -55,47 +58,33 @@ async def get_stars(self): title="GitHub stars", severity="error", ) - self.loading = False - def on_mount(self) -> None: - self.loading = True - def compose(self) -> ComposeResult: - def thousands(number: int) -> str: - if number > 2000: - return f"{number / 1000:.1f}K " - return str(number) - with Horizontal(): yield Label("GitHub ★ ") - yield Digits(thousands(self.stars)).with_tooltip( + yield Digits(f"{self.stars / 1000:.1f}K").with_tooltip( f"{self.stars} GitHub stars" ) yield Label("Forks ") yield Digits(str(self.forks)).with_tooltip(f"{self.forks} Forks") + + def update_stars(self) -> None: + self.loading = True self.call_later(self.get_stars) + def on_mount(self) -> None: + self.update_stars() + def on_click(self) -> None: - self.loading = True - self.refresh(recompose=True) - self.notify( - "Refreshing GitHub stats from API", - title="GitHub stats", - ) + self.update_stars() class WelcomeScreen(PageScreen): DEFAULT_CSS = """ WelcomeScreen { align: center middle; - Digits { - width: auto; - - } - Collapsible { - margin: 2 4; - } + Digits { width: auto; } Markdown { background: $boost; margin: 2 2; @@ -114,5 +103,5 @@ def compose(self) -> ComposeResult: with Center(): yield Label("The [i]lean application[/i] framework") with Center(): - yield Markdown(WHAT_IS_TEXTUAL) + yield Markdown(WHAT_IS_TEXTUAL_MD) yield Footer() From f670fbeef4d43dd28e3823410814264901a0637a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 11 Oct 2024 19:36:38 +0100 Subject: [PATCH 004/104] regular grid --- src/textual/_arrange.py | 2 +- src/textual/_resolve.py | 6 ++++++ src/textual/color.py | 2 +- src/textual/containers.py | 33 +++++++++++++++++++++++++++++++++ src/textual/demo2/projects.py | 25 ++++++++++++++----------- src/textual/layouts/grid.py | 25 +++++++++++++++++++++++-- src/textual/widget.py | 18 +++++++++++++----- 7 files changed, 91 insertions(+), 20 deletions(-) diff --git a/src/textual/_arrange.py b/src/textual/_arrange.py index 2f2f7fe60d..570a3edd4e 100644 --- a/src/textual/_arrange.py +++ b/src/textual/_arrange.py @@ -90,7 +90,7 @@ def arrange( if layout_widgets: # Arrange layout widgets (i.e. not docked) - layout_placements = widget._layout.arrange( + layout_placements = widget.layout.arrange( widget, layout_widgets, dock_region.size, diff --git a/src/textual/_resolve.py b/src/textual/_resolve.py index 7fbd4751d6..6319adb9c8 100644 --- a/src/textual/_resolve.py +++ b/src/textual/_resolve.py @@ -21,6 +21,7 @@ def resolve( gutter: int, size: Size, viewport: Size, + min_size: int | None = None, ) -> list[tuple[int, int]]: """Resolve a list of dimensions. @@ -62,6 +63,11 @@ def resolve( "list[Fraction]", [fraction for _, fraction in resolved] ) + if min_size is not None: + resolved_fractions = [ + max(Fraction(min_size), fraction) for fraction in resolved_fractions + ] + fraction_gutter = Fraction(gutter) offsets = [0] + [ int(fraction) diff --git a/src/textual/color.py b/src/textual/color.py index a0c2c1a7aa..23c5a4e6dd 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -332,7 +332,7 @@ def __rich_repr__(self) -> rich.repr.Result: yield g yield b yield "a", a, 1.0 - yield "ansi", ansi + yield "ansi", ansi, None def with_alpha(self, alpha: float) -> Color: """Create a new color with the given alpha. diff --git a/src/textual/containers.py b/src/textual/containers.py index 3c2fc1e467..abc4190096 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -10,6 +10,8 @@ from typing import ClassVar from textual.binding import Binding, BindingType +from textual.layouts.grid import GridLayout +from textual.reactive import reactive from textual.widget import Widget @@ -155,3 +157,34 @@ class Grid(Widget, inherit_bindings=False): layout: grid; } """ + + min_column_width: reactive[int | None] = reactive(None, layout=True) + + def __init__( + self, + *children: Widget, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + disabled: bool = False, + min_column_width: int | None = None, + ) -> None: + """Initialize a Widget. + + Args: + *children: Child widgets. + name: The name of the widget. + id: The ID of the widget in the DOM. + classes: The CSS classes for the widget. + disabled: Whether the widget is disabled or not. + """ + super().__init__( + *children, name=name, id=id, classes=classes, disabled=disabled + ) + self.min_column_width = min_column_width + + def pre_layout(self) -> None: + if isinstance(self.layout, GridLayout): + if self.layout.min_column_width != self.min_column_width: + self.layout.min_column_width = self.min_column_width + self.refresh(layout=True) diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index 693c0de4f1..ea8207140c 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -3,7 +3,7 @@ from textual import events, on from textual.app import ComposeResult from textual.binding import Binding -from textual.containers import Grid, Horizontal, VerticalScroll +from textual.containers import Grid, Horizontal, Vertical from textual.demo2.page import PageScreen from textual.widgets import Footer, Label, Static @@ -44,25 +44,26 @@ def on_click(self) -> None: self.app.open_url(self.url) -class Project(VerticalScroll, can_focus=True): +class Project(Vertical, can_focus=True): ALLOW_MAXIMIZE = True DEFAULT_CSS = """ Project { width: 1fr; - min-height: 8; + height: auto; padding: 0 1; border: tall transparent; - opacity: 0.5; + opacity: 0.8; &:focus-within { border: tall $accent; - background: $boost; + background: $primary 40%; opacity: 1.0; } #title { text-style: bold; } .stars { color: $secondary; text-align: right; + text-style: bold; width: 1fr; } .header { height: 1; } @@ -108,26 +109,28 @@ def action_open_repository(self) -> None: class ProjectsScreen(PageScreen): DEFAULT_CSS = """ ProjectsScreen { - layout: grid; - + margin: 1; Grid { margin: 1 2; padding: 1 2; background: $boost; width: 1fr; - height: 1fr; + height: auto; grid-size: 2; grid-gutter: 1 1; - hatch: right $primary 50%; - keyline:heavy $background; + hatch: right $accent 80%; + keyline:heavy $secondary; } + Placeholder { + height: auto; + } } """ def compose(self) -> ComposeResult: - with Grid(): + with Grid(min_column_width=40): for project in PROJECTS: yield Project(project) yield Footer() diff --git a/src/textual/layouts/grid.py b/src/textual/layouts/grid.py index 506c45e6d2..5351dedb3d 100644 --- a/src/textual/layouts/grid.py +++ b/src/textual/layouts/grid.py @@ -17,9 +17,13 @@ class GridLayout(Layout): name = "grid" + def __init__(self) -> None: + self.min_column_width: int | None = None + def arrange( self, parent: Widget, children: list[Widget], size: Size ) -> ArrangeResult: + parent.pre_layout() styles = parent.styles row_scalars = styles.grid_rows or ( [Scalar.parse("1fr")] if size.height else [Scalar.parse("auto")] @@ -27,10 +31,21 @@ def arrange( column_scalars = styles.grid_columns or [Scalar.parse("1fr")] gutter_horizontal = styles.grid_gutter_horizontal gutter_vertical = styles.grid_gutter_vertical + table_size_columns = max(1, styles.grid_size_columns) + min_column_width = self.min_column_width + if min_column_width is not None: + container_width = size.width + table_size_columns = max( + 1, + (container_width + gutter_horizontal) + // (min_column_width + gutter_horizontal), + ) + + table_size_columns = min(table_size_columns, len(children)) table_size_rows = styles.grid_size_rows viewport = parent.screen.size - keyline_style, keyline_color = styles.keyline + keyline_style, _keyline_color = styles.keyline offset = (0, 0) gutter_spacing: Spacing | None if keyline_style == "none": @@ -199,7 +214,13 @@ def apply_height_limits(widget: Widget, height: int) -> int: ) column_scalars[column] = Scalar.from_number(width) - columns = resolve(column_scalars, size.width, gutter_vertical, size, viewport) + columns = resolve( + column_scalars, + size.width, + gutter_vertical, + size, + viewport, + ) # Handle any auto rows for row, scalar in enumerate(row_scalars): diff --git a/src/textual/widget.py b/src/textual/widget.py index dc8f7515db..eaddfaf645 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1417,7 +1417,7 @@ def get_content_width(self, container: Size, viewport: Size) -> int: """ if self.is_container: - width = self._layout.get_content_width(self, container, viewport) + width = self.layout.get_content_width(self, container, viewport) return width cache_key = container.width @@ -1451,8 +1451,8 @@ def get_content_height(self, container: Size, viewport: Size, width: int) -> int The height of the content. """ if self.is_container: - assert self._layout is not None - height = self._layout.get_content_height( + assert self.layout is not None + height = self.layout.get_content_height( self, container, viewport, @@ -2035,7 +2035,7 @@ async def stop_animation(self, attribute: str, complete: bool = True) -> None: await self.app.animator.stop_animation(self, attribute, complete) @property - def _layout(self) -> Layout: + def layout(self) -> Layout: """Get the layout object if set in styles, or a default layout. Returns: @@ -2246,6 +2246,14 @@ def _scroll_to( return scrolled_x or scrolled_y + def pre_layout(self) -> None: + """This method id called prior to a layout operation. + + Implement this method if you want to make updates that should impact + the layout. + + """ + def scroll_to( self, x: float | None = None, @@ -3683,7 +3691,7 @@ def render(self) -> RenderableType: if self.is_container: if self.styles.layout and self.styles.keyline[0] != "none": - return self._layout.render_keyline(self) + return self.layout.render_keyline(self) else: return Blank(self.background_colors[1]) return self.css_identifier_styled From e3665569ce40afe24d61bcd3832bb84573f83c16 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 12 Oct 2024 15:07:00 +0100 Subject: [PATCH 005/104] grid height --- CHANGELOG.md | 6 ++++++ src/textual/css/constants.py | 1 - src/textual/layouts/grid.py | 2 ++ src/textual/widget.py | 5 ----- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2471d5ed43..40e3455332 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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/). +## Unreleased + +### Changed + +- Grid will now size children to the maximum height of a row + ## [0.83.0] - 2024-10-10 ### Added diff --git a/src/textual/css/constants.py b/src/textual/css/constants.py index 9189efa0ea..026e78be45 100644 --- a/src/textual/css/constants.py +++ b/src/textual/css/constants.py @@ -69,7 +69,6 @@ "focus-within", "focus", "hover", - "hover-within", "inline", "light", "nocolor", diff --git a/src/textual/layouts/grid.py b/src/textual/layouts/grid.py index 5351dedb3d..07d7fff45e 100644 --- a/src/textual/layouts/grid.py +++ b/src/textual/layouts/grid.py @@ -272,6 +272,8 @@ def apply_height_limits(widget: Widget, height: int) -> int: Fraction(cell_size.width), Fraction(cell_size.height), ) + if column_span == 1 and row_span == 1: + height = cell_height region = ( Region(x, y, int(width + margin.width), int(height + margin.height)) .crop_size(cell_size) diff --git a/src/textual/widget.py b/src/textual/widget.py index eaddfaf645..0d13c6fcac 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -340,9 +340,6 @@ class Widget(DOMNode): mouse_hover: Reactive[bool] = Reactive(False, repaint=False) """Is the mouse over this widget? Read only.""" - mouse_hover_within: Reactive[bool] = Reactive(False, repaint=False) - """Is the mouse over this widget or a descendant? Read only.""" - scroll_x: Reactive[float] = Reactive(0.0, repaint=False, layout=False) """The scroll position on the X axis.""" @@ -3270,8 +3267,6 @@ def get_pseudo_classes(self) -> Iterable[str]: app = self.app if self.mouse_hover: yield "hover" - if self.mouse_hover_within: - yield "hover-within" if self.has_focus: yield "focus" else: From 7451a4d08f956d7a95b483a2448a9624d42f1d22 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 12 Oct 2024 17:14:12 +0100 Subject: [PATCH 006/104] more projects --- src/textual/app.py | 3 +- src/textual/demo2/projects.py | 100 ++++++++++++++++++++++++++++++++-- src/textual/layouts/grid.py | 8 ++- src/textual/widget.py | 3 - 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index 0ac65b9fbf..5d60a2eed1 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -371,8 +371,7 @@ class App(Generic[ReturnType], DOMNode): overflow-y: auto !important; align: center middle; .-maximized { - dock: initial !important; - margin: 2 4; + dock: initial !important; } } /* Fade the header title when app is blurred */ diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index ea8207140c..ebfe84f6b3 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -11,17 +11,101 @@ @dataclass class ProjectInfo: title: str + author: str url: str description: str + stars: str PROJECTS = [ ProjectInfo( "Posting", + "Darren Burns", "https://posting.sh/", - "Posting is a beautiful open-source terminal app for developing and testing APIs.", - ) -] * 5 + """A powerful HTTP client that lives in your terminal. + +Posting is an HTTP client, not unlike Postman and Insomnia. As a TUI application, it can be used over SSH and enables efficient keyboard-centric workflows. """, + "4.7k", + ), + ProjectInfo( + "Memray", + "Bloomberg", + "https://github.com/bloomberg/memray", + """Memray is a memory profiler for Python. It can track memory allocations in Python code, in native extension modules, and in the Python interpreter itself. It can generate several different types of reports to help you analyze the captured memory usage data.""", + "13.2k", + ), + ProjectInfo( + "Toolong", + "Will McGugan", + "https://github.com/Textualize/toolong", + """A terminal application to view, tail, merge, and search log files (plus JSONL).""", + "3.1k", + ), + ProjectInfo( + "Dolphie", + "Charles Thompson", + "https://github.com/charles-001/dolphie", + "Your single pane of glass for real-time analytics into MySQL/MariaDB & ProxySQL", + "608", + ), + ProjectInfo( + "Harlequin", + "Ted Conbeer", + "https://harlequin.sh/", + """Portable, powerful, colorful. +An easy, fast, and beautiful database client for the terminal.""", + "3.7k", + ), + ProjectInfo( + "Elia", + "Darren Burns", + "https://github.com/darrenburns/elia", + """A snappy, keyboard-centric terminal user interface for interacting with large language models. +Chat with Claude 3, ChatGPT, and local models like Llama 3, Phi 3, Mistral and Gemma.""", + "1.8k", + ), + ProjectInfo( + "Trogon", + "Textualize", + "https://github.com/Textualize/trogon", + """Auto-generate friendly terminal user interfaces for command line apps. + +Trogon works with the popular Click library for Python, but will support other libraries and languages in the future.""", + "2.5k", + ), + ProjectInfo( + "TFTUI - The Terraform textual UI", + "Ido Avraham", + "https://github.com/idoavrah/terraform-tui", + "TFTUI is a powerful textual UI that empowers users to effortlessly view and interact with their Terraform state.", + "1k", + ), + ProjectInfo( + "RecoverPy", + "Pablo Lecolinet", + "https://github.com/PabloLec/RecoverPy", + """RecoverPy is a powerful tool that leverages your system capabilities to recover lost files. + +Unlike others, you can not only recover deleted files but also overwritten data.""", + "1.3k", + ), + ProjectInfo( + "Frogmouth", + "Dave Pearson", + "https://github.com/Textualize/frogmouth", + """Frogmouth is a Markdown viewer / browser for your terminal, built with Textual. + +Frogmouth can open *.md files locally or via a URL. There is a familiar browser-like navigation stack, history, bookmarks, and table of contents.""", + "2.5k", + ), + ProjectInfo( + "oterm", + "Yiorgis Gozadinos", + "https://github.com/ggozad/oterm", + "The text-based terminal client for Ollama.", + "1k", + ), +] class Link(Label): @@ -53,6 +137,7 @@ class Project(Vertical, can_focus=True): padding: 0 1; border: tall transparent; opacity: 0.8; + box-sizing: border-box; &:focus-within { border: tall $accent; @@ -60,6 +145,7 @@ class Project(Vertical, can_focus=True): opacity: 1.0; } #title { text-style: bold; } + #author { text-style: italic; } .stars { color: $secondary; text-align: right; @@ -92,7 +178,8 @@ def __init__(self, project_info: ProjectInfo) -> None: def compose(self) -> ComposeResult: with Horizontal(classes="header"): yield Label(self.project_info.title, id="title") - yield Label("★ 23K", classes="stars") + yield Label(f"★ {self.project_info.stars}", classes="stars") + yield Label(self.project_info.author, id="author") yield Link(self.project_info.url) yield Static(self.project_info.description, classes="description") @@ -110,15 +197,16 @@ class ProjectsScreen(PageScreen): DEFAULT_CSS = """ ProjectsScreen { - margin: 1; + # margin: 1; Grid { margin: 1 2; padding: 1 2; background: $boost; width: 1fr; height: auto; - grid-size: 2; + # grid-size: 2; grid-gutter: 1 1; + grid-rows: auto; hatch: right $accent 80%; keyline:heavy $secondary; diff --git a/src/textual/layouts/grid.py b/src/textual/layouts/grid.py index 07d7fff45e..df46f88de9 100644 --- a/src/textual/layouts/grid.py +++ b/src/textual/layouts/grid.py @@ -41,8 +41,8 @@ def arrange( (container_width + gutter_horizontal) // (min_column_width + gutter_horizontal), ) + table_size_columns = min(table_size_columns, len(children)) - table_size_columns = min(table_size_columns, len(children)) table_size_rows = styles.grid_size_rows viewport = parent.screen.size keyline_style, _keyline_color = styles.keyline @@ -272,8 +272,10 @@ def apply_height_limits(widget: Widget, height: int) -> int: Fraction(cell_size.width), Fraction(cell_size.height), ) - if column_span == 1 and row_span == 1: - height = cell_height + if len(children) > 1: + height = ( + height if height > cell_size.height else Fraction(cell_size.height) + ) region = ( Region(x, y, int(width + margin.width), int(height + margin.height)) .crop_size(cell_size) diff --git a/src/textual/widget.py b/src/textual/widget.py index 0d13c6fcac..34bad6fa96 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -3364,9 +3364,6 @@ def post_render(self, renderable: RenderableType) -> ConsoleRenderable: def watch_mouse_hover(self, value: bool) -> None: """Update from CSS if mouse over state changes.""" - for node in self.ancestors_with_self: - if isinstance(node, Widget): - node.mouse_hover_within = value if self._has_hover_style: self._update_styles() From ca4af0c41f0c8dce0ea09aa3d3dbed63d38e64e4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 12 Oct 2024 18:44:24 +0100 Subject: [PATCH 007/104] more projects --- src/textual/demo2/projects.py | 60 +++++++++++++++++------------------ 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index ebfe84f6b3..6f88ccfb01 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -22,16 +22,14 @@ class ProjectInfo: "Posting", "Darren Burns", "https://posting.sh/", - """A powerful HTTP client that lives in your terminal. - -Posting is an HTTP client, not unlike Postman and Insomnia. As a TUI application, it can be used over SSH and enables efficient keyboard-centric workflows. """, + """Posting is an HTTP client, not unlike Postman and Insomnia. As a TUI application, it can be used over SSH and enables efficient keyboard-centric workflows. """, "4.7k", ), ProjectInfo( "Memray", "Bloomberg", "https://github.com/bloomberg/memray", - """Memray is a memory profiler for Python. It can track memory allocations in Python code, in native extension modules, and in the Python interpreter itself. It can generate several different types of reports to help you analyze the captured memory usage data.""", + """Memray is a memory profiler for Python. It can track memory allocations in Python code, in native extension modules, and in the Python interpreter itself.""", "13.2k", ), ProjectInfo( @@ -52,8 +50,7 @@ class ProjectInfo: "Harlequin", "Ted Conbeer", "https://harlequin.sh/", - """Portable, powerful, colorful. -An easy, fast, and beautiful database client for the terminal.""", + """Portable, powerful, colorful. An easy, fast, and beautiful database client for the terminal.""", "3.7k", ), ProjectInfo( @@ -68,9 +65,7 @@ class ProjectInfo: "Trogon", "Textualize", "https://github.com/Textualize/trogon", - """Auto-generate friendly terminal user interfaces for command line apps. - -Trogon works with the popular Click library for Python, but will support other libraries and languages in the future.""", + "Auto-generate friendly terminal user interfaces for command line apps.", "2.5k", ), ProjectInfo( @@ -84,18 +79,14 @@ class ProjectInfo: "RecoverPy", "Pablo Lecolinet", "https://github.com/PabloLec/RecoverPy", - """RecoverPy is a powerful tool that leverages your system capabilities to recover lost files. - -Unlike others, you can not only recover deleted files but also overwritten data.""", + """RecoverPy is a powerful tool that leverages your system capabilities to recover lost files.""", "1.3k", ), ProjectInfo( "Frogmouth", "Dave Pearson", "https://github.com/Textualize/frogmouth", - """Frogmouth is a Markdown viewer / browser for your terminal, built with Textual. - -Frogmouth can open *.md files locally or via a URL. There is a familiar browser-like navigation stack, history, bookmarks, and table of contents.""", + """Frogmouth is a Markdown viewer / browser for your terminal, built with Textual.""", "2.5k", ), ProjectInfo( @@ -105,6 +96,20 @@ class ProjectInfo: "The text-based terminal client for Ollama.", "1k", ), + ProjectInfo( + "logmerger", + "Paul McGuire", + "https://github.com/ptmcg/logmerger", + "logmerger is a TUI for viewing a merged display of multiple log files, merged by timestamp.", + "162", + ), + ProjectInfo( + "doit", + "Murli Tawari", + "https://github.com/kraanzu/dooit", + "A todo manager that you didn't ask for, but needed!", + "2.1k", + ), ] @@ -138,7 +143,6 @@ class Project(Vertical, can_focus=True): border: tall transparent; opacity: 0.8; box-sizing: border-box; - &:focus-within { border: tall $accent; background: $primary 40%; @@ -176,12 +180,13 @@ def __init__(self, project_info: ProjectInfo) -> None: super().__init__() def compose(self) -> ComposeResult: + info = self.project_info with Horizontal(classes="header"): - yield Label(self.project_info.title, id="title") - yield Label(f"★ {self.project_info.stars}", classes="stars") - yield Label(self.project_info.author, id="author") - yield Link(self.project_info.url) - yield Static(self.project_info.description, classes="description") + yield Label(info.title, id="title") + yield Label(f"★ {info.stars}", classes="stars") + yield Label(info.author, id="author") + yield Link(info.url) + yield Static(info.description, classes="description") @on(events.Enter) @on(events.Leave) @@ -195,24 +200,17 @@ def action_open_repository(self) -> None: class ProjectsScreen(PageScreen): DEFAULT_CSS = """ - ProjectsScreen { - - # margin: 1; + ProjectsScreen { Grid { margin: 1 2; padding: 1 2; background: $boost; width: 1fr; - height: auto; - # grid-size: 2; + height: auto; grid-gutter: 1 1; grid-rows: auto; hatch: right $accent 80%; - keyline:heavy $secondary; - - } - Placeholder { - height: auto; + keyline:heavy $secondary; } } """ From 6298acb2a27a3ec0080d8c636c5e3a14baae06b2 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 13 Oct 2024 18:44:33 +0100 Subject: [PATCH 008/104] link widget --- CHANGELOG.md | 5 ++ docs/api/layout.md | 6 +++ docs/examples/widgets/link.py | 23 +++++++++ docs/widget_gallery.md | 7 +++ docs/widgets/link.md | 61 +++++++++++++++++++++++ mkdocs-nav.yml | 2 + src/textual/_arrange.py | 2 +- src/textual/containers.py | 27 ++++++++--- src/textual/css/_style_properties.py | 2 +- src/textual/css/styles.py | 2 +- src/textual/demo2/projects.py | 34 +++---------- src/textual/{_layout.py => layout.py} | 4 +- src/textual/layouts/factory.py | 2 +- src/textual/layouts/grid.py | 11 +++-- src/textual/layouts/horizontal.py | 2 +- src/textual/layouts/vertical.py | 2 +- src/textual/screen.py | 4 +- src/textual/widget.py | 7 ++- src/textual/widgets/__init__.py | 2 + src/textual/widgets/__init__.pyi | 1 + src/textual/widgets/_link.py | 70 +++++++++++++++++++++++++++ tests/test_arrange.py | 2 +- 22 files changed, 229 insertions(+), 49 deletions(-) create mode 100644 docs/api/layout.md create mode 100644 docs/examples/widgets/link.py create mode 100644 docs/widgets/link.md rename src/textual/{_layout.py => layout.py} (97%) create mode 100644 src/textual/widgets/_link.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 40e3455332..387dbcc765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Grid will now size children to the maximum height of a row + +### Added + +- Added Link widget + ## [0.83.0] - 2024-10-10 ### Added diff --git a/docs/api/layout.md b/docs/api/layout.md new file mode 100644 index 0000000000..06fba9113c --- /dev/null +++ b/docs/api/layout.md @@ -0,0 +1,6 @@ +--- +title: "textual.layout" +--- + + +::: textual.layout diff --git a/docs/examples/widgets/link.py b/docs/examples/widgets/link.py new file mode 100644 index 0000000000..d4d9885622 --- /dev/null +++ b/docs/examples/widgets/link.py @@ -0,0 +1,23 @@ +from textual.app import App, ComposeResult +from textual.widgets import Link + + +class LabelApp(App): + AUTO_FOCUS = None + CSS = """ + Screen { + align: center middle; + } + """ + + def compose(self) -> ComposeResult: + yield Link( + "Go to textualize.io", + url="https://textualize.io", + tooltip="Click me", + ) + + +if __name__ == "__main__": + app = LabelApp() + app.run() diff --git a/docs/widget_gallery.md b/docs/widget_gallery.md index 62d6df383f..f1d26d69fa 100644 --- a/docs/widget_gallery.md +++ b/docs/widget_gallery.md @@ -121,6 +121,13 @@ A simple text label. [Label reference](./widgets/label.md){ .md-button .md-button--primary } +## Link + +A clickable link that opens a URL. + +[Link reference](./widgets/link.md){ .md-button .md-button--primary } + + ## ListView Display a list of items (items may be other widgets). diff --git a/docs/widgets/link.md b/docs/widgets/link.md new file mode 100644 index 0000000000..a719962eec --- /dev/null +++ b/docs/widgets/link.md @@ -0,0 +1,61 @@ +# Link + +!!! tip "Added in version 0.84.0" + +A widget to display a piece of text that opens a URL when clicked, like a web browser link. + +- [x] Focusable +- [ ] Container + + +## Example + +A trivial app with a link. +Clicking the link open's a web-browser—as you might expect! + +=== "Output" + + ```{.textual path="docs/examples/widgets/link.py"} + ``` + +=== "link.py" + + ```python + --8<-- "docs/examples/widgets/link.py" + ``` + + +## Reactive Attributes + +| Name | Type | Default | Description | +| ------ | ----- | ------- | ----------------------------------------- | +| `text` | `str` | `""` | The text of the link. | +| `url` | `str` | `""` | The URL to open when the link is clicked. | + + +## Messages + +This widget sends no messages. + +## Bindings + +The Link widget defines the following bindings: + +::: textual.widgets.Link.BINDINGS + options: + show_root_heading: false + show_root_toc_entry: false + + +## Component classes + +This widget contains no component classes. + + + +--- + + +::: textual.widgets.Link + options: + heading_level: 2 diff --git a/mkdocs-nav.yml b/mkdocs-nav.yml index 53b75f0391..3e5b44f966 100644 --- a/mkdocs-nav.yml +++ b/mkdocs-nav.yml @@ -151,6 +151,7 @@ nav: - "widgets/index.md" - "widgets/input.md" - "widgets/label.md" + - "widgets/link.md" - "widgets/list_item.md" - "widgets/list_view.md" - "widgets/loading_indicator.md" @@ -194,6 +195,7 @@ nav: - "api/filter.md" - "api/fuzzy_matcher.md" - "api/geometry.md" + - "api/layout.md" - "api/lazy.md" - "api/logger.md" - "api/logging.md" diff --git a/src/textual/_arrange.py b/src/textual/_arrange.py index 570a3edd4e..347e8ce09e 100644 --- a/src/textual/_arrange.py +++ b/src/textual/_arrange.py @@ -5,9 +5,9 @@ from operator import attrgetter from typing import TYPE_CHECKING, Iterable, Mapping, Sequence -from textual._layout import DockArrangeResult, WidgetPlacement from textual._partition import partition from textual.geometry import Region, Size, Spacing +from textual.layout import DockArrangeResult, WidgetPlacement if TYPE_CHECKING: from textual.widget import Widget diff --git a/src/textual/containers.py b/src/textual/containers.py index abc4190096..c10e25d275 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -10,6 +10,7 @@ from typing import ClassVar from textual.binding import Binding, BindingType +from textual.layout import Layout from textual.layouts.grid import GridLayout from textual.reactive import reactive from textual.widget import Widget @@ -158,6 +159,19 @@ class Grid(Widget, inherit_bindings=False): } """ + +class ItemGrid(Widget, inherit_bindings=False): + """A container with grid layout.""" + + DEFAULT_CSS = """ + ItemGrid { + width: 1fr; + height: 1fr; + layout: grid; + } + """ + + stretch_height: reactive[bool] = reactive(True) min_column_width: reactive[int | None] = reactive(None, layout=True) def __init__( @@ -168,6 +182,7 @@ def __init__( classes: str | None = None, disabled: bool = False, min_column_width: int | None = None, + stretch_height: bool = True, ) -> None: """Initialize a Widget. @@ -181,10 +196,10 @@ def __init__( super().__init__( *children, name=name, id=id, classes=classes, disabled=disabled ) - self.min_column_width = min_column_width + self.set_reactive(ItemGrid.stretch_height, stretch_height) + self.set_reactive(ItemGrid.min_column_width, min_column_width) - def pre_layout(self) -> None: - if isinstance(self.layout, GridLayout): - if self.layout.min_column_width != self.min_column_width: - self.layout.min_column_width = self.min_column_width - self.refresh(layout=True) + def pre_layout(self, layout: Layout) -> None: + if isinstance(layout, GridLayout): + layout.stretch_height = self.stretch_height + layout.min_column_width = self.min_column_width diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py index e8989847eb..3fb548de8a 100644 --- a/src/textual/css/_style_properties.py +++ b/src/textual/css/_style_properties.py @@ -57,7 +57,7 @@ if TYPE_CHECKING: from textual.canvas import CanvasLineType - from textual._layout import Layout + from textual.layout import Layout from textual.css.styles import StylesBase from textual.css.types import AlignHorizontal, AlignVertical, DockEdge, EdgeType diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 4e00d74d23..4315c0e2a3 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -69,9 +69,9 @@ from textual.geometry import Offset, Spacing if TYPE_CHECKING: - from textual._layout import Layout from textual.css.types import CSSLocation from textual.dom import DOMNode + from textual.layout import Layout class RulesMap(TypedDict, total=False): diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index 6f88ccfb01..60c8ed2f81 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -3,9 +3,9 @@ from textual import events, on from textual.app import ComposeResult from textual.binding import Binding -from textual.containers import Grid, Horizontal, Vertical +from textual.containers import Horizontal, ItemGrid, Vertical from textual.demo2.page import PageScreen -from textual.widgets import Footer, Label, Static +from textual.widgets import Footer, Label, Link, Static @dataclass @@ -113,27 +113,7 @@ class ProjectInfo: ] -class Link(Label): - """The link in the Project widget.""" - - DEFAULT_CSS = """ - Link { - color: $accent; - text-style: underline; - &:hover { text-style: reverse;} - } - """ - - def __init__(self, url: str) -> None: - super().__init__(url) - self.url = url - self.tooltip = "Click to open Repository URL" - - def on_click(self) -> None: - self.app.open_url(self.url) - - -class Project(Vertical, can_focus=True): +class Project(Vertical, can_focus=True, can_focus_children=False): ALLOW_MAXIMIZE = True DEFAULT_CSS = """ Project { @@ -143,7 +123,7 @@ class Project(Vertical, can_focus=True): border: tall transparent; opacity: 0.8; box-sizing: border-box; - &:focus-within { + &:focus { border: tall $accent; background: $primary 40%; opacity: 1.0; @@ -185,7 +165,7 @@ def compose(self) -> ComposeResult: yield Label(info.title, id="title") yield Label(f"★ {info.stars}", classes="stars") yield Label(info.author, id="author") - yield Link(info.url) + yield Link(info.url, tooltip="Click to open project repository") yield Static(info.description, classes="description") @on(events.Enter) @@ -201,7 +181,7 @@ def action_open_repository(self) -> None: class ProjectsScreen(PageScreen): DEFAULT_CSS = """ ProjectsScreen { - Grid { + ItemGrid { margin: 1 2; padding: 1 2; background: $boost; @@ -216,7 +196,7 @@ class ProjectsScreen(PageScreen): """ def compose(self) -> ComposeResult: - with Grid(min_column_width=40): + with ItemGrid(min_column_width=40): for project in PROJECTS: yield Project(project) yield Footer() diff --git a/src/textual/_layout.py b/src/textual/layout.py similarity index 97% rename from src/textual/_layout.py rename to src/textual/layout.py index 9d261129e1..717197e4d2 100644 --- a/src/textual/_layout.py +++ b/src/textual/layout.py @@ -19,6 +19,8 @@ @dataclass class DockArrangeResult: + """Result of [Layout.arrange][textual.layout.Layout.arrange].""" + placements: list[WidgetPlacement] """A `WidgetPlacement` for every widget to describe its location on screen.""" widgets: set[Widget] @@ -125,7 +127,7 @@ def get_bounds(cls, placements: Iterable[WidgetPlacement]) -> Region: class Layout(ABC): - """Responsible for arranging Widgets in a view and rendering them.""" + """Base class of the object responsible for arranging Widgets within a container.""" name: ClassVar[str] = "" diff --git a/src/textual/layouts/factory.py b/src/textual/layouts/factory.py index e6b0cfb2e1..8667363737 100644 --- a/src/textual/layouts/factory.py +++ b/src/textual/layouts/factory.py @@ -1,6 +1,6 @@ from __future__ import annotations -from textual._layout import Layout +from textual.layout import Layout from textual.layouts.grid import GridLayout from textual.layouts.horizontal import HorizontalLayout from textual.layouts.vertical import VerticalLayout diff --git a/src/textual/layouts/grid.py b/src/textual/layouts/grid.py index df46f88de9..310c8b3128 100644 --- a/src/textual/layouts/grid.py +++ b/src/textual/layouts/grid.py @@ -3,10 +3,10 @@ from fractions import Fraction from typing import TYPE_CHECKING, Iterable -from textual._layout import ArrangeResult, Layout, WidgetPlacement from textual._resolve import resolve from textual.css.scalar import Scalar from textual.geometry import Region, Size, Spacing +from textual.layout import ArrangeResult, Layout, WidgetPlacement if TYPE_CHECKING: from textual.widget import Widget @@ -19,11 +19,12 @@ class GridLayout(Layout): def __init__(self) -> None: self.min_column_width: int | None = None + self.stretch_height: bool = False def arrange( self, parent: Widget, children: list[Widget], size: Size ) -> ArrangeResult: - parent.pre_layout() + parent.pre_layout(self) styles = parent.styles row_scalars = styles.grid_rows or ( [Scalar.parse("1fr")] if size.height else [Scalar.parse("auto")] @@ -272,9 +273,11 @@ def apply_height_limits(widget: Widget, height: int) -> int: Fraction(cell_size.width), Fraction(cell_size.height), ) - if len(children) > 1: + if self.stretch_height and len(children) > 1: height = ( - height if height > cell_size.height else Fraction(cell_size.height) + height + if (height > cell_size.height) + else Fraction(cell_size.height) ) region = ( Region(x, y, int(width + margin.width), int(height + margin.height)) diff --git a/src/textual/layouts/horizontal.py b/src/textual/layouts/horizontal.py index 3ac0d3ca0c..ce032d27a5 100644 --- a/src/textual/layouts/horizontal.py +++ b/src/textual/layouts/horizontal.py @@ -3,9 +3,9 @@ from fractions import Fraction from typing import TYPE_CHECKING -from textual._layout import ArrangeResult, Layout, WidgetPlacement from textual._resolve import resolve_box_models from textual.geometry import Region, Size +from textual.layout import ArrangeResult, Layout, WidgetPlacement if TYPE_CHECKING: from textual.geometry import Spacing diff --git a/src/textual/layouts/vertical.py b/src/textual/layouts/vertical.py index 31c04977a7..c98b0aa35d 100644 --- a/src/textual/layouts/vertical.py +++ b/src/textual/layouts/vertical.py @@ -3,9 +3,9 @@ from fractions import Fraction from typing import TYPE_CHECKING -from textual._layout import ArrangeResult, Layout, WidgetPlacement from textual._resolve import resolve_box_models from textual.geometry import Region, Size +from textual.layout import ArrangeResult, Layout, WidgetPlacement if TYPE_CHECKING: from textual.geometry import Spacing diff --git a/src/textual/screen.py b/src/textual/screen.py index 1f1c3b1d22..ac452ba03f 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -34,7 +34,6 @@ from textual._callback import invoke from textual._compositor import Compositor, MapGeometry from textual._context import active_message_pump, visible_screen_stack -from textual._layout import DockArrangeResult from textual._path import ( CSSPathType, _css_path_type_as_list, @@ -50,6 +49,7 @@ from textual.errors import NoWidget from textual.geometry import Offset, Region, Size from textual.keys import key_to_character +from textual.layout import DockArrangeResult from textual.reactive import Reactive from textual.renderables.background_screen import BackgroundScreen from textual.renderables.blank import Blank @@ -1205,8 +1205,8 @@ def _get_inline_height(self, size: Size) -> int: def _screen_resized(self, size: Size): """Called by App when the screen is resized.""" + self._compositor_refresh() self._refresh_layout(size) - self.refresh() def _on_screen_resume(self) -> None: """Screen has resumed.""" diff --git a/src/textual/widget.py b/src/textual/widget.py index 34bad6fa96..f9bca1871a 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -52,7 +52,6 @@ from textual._debug import get_caller_file_and_line from textual._dispatch_key import dispatch_key from textual._easing import DEFAULT_SCROLL_EASING -from textual._layout import Layout from textual._segment_tools import align_lines from textual._styles_cache import StylesCache from textual._types import AnimationLevel @@ -76,6 +75,7 @@ Spacing, clamp, ) +from textual.layout import Layout from textual.layouts.vertical import VerticalLayout from textual.message import Message from textual.messages import CallbackType, Prune @@ -2243,12 +2243,15 @@ def _scroll_to( return scrolled_x or scrolled_y - def pre_layout(self) -> None: + def pre_layout(self, layout: Layout) -> None: """This method id called prior to a layout operation. Implement this method if you want to make updates that should impact the layout. + Args: + layout: The [Layout][textual.layout.Layout] instance that will be used to arrange this widget's children. + """ def scroll_to( diff --git a/src/textual/widgets/__init__.py b/src/textual/widgets/__init__.py index bcaac621c0..ffc861dad1 100644 --- a/src/textual/widgets/__init__.py +++ b/src/textual/widgets/__init__.py @@ -23,6 +23,7 @@ from textual.widgets._input import Input from textual.widgets._key_panel import KeyPanel from textual.widgets._label import Label + from textual.widgets._link import Link from textual.widgets._list_item import ListItem from textual.widgets._list_view import ListView from textual.widgets._loading_indicator import LoadingIndicator @@ -63,6 +64,7 @@ "Input", "KeyPanel", "Label", + "Link", "ListItem", "ListView", "LoadingIndicator", diff --git a/src/textual/widgets/__init__.pyi b/src/textual/widgets/__init__.pyi index b9df9b9195..141c1df46a 100644 --- a/src/textual/widgets/__init__.pyi +++ b/src/textual/widgets/__init__.pyi @@ -12,6 +12,7 @@ from ._help_panel import HelpPanel as HelpPanel from ._input import Input as Input from ._key_panel import KeyPanel as KeyPanel from ._label import Label as Label +from ._link import Link as Link from ._list_item import ListItem as ListItem from ._list_view import ListView as ListView from ._loading_indicator import LoadingIndicator as LoadingIndicator diff --git a/src/textual/widgets/_link.py b/src/textual/widgets/_link.py new file mode 100644 index 0000000000..a7fa41f7e6 --- /dev/null +++ b/src/textual/widgets/_link.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from textual.binding import Binding +from textual.reactive import reactive +from textual.widgets import Static + + +class Link(Static, can_focus=True): + """A simple, clickable link that opens a URL.""" + + DEFAULT_CSS = """ + Link { + width: auto; + height: auto; + min-height: 1; + color: $accent; + text-style: underline; + &:hover { color: $accent-lighten-1; } + &:focus { text-style: bold reverse; } + } + """ + + BINDINGS = [Binding("enter", "select", "Open link")] + """ + | Key(s) | Description | + | :- | :- | + | enter | Open the link in the browser. | + """ + + text: reactive[str] = reactive("", layout=True) + url: reactive[str] = reactive("") + + def __init__( + self, + text: str, + *, + url: str | None = None, + tooltip: str | None = None, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + disabled: bool = False, + ) -> None: + """A link widget. + + Args: + text: Text of the link. + url: A URL to open, when clicked. If `None`, the `text` parameter will also be used as the url. + tooltip: Optional tooltip. + name: Name of widget. + id: ID of Widget. + classes: Space separated list of class names. + disabled: Whether the static is disabled or not. + """ + super().__init__( + text, name=name, id=id, classes=classes, disabled=disabled, markup=False + ) + self.set_reactive(Link.text, text) + self.set_reactive(Link.url, text if url is None else url) + self.tooltip = tooltip + + def watch_text(self, text: str) -> None: + self.update(text) + + def on_click(self) -> None: + self.action_open_link() + + def action_open_link(self) -> None: + if self.url: + self.app.open_url(self.url) diff --git a/tests/test_arrange.py b/tests/test_arrange.py index ffedb6d787..4b671b8334 100644 --- a/tests/test_arrange.py +++ b/tests/test_arrange.py @@ -1,9 +1,9 @@ import pytest from textual._arrange import TOP_Z, arrange -from textual._layout import WidgetPlacement from textual.app import App from textual.geometry import Region, Size, Spacing +from textual.layout import WidgetPlacement from textual.widget import Widget From 5bf30fec7e11d625d560d26a465170dcac0c75c8 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 13 Oct 2024 19:14:50 +0100 Subject: [PATCH 009/104] added text column --- src/textual/demo2/projects.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index 60c8ed2f81..266b8cb15b 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -3,13 +3,15 @@ from textual import events, on from textual.app import ComposeResult from textual.binding import Binding -from textual.containers import Horizontal, ItemGrid, Vertical +from textual.containers import Center, Horizontal, ItemGrid, Vertical, VerticalScroll from textual.demo2.page import PageScreen -from textual.widgets import Footer, Label, Link, Static +from textual.widgets import Footer, Label, Link, Markdown, Static @dataclass class ProjectInfo: + """Dataclass for storing project information.""" + title: str author: str url: str @@ -17,6 +19,15 @@ class ProjectInfo: stars: str +PROJECTS_MD = """\ +# Projects + +There are many amazing Open Source Textual apps available for download. +And many more still in development. + +See below for a small selection! +""" + PROJECTS = [ ProjectInfo( "Posting", @@ -179,8 +190,10 @@ def action_open_repository(self) -> None: class ProjectsScreen(PageScreen): + AUTO_FOCUS = None DEFAULT_CSS = """ - ProjectsScreen { + ProjectsScreen { + align-horizontal: center; ItemGrid { margin: 1 2; padding: 1 2; @@ -192,11 +205,17 @@ class ProjectsScreen(PageScreen): hatch: right $accent 80%; keyline:heavy $secondary; } + Markdown { + max-width: 80; + } } """ def compose(self) -> ComposeResult: - with ItemGrid(min_column_width=40): - for project in PROJECTS: - yield Project(project) + with VerticalScroll(): + with Center(): + yield Markdown(PROJECTS_MD) + with ItemGrid(min_column_width=40): + for project in PROJECTS: + yield Project(project) yield Footer() From dd980b825a43770288834e474dea82d26e8f9fd9 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 14 Oct 2024 12:40:11 +0100 Subject: [PATCH 010/104] fix keyline color --- src/textual/containers.py | 12 ++++++++++++ src/textual/demo2/projects.py | 13 +++++-------- src/textual/demo2/welcome.py | 36 +++++++++++++++++++++-------------- src/textual/layout.py | 2 ++ 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/textual/containers.py b/src/textual/containers.py index c10e25d275..2ff19ac3d0 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -136,6 +136,18 @@ class Center(Widget, inherit_bindings=False): """ +class Right(Widget, inherit_bindings=False): + """A container which aligns children on the X axis.""" + + DEFAULT_CSS = """ + Right { + align-horizontal: right; + width: 1fr; + height: auto; + } + """ + + class Middle(Widget, inherit_bindings=False): """A container which aligns children on the Y axis.""" diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index 266b8cb15b..f39877f865 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -137,7 +137,7 @@ class Project(Vertical, can_focus=True, can_focus_children=False): &:focus { border: tall $accent; background: $primary 40%; - opacity: 1.0; + opacity: 1.0; } #title { text-style: bold; } #author { text-style: italic; } @@ -195,19 +195,16 @@ class ProjectsScreen(PageScreen): ProjectsScreen { align-horizontal: center; ItemGrid { - margin: 1 2; + margin: 2 4; padding: 1 2; background: $boost; width: 1fr; height: auto; grid-gutter: 1 1; - grid-rows: auto; - hatch: right $accent 80%; - keyline:heavy $secondary; - } - Markdown { - max-width: 80; + grid-rows: auto; + keyline:thin $foreground 50%; } + Markdown { max-width: 80; } } """ diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/welcome.py index 08116898dc..0730927ca8 100644 --- a/src/textual/demo2/welcome.py +++ b/src/textual/demo2/welcome.py @@ -10,13 +10,18 @@ from textual.widgets import Digits, Footer, Label, Markdown WHAT_IS_TEXTUAL_MD = """\ -### Turbo charge your developers! +# What is Textual? + +**The fastest way to build sophisticated data applications that run *anywhere*.** + +Textual apps can run on virtually anything from a $5 single-board computer upwards. +Deploy as a terminal application, over SSH, or serve as a web application [web application](https://github.com/Textualize/textual-web). + + * Build sophisticated applications — fast! * No front-end skills required. * Elegant Python API from the developer of [Rich](https://github.com/Textualize/rich). - - * Deploy Textual as a terminal application, over SSH, *or* a [web application](https://github.com/Textualize/textual-web)! """ @@ -37,6 +42,9 @@ class StarCount(Vertical): Label { text-style: bold; } LoadingIndicator { background: transparent !important; } Digits { margin-right: 1; } + Label { margin-right: 1; } + #stars { align: center middle; } + #forks { align: right middle; } } """ stars = reactive(25251, recompose=True) @@ -61,19 +69,24 @@ async def get_stars(self): self.loading = False def compose(self) -> ComposeResult: - with Horizontal(): - yield Label("GitHub ★ ") + with Horizontal(id="version"): + yield Label("Version") + yield Digits(version("textual")) + with Horizontal(id="stars"): + yield Label("GitHub ★") yield Digits(f"{self.stars / 1000:.1f}K").with_tooltip( f"{self.stars} GitHub stars" ) - yield Label("Forks ") + with Horizontal(id="forks"): + yield Label("Forks") yield Digits(str(self.forks)).with_tooltip(f"{self.forks} Forks") def update_stars(self) -> None: self.loading = True - self.call_later(self.get_stars) + self.get_stars() def on_mount(self) -> None: + self.tooltip = "Click to refresh" self.update_stars() def on_click(self) -> None: @@ -86,7 +99,7 @@ class WelcomeScreen(PageScreen): align: center middle; Digits { width: auto; } Markdown { - background: $boost; + # background: $boost; margin: 2 2; padding: 1 2 0 2; max-width: 80; @@ -96,12 +109,7 @@ class WelcomeScreen(PageScreen): def compose(self) -> ComposeResult: yield StarCount() - with Center(): - yield Label("Textual") - with Center(): - yield Digits(version("textual")) - with Center(): - yield Label("The [i]lean application[/i] framework") + with Center(): yield Markdown(WHAT_IS_TEXTUAL_MD) yield Footer() diff --git a/src/textual/layout.py b/src/textual/layout.py index 717197e4d2..adb7ab5c2d 100644 --- a/src/textual/layout.py +++ b/src/textual/layout.py @@ -214,6 +214,8 @@ def render_keyline(self, container: Widget) -> StripRenderable: canvas = Canvas(width, height) line_style, keyline_color = container.styles.keyline + if keyline_color: + keyline_color = container.background_colors[0] + keyline_color container_offset = container.content_region.offset From 1e2064a4755cf0d51a13441472d5758a546c965c Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 14 Oct 2024 13:30:35 +0100 Subject: [PATCH 011/104] simplify --- src/textual/demo2/welcome.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/welcome.py index 0730927ca8..739eb7d47d 100644 --- a/src/textual/demo2/welcome.py +++ b/src/textual/demo2/welcome.py @@ -53,6 +53,7 @@ class StarCount(Vertical): @work async def get_stars(self): """Worker to get stars from GitHub API.""" + self.loading = True try: async with httpx.AsyncClient() as client: repository_json = ( @@ -74,23 +75,18 @@ def compose(self) -> ComposeResult: yield Digits(version("textual")) with Horizontal(id="stars"): yield Label("GitHub ★") - yield Digits(f"{self.stars / 1000:.1f}K").with_tooltip( - f"{self.stars} GitHub stars" - ) + stars = f"{self.stars / 1000:.1f}K" + yield Digits(stars).with_tooltip(f"{self.stars} GitHub stars") with Horizontal(id="forks"): yield Label("Forks") yield Digits(str(self.forks)).with_tooltip(f"{self.forks} Forks") - def update_stars(self) -> None: - self.loading = True - self.get_stars() - def on_mount(self) -> None: self.tooltip = "Click to refresh" - self.update_stars() + self.get_stars() def on_click(self) -> None: - self.update_stars() + self.get_stars() class WelcomeScreen(PageScreen): @@ -109,7 +105,6 @@ class WelcomeScreen(PageScreen): def compose(self) -> ComposeResult: yield StarCount() - with Center(): yield Markdown(WHAT_IS_TEXTUAL_MD) yield Footer() From b36ee03dc9b8afbc8ca0e3f4f730d44130231f46 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 14 Oct 2024 16:57:46 +0100 Subject: [PATCH 012/104] fix fr resolve --- src/textual/_resolve.py | 15 ++++++++--- tests/snapshot_tests/test_snapshots.py | 35 +++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/textual/_resolve.py b/src/textual/_resolve.py index 7fbd4751d6..e6d7d25b1b 100644 --- a/src/textual/_resolve.py +++ b/src/textual/_resolve.py @@ -196,10 +196,14 @@ def resolve_box_models( Returns: List of resolved box models. """ + + margins = [widget.styles.margin for widget in widgets] + margin_width, margin_height = margin - fraction_width = Fraction(max(0, size.width - margin_width)) - fraction_height = Fraction(max(0, size.height - margin_height)) + fraction_width = Fraction(size.width) + fraction_height = Fraction(size.height) + fraction_zero = Fraction(0) margin_size = size - margin @@ -209,10 +213,13 @@ def resolve_box_models( None if _dimension is not None and _dimension.is_fraction else widget._get_box_model( - size, viewport_size, fraction_width, fraction_height + size, + viewport_size, + max(fraction_zero, fraction_width - margin.width), + max(fraction_zero, fraction_height - margin.height), ) ) - for (_dimension, widget) in zip(dimensions, widgets) + for (_dimension, widget, margin) in zip(dimensions, widgets, margins) ] if None not in box_models: diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index dca33964cf..cd53ab7b31 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -8,7 +8,7 @@ from textual import events, on from textual.app import App, ComposeResult from textual.binding import Binding, Keymap -from textual.containers import Center, Grid, Middle, Vertical, VerticalScroll +from textual.containers import Center, Container, Grid, Middle, Vertical, VerticalScroll from textual.pilot import Pilot from textual.renderables.gradient import LinearGradient from textual.screen import ModalScreen, Screen @@ -2336,3 +2336,36 @@ def compose(self) -> ComposeResult: yield Label("100%") assert snap_compare(BackgroundTintApp()) + + +def test_fr_and_margin(snap_compare): + class FRApp(App): + CSS = """ + #first-container { + background: green; + height: auto; + } + + #second-container { + margin: 2; + background: red; + height: auto; + } + + #third-container { + margin: 4; + background: blue; + } + """ + + def compose(self) -> ComposeResult: + with Container(id="first-container"): + yield Label("No margin - should extend to left and right") + + with Container(id="second-container"): + yield Label("A margin of 2, should be 2 cells around the text") + + with Center(id="third-container"): + yield Label("A margin of 4, should be 4 cells around the text") + + assert snap_compare(FRApp()) From c1673389244e9f6f6d3cbc17f27d33a81041a5ba Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 14 Oct 2024 17:00:10 +0100 Subject: [PATCH 013/104] snapshot --- tests/snapshot_tests/test_snapshots.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index cd53ab7b31..a8a35dfeb0 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2339,6 +2339,10 @@ def compose(self) -> ComposeResult: def test_fr_and_margin(snap_compare): + """Regression test for https://github.com/Textualize/textual/issues/5116""" + + # Check margins can be independently applied to widgets with fr unites + class FRApp(App): CSS = """ #first-container { From f6f695facc2bb097a062d56dc651797763d8b1ba Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 14 Oct 2024 17:02:29 +0100 Subject: [PATCH 014/104] snapshot --- .../test_snapshots/test_fr_and_margin.svg | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 tests/snapshot_tests/__snapshots__/test_snapshots/test_fr_and_margin.svg diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_fr_and_margin.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_fr_and_margin.svg new file mode 100644 index 0000000000..f7ec88b079 --- /dev/null +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_fr_and_margin.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FRApp + + + + + + + + + + No margin - should extend to left and right                                      + + +A margin of 2, should be 2 cells around the text                             + + + + +            A margin of 4, should be 4 cells around the text             + + + + + + + + + + + + + + + + + + From 41441df0db2a10732334f162c5cdaf1ff2716d05 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 14 Oct 2024 17:15:30 +0100 Subject: [PATCH 015/104] simplify --- src/textual/_resolve.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/textual/_resolve.py b/src/textual/_resolve.py index e6d7d25b1b..1bd05ae1fb 100644 --- a/src/textual/_resolve.py +++ b/src/textual/_resolve.py @@ -197,14 +197,10 @@ def resolve_box_models( List of resolved box models. """ - margins = [widget.styles.margin for widget in widgets] - margin_width, margin_height = margin - fraction_width = Fraction(size.width) fraction_height = Fraction(size.height) fraction_zero = Fraction(0) - margin_size = size - margin # Fixed box models @@ -215,11 +211,11 @@ def resolve_box_models( else widget._get_box_model( size, viewport_size, - max(fraction_zero, fraction_width - margin.width), - max(fraction_zero, fraction_height - margin.height), + max(fraction_zero, fraction_width - widget.styles.margin.width), + max(fraction_zero, fraction_height - widget.styles.margin.height), ) ) - for (_dimension, widget, margin) in zip(dimensions, widgets, margins) + for (_dimension, widget) in zip(dimensions, widgets) ] if None not in box_models: From bcaad58926a3dbb594b62d7db3c7018a55b1686e Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 14 Oct 2024 17:19:52 +0100 Subject: [PATCH 016/104] simpler test --- .../test_snapshots/test_fr_and_margin.svg | 114 +++++++++--------- tests/snapshot_tests/test_snapshots.py | 3 +- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_fr_and_margin.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_fr_and_margin.svg index f7ec88b079..82abecfc79 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_fr_and_margin.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_fr_and_margin.svg @@ -19,134 +19,134 @@ font-weight: 700; } - .terminal-3157579860-matrix { + .terminal-952358996-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3157579860-title { + .terminal-952358996-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3157579860-r1 { fill: #ddeedd } -.terminal-3157579860-r2 { fill: #c5c8c6 } -.terminal-3157579860-r3 { fill: #e1e1e1 } -.terminal-3157579860-r4 { fill: #ffdddd } -.terminal-3157579860-r5 { fill: #ddddff } + .terminal-952358996-r1 { fill: #ddeedd } +.terminal-952358996-r2 { fill: #c5c8c6 } +.terminal-952358996-r3 { fill: #e1e1e1 } +.terminal-952358996-r4 { fill: #ffdddd } +.terminal-952358996-r5 { fill: #ddddff } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - FRApp + FRApp - + - - No margin - should extend to left and right                                      - - -A margin of 2, should be 2 cells around the text                             - - - - -            A margin of 4, should be 4 cells around the text             - - - - - - - - - - - - - - + + No margin - should extend to left and right                                      + + +A margin of 2, should be 2 cells around the text                             + + + + +A margin of 4, should be 4 cells around the text                         + + + + + + + + + + + + + + diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index a8a35dfeb0..8ec68c0414 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2359,6 +2359,7 @@ class FRApp(App): #third-container { margin: 4; background: blue; + height: auto; } """ @@ -2369,7 +2370,7 @@ def compose(self) -> ComposeResult: with Container(id="second-container"): yield Label("A margin of 2, should be 2 cells around the text") - with Center(id="third-container"): + with Container(id="third-container"): yield Label("A margin of 4, should be 4 cells around the text") assert snap_compare(FRApp()) From 11122f9a8cde2c21914dc4c69e1958c23a778cb0 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Mon, 14 Oct 2024 17:35:34 +0100 Subject: [PATCH 017/104] optimize --- src/textual/_resolve.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/textual/_resolve.py b/src/textual/_resolve.py index 1bd05ae1fb..dce4663281 100644 --- a/src/textual/_resolve.py +++ b/src/textual/_resolve.py @@ -203,6 +203,8 @@ def resolve_box_models( fraction_zero = Fraction(0) margin_size = size - margin + margins = [widget.styles.margin.totals for widget in widgets] + # Fixed box models box_models: list[BoxModel | None] = [ ( @@ -211,11 +213,13 @@ def resolve_box_models( else widget._get_box_model( size, viewport_size, - max(fraction_zero, fraction_width - widget.styles.margin.width), - max(fraction_zero, fraction_height - widget.styles.margin.height), + max(fraction_zero, fraction_width - margin_width), + max(fraction_zero, fraction_height - margin_height), ) ) - for (_dimension, widget) in zip(dimensions, widgets) + for (_dimension, widget, (margin_width, margin_height)) in zip( + dimensions, widgets, margins + ) ] if None not in box_models: From bd4688f19dc42b3d22096bbcba615c480e78ab27 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 15 Oct 2024 14:16:35 +0100 Subject: [PATCH 018/104] demo update --- src/textual/demo2/page.py | 6 +- src/textual/demo2/welcome.py | 147 ++++++++++++++++++++++------ src/textual/widgets/_collapsible.py | 2 + src/textual/widgets/_markdown.py | 4 +- 4 files changed, 124 insertions(+), 35 deletions(-) diff --git a/src/textual/demo2/page.py b/src/textual/demo2/page.py index b3463b0a27..f8f85a71f5 100644 --- a/src/textual/demo2/page.py +++ b/src/textual/demo2/page.py @@ -45,7 +45,7 @@ def on_mount(self): code_widget.border_subtitle = "Escape to close" -class PageScreen(Screen): +class PageScreen(Screen, inherit_bindings=True): DEFAULT_CSS = """ Page { width: 100%; @@ -59,7 +59,9 @@ class PageScreen(Screen): "show_code", "show code", tooltip="Show the code used to generate this screen", - ) + ), + Binding("tab", "app.focus_next", "focus next"), + Binding("shift+tab", "app.focus_previous", "focus previous"), ] @work(thread=True) diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/welcome.py index 739eb7d47d..c97c1db16e 100644 --- a/src/textual/demo2/welcome.py +++ b/src/textual/demo2/welcome.py @@ -4,25 +4,83 @@ from textual import work from textual.app import ComposeResult -from textual.containers import Center, Horizontal, Vertical +from textual.containers import Horizontal, Vertical, VerticalScroll from textual.demo2.page import PageScreen from textual.reactive import reactive -from textual.widgets import Digits, Footer, Label, Markdown +from textual.widgets import Collapsible, Digits, Footer, Label, Markdown WHAT_IS_TEXTUAL_MD = """\ # What is Textual? -**The fastest way to build sophisticated data applications that run *anywhere*.** +**The fastest way to build applications that run *anywhere*.** -Textual apps can run on virtually anything from a $5 single-board computer upwards. -Deploy as a terminal application, over SSH, or serve as a web application [web application](https://github.com/Textualize/textual-web). +Textual apps run on virtually any device from a $5 single-board computer upwards. +Deploy as a terminal application, over SSH, or serve as a [web application](https://github.com/Textualize/textual-web). +""" + + +ABOUT_MD = """\ +The retro look is not just an aesthetic choice! Textual apps have some unique properties that make them preferable for many tasks. + +## Textual interfaces are *snappy* +Even the most modern of web apps can leave the user waiting hundreds of milliseconds or more for a response. +Given their low graphical requirements, Textual interfaces can be far more responsive—no waiting required. +## Reward repeated use +Use the mouse to explore, but Textual apps are keyboard-centric and reward repeated use. +An experience user can operate a Textual app far faster than their web / GUI counterparts. +## Command palette +A builtin command palette with fuzzy searching, puts powerful commands at your fingertips. + +**Try it:** Press `ctrl+p` now. -* Build sophisticated applications — fast! -* No front-end skills required. -* Elegant Python API from the developer of [Rich](https://github.com/Textualize/rich). -* Deploy Textual as a terminal application, over SSH, *or* a [web application](https://github.com/Textualize/textual-web)! +""" + +API_MD = """\ +A modern Python API from the developer of [Rich](https://github.com/Textualize/rich). + +**Hint:** press `C` to view the code for this page. + +## Re-usable components + +Message passing allows widgets to be entirely self contained. + +## Builtin widgets + +A large [library of builtin widgets](https://textual.textualize.io/widget_gallery/), and a growing ecosystem of third party widgets on pyPi +(this content is generated by the builtin [Markdown](https://textual.textualize.io/widget_gallery/#markdown) widget). + + +## Reactive variables + +[Reactivity](https://textual.textualize.io/guide/reactivity/) using Python idioms, keeps your logic separate from display code. + +## Async support + +Built on asyncio, you can easily integrate async libraries while keeping your UI responsive. + +## Concurrency + +Textual's [Workers](https://textual.textualize.io/guide/workers/) provide a far-less error prone interface to +concurrency: both async and threads. +""" + +DEPLOY_MD = """\ +There are a number of ways to deploy and share Textual apps. + +## As a Python library + +Textual apps make be pip installed, via tools such as `pipx` or `uvx`, and other package manager. + +## As a web application + +It takes two lines of code to [serve your Textual app](https://github.com/Textualize/textual-serve) as web application. + +## Managed web application + +With [Textual web](https://github.com/Textualize/textual-serve) you can serve multiple Textual apps on the web, +with zero configuration. Even behind a firewall. """ @@ -41,10 +99,13 @@ class StarCount(Vertical): color: $warning; Label { text-style: bold; } LoadingIndicator { background: transparent !important; } - Digits { margin-right: 1; } + Digits { width: auto; margin-right: 1; } Label { margin-right: 1; } - #stars { align: center middle; } - #forks { align: right middle; } + #stars { align: center top; } + #forks { align: right top; } + align: center top; + &>Horizontal { max-width: 100;} + } """ stars = reactive(25251, recompose=True) @@ -70,16 +131,17 @@ async def get_stars(self): self.loading = False def compose(self) -> ComposeResult: - with Horizontal(id="version"): - yield Label("Version") - yield Digits(version("textual")) - with Horizontal(id="stars"): - yield Label("GitHub ★") - stars = f"{self.stars / 1000:.1f}K" - yield Digits(stars).with_tooltip(f"{self.stars} GitHub stars") - with Horizontal(id="forks"): - yield Label("Forks") - yield Digits(str(self.forks)).with_tooltip(f"{self.forks} Forks") + with Horizontal(): + with Horizontal(id="version"): + yield Label("Version") + yield Digits(version("textual")) + with Horizontal(id="stars"): + yield Label("GitHub ★") + stars = f"{self.stars / 1000:.1f}K" + yield Digits(stars).with_tooltip(f"{self.stars} GitHub stars") + with Horizontal(id="forks"): + yield Label("Forks") + yield Digits(str(self.forks)).with_tooltip(f"{self.forks} Forks") def on_mount(self) -> None: self.tooltip = "Click to refresh" @@ -89,22 +151,47 @@ def on_click(self) -> None: self.get_stars() +class Content(VerticalScroll, can_focus=False): + """Non focusable vertical scroll.""" + + class WelcomeScreen(PageScreen): DEFAULT_CSS = """ WelcomeScreen { - align: center middle; - Digits { width: auto; } - Markdown { - # background: $boost; - margin: 2 2; - padding: 1 2 0 2; - max-width: 80; + align-horizontal: center; + Content { + max-width: 100; + overflow-y: auto; + height: 1fr; + scrollbar-gutter: stable; + MarkdownFence { + height: auto; + max-height: initial; + } + + Collapsible { + padding-right: 0; + &.-collapsed { + padding-bottom: 1; + } + } + Markdown { + margin-right: 1; + padding-right: 1; + } } + } """ def compose(self) -> ComposeResult: yield StarCount() - with Center(): + with Content(): yield Markdown(WHAT_IS_TEXTUAL_MD) + with Collapsible(title="Textual Interfaces"): + yield Markdown(ABOUT_MD) + with Collapsible(title="Textual API"): + yield Markdown(API_MD) + with Collapsible(title="Deploying Textual apps"): + yield Markdown(DEPLOY_MD) yield Footer() diff --git a/src/textual/widgets/_collapsible.py b/src/textual/widgets/_collapsible.py index d7b7359f65..0cb791a7a7 100644 --- a/src/textual/widgets/_collapsible.py +++ b/src/textual/widgets/_collapsible.py @@ -92,6 +92,7 @@ def _watch_collapsed(self, collapsed: bool) -> None: class Collapsible(Widget): """A collapsible container.""" + ALLOW_MAXIMIZE = True collapsed = reactive(True, init=False) title = reactive("Toggle") @@ -202,6 +203,7 @@ def _watch_collapsed(self, collapsed: bool) -> None: self.post_message(self.Collapsed(self)) else: self.post_message(self.Expanded(self)) + self.call_later(self.scroll_visible) def _update_collapsed(self, collapsed: bool) -> None: """Update children to match collapsed state.""" diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index 3e72e4edb3..1e31f5ff30 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -421,7 +421,7 @@ class MarkdownOrderedList(MarkdownList): MarkdownOrderedList Vertical { height: auto; - width: 1fr; + width: 1fr; } """ @@ -606,8 +606,6 @@ class MarkdownFence(MarkdownBlock): height: auto; max-height: 20; color: rgb(210,210,210); - - } MarkdownFence > * { From 901db1247a65ebcedafbedd76c83f1e3177cf644 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 15 Oct 2024 16:00:55 +0100 Subject: [PATCH 019/104] Added open links --- CHANGELOG.md | 3 ++- src/textual/demo2/welcome.py | 42 ++++++++++++++++++++------------ src/textual/widgets/_markdown.py | 14 ++++++++++- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 387dbcc765..9620cc02d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Grid will now size children to the maximum height of a row - +- Markdown links will be opened with `App.open_url` automatically ### Added - Added Link widget +- Added `open_links` to `Markdown` and `MarkdownViewer` widgets ## [0.83.0] - 2024-10-10 diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/welcome.py index c97c1db16e..a00a2a36c0 100644 --- a/src/textual/demo2/welcome.py +++ b/src/textual/demo2/welcome.py @@ -33,25 +33,33 @@ ## Command palette A builtin command palette with fuzzy searching, puts powerful commands at your fingertips. -**Try it:** Press `ctrl+p` now. +**Try it:** Press **ctrl+p** now. """ API_MD = """\ A modern Python API from the developer of [Rich](https://github.com/Textualize/rich). -**Hint:** press `C` to view the code for this page. +Well documented, typed, and intuitive. +Textual's API is accessible to Python developers of all skill levels. -## Re-usable components +**Hint:** press **C** to view the code for this page. -Message passing allows widgets to be entirely self contained. +## Built on Rich + +With over 1.4 *billion* downloads, Rich is the most popular terminal library out there. +Textual builds on Rich to add interactivity, and is compatible with Rich renderables. + +## Re-usable widgets + +Textual's widgets are self-contained and re-usable across projects. +Virtually all aspects of a widget's look and feel can be customized to your requirements. ## Builtin widgets A large [library of builtin widgets](https://textual.textualize.io/widget_gallery/), and a growing ecosystem of third party widgets on pyPi (this content is generated by the builtin [Markdown](https://textual.textualize.io/widget_gallery/#markdown) widget). - ## Reactive variables [Reactivity](https://textual.textualize.io/guide/reactivity/) using Python idioms, keeps your logic separate from display code. @@ -64,6 +72,15 @@ Textual's [Workers](https://textual.textualize.io/guide/workers/) provide a far-less error prone interface to concurrency: both async and threads. + +## Testing + +With a comprehensive [testing framework](https://textual.textualize.io/guide/testing/), you can release reliable software, that can be maintained indefinitely. + +## Docs + +Textual has [amazing docs](https://textual.textualize.io/)! + """ DEPLOY_MD = """\ @@ -71,7 +88,7 @@ ## As a Python library -Textual apps make be pip installed, via tools such as `pipx` or `uvx`, and other package manager. +Textual apps make be pip installed, via tools such as `pipx` or `uvx`, and other package managers. ## As a web application @@ -97,15 +114,14 @@ class StarCount(Vertical): background: $boost; padding: 0 1; color: $warning; + #stars { align: center top; } + #forks { align: right top; } Label { text-style: bold; } LoadingIndicator { background: transparent !important; } Digits { width: auto; margin-right: 1; } Label { margin-right: 1; } - #stars { align: center top; } - #forks { align: right top; } align: center top; - &>Horizontal { max-width: 100;} - + &>Horizontal { max-width: 100;} } """ stars = reactive(25251, recompose=True) @@ -168,19 +184,15 @@ class WelcomeScreen(PageScreen): height: auto; max-height: initial; } - Collapsible { padding-right: 0; - &.-collapsed { - padding-bottom: 1; - } + &.-collapsed { padding-bottom: 1; } } Markdown { margin-right: 1; padding-right: 1; } } - } """ diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index 1e31f5ff30..8d0813cfd6 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -718,6 +718,7 @@ def __init__( id: str | None = None, classes: str | None = None, parser_factory: Callable[[], MarkdownIt] | None = None, + open_links: bool = True, ): """A Markdown widget. @@ -727,11 +728,13 @@ def __init__( id: The ID of the widget in the DOM. classes: The CSS classes of the widget. parser_factory: A factory function to return a configured MarkdownIt instance. If `None`, a "gfm-like" parser is used. + open_links: Open links automatically. If you set this to `False`, you can handle the [`LinkClicked`][textual.widgets.markdown.Markdown.LinkClicked] events. """ super().__init__(name=name, id=id, classes=classes) self._markdown = markdown self._parser_factory = parser_factory self._table_of_contents: TableOfContentsType | None = None + self._open_links = open_links class TableOfContentsUpdated(Message): """The table of contents was updated.""" @@ -796,6 +799,10 @@ async def _on_mount(self, _: Mount) -> None: if self._markdown is not None: await self.update(self._markdown) + def on_markdown_link_clicked(self, event: LinkClicked) -> None: + if self._open_links: + self.app.open_url(event.href) + def _watch_code_dark_theme(self) -> None: """React to the dark theme being changed.""" if self.app.dark: @@ -1152,6 +1159,7 @@ def __init__( id: str | None = None, classes: str | None = None, parser_factory: Callable[[], MarkdownIt] | None = None, + open_links: bool = True, ): """Create a Markdown Viewer object. @@ -1162,11 +1170,13 @@ def __init__( id: The ID of the widget in the DOM. classes: The CSS classes of the widget. parser_factory: A factory function to return a configured MarkdownIt instance. If `None`, a "gfm-like" parser is used. + open_links: Open links automatically. If you set this to `False`, you can handle the [`LinkClicked`][textual.widgets.markdown.Markdown.LinkClicked] events. """ super().__init__(name=name, id=id, classes=classes) self.show_table_of_contents = show_table_of_contents self._markdown = markdown self._parser_factory = parser_factory + self._open_links = open_links @property def document(self) -> Markdown: @@ -1213,7 +1223,9 @@ def watch_show_table_of_contents(self, show_table_of_contents: bool) -> None: self.set_class(show_table_of_contents, "-show-table-of-contents") def compose(self) -> ComposeResult: - markdown = Markdown(parser_factory=self._parser_factory) + markdown = Markdown( + parser_factory=self._parser_factory, open_links=self._open_links + ) yield MarkdownTableOfContents(markdown) yield markdown From f446576d465136a8cd56795d92356de807530480 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 15 Oct 2024 18:05:11 +0100 Subject: [PATCH 020/104] Added welcome text --- src/textual/demo2/page.py | 6 ++---- src/textual/demo2/welcome.py | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/textual/demo2/page.py b/src/textual/demo2/page.py index f8f85a71f5..b3463b0a27 100644 --- a/src/textual/demo2/page.py +++ b/src/textual/demo2/page.py @@ -45,7 +45,7 @@ def on_mount(self): code_widget.border_subtitle = "Escape to close" -class PageScreen(Screen, inherit_bindings=True): +class PageScreen(Screen): DEFAULT_CSS = """ Page { width: 100%; @@ -59,9 +59,7 @@ class PageScreen(Screen, inherit_bindings=True): "show_code", "show code", tooltip="Show the code used to generate this screen", - ), - Binding("tab", "app.focus_next", "focus next"), - Binding("shift+tab", "app.focus_previous", "focus previous"), + ) ] @work(thread=True) diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/welcome.py index a00a2a36c0..e2d0fca456 100644 --- a/src/textual/demo2/welcome.py +++ b/src/textual/demo2/welcome.py @@ -18,6 +18,20 @@ Deploy as a terminal application, over SSH, or serve as a [web application](https://github.com/Textualize/textual-web). """ +WELCOME_MD = """\ +## Welcome keyboard warriors! + +This is a Textual app. Here's what you need to know: + +* **enter** toggles this collapsible widget +* **tab** to focus the next widget +* **shift+tab** to focus the previous widget + +👇 Familiarize yourself with the footer below. + +`Or… click away with the mouse (no judgement).` + +""" ABOUT_MD = """\ The retro look is not just an aesthetic choice! Textual apps have some unique properties that make them preferable for many tasks. @@ -107,7 +121,7 @@ class StarCount(Vertical): DEFAULT_CSS = """ StarCount { dock: top; - height: 5; + height: 6; border-bottom: hkey $background; border-top: hkey $background; layout: horizontal; @@ -148,14 +162,14 @@ async def get_stars(self): def compose(self) -> ComposeResult: with Horizontal(): - with Horizontal(id="version"): + with Vertical(id="version"): yield Label("Version") yield Digits(version("textual")) - with Horizontal(id="stars"): + with Vertical(id="stars"): yield Label("GitHub ★") stars = f"{self.stars / 1000:.1f}K" yield Digits(stars).with_tooltip(f"{self.stars} GitHub stars") - with Horizontal(id="forks"): + with Vertical(id="forks"): yield Label("Forks") yield Digits(str(self.forks)).with_tooltip(f"{self.forks} Forks") @@ -200,6 +214,8 @@ def compose(self) -> ComposeResult: yield StarCount() with Content(): yield Markdown(WHAT_IS_TEXTUAL_MD) + with Collapsible(title="Welcome", collapsed=False): + yield Markdown(WELCOME_MD) with Collapsible(title="Textual Interfaces"): yield Markdown(ABOUT_MD) with Collapsible(title="Textual API"): From 9410b8326a72352317d0dd21734b596f76f6f06b Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 15 Oct 2024 22:00:51 +0100 Subject: [PATCH 021/104] Home screen --- CHANGELOG.md | 1 + src/textual/app.py | 29 ++++++++++++++++++----- src/textual/demo2/demo_app.py | 21 ++++++++-------- src/textual/demo2/{welcome.py => home.py} | 9 +++---- 4 files changed, 40 insertions(+), 20 deletions(-) rename src/textual/demo2/{welcome.py => home.py} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9620cc02d7..ca8f6f1155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added Link widget - Added `open_links` to `Markdown` and `MarkdownViewer` widgets +- Added `App.DEFAULT_MODE` ## [0.83.0] - 2024-10-10 diff --git a/src/textual/app.py b/src/textual/app.py index 5d60a2eed1..a46ce669e5 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -2176,8 +2176,12 @@ def _init_mode(self, mode: str) -> AwaitMount: stack = self._screen_stacks.get(mode, []) if stack: - await_mount = AwaitMount(stack[0], []) - else: + # Mode already exists + # Return an dummy await + return AwaitMount(stack[0], []) + + if mode in self._modes: + # Mode is defined in MODES _screen = self._modes[mode] if isinstance(_screen, Screen): raise TypeError( @@ -2188,6 +2192,17 @@ def _init_mode(self, mode: str) -> AwaitMount: screen, await_mount = self._get_screen(new_screen) stack.append(screen) self._load_screen_css(screen) + self.refresh_css() + screen.post_message(events.ScreenResume()) + else: + # Mode is not defined + screen = self.get_default_screen() + stack.append(screen) + self._register(self, screen) + screen.post_message(events.ScreenResume()) + await_mount = AwaitMount(stack[0], []) + + screen._screen_resized(self.size) self._screen_stacks[mode] = stack return await_mount @@ -2204,7 +2219,12 @@ def switch_mode(self, mode: str) -> AwaitMount: Raises: UnknownModeError: If trying to switch to an unknown mode. + """ + + if mode == self._current_mode: + return AwaitMount(self.screen, []) + if mode not in self._modes: raise UnknownModeError(f"No known mode {mode!r}") @@ -3506,10 +3526,7 @@ async def on_event(self, event: events.Event) -> None: # Handle input events that haven't been forwarded # If the event has been forwarded it may have bubbled up back to the App if isinstance(event, events.Compose): - screen: Screen[Any] = self.get_default_screen() - self._register(self, screen) - self._screen_stack.append(screen) - screen.post_message(events.ScreenResume()) + await self._init_mode(self._current_mode) await super().on_event(event) elif isinstance(event, events.InputEvent) and not event.is_forwarded: diff --git a/src/textual/demo2/demo_app.py b/src/textual/demo2/demo_app.py index 09f31e3cf8..ea9ec1d364 100644 --- a/src/textual/demo2/demo_app.py +++ b/src/textual/demo2/demo_app.py @@ -1,19 +1,23 @@ from textual.app import App from textual.binding import Binding +from textual.demo2.home import HomeScreen from textual.demo2.projects import ProjectsScreen -from textual.demo2.welcome import WelcomeScreen class DemoApp(App): - MODES = {"welcome": WelcomeScreen, "projects": ProjectsScreen} - # DEFAULT_MODE = "welcome" + """The demo app defines the modes and sets a few bindings.""" + MODES = { + "home": HomeScreen, + "projects": ProjectsScreen, + } + DEFAULT_MODE = "home" BINDINGS = [ Binding( - "w", - "app.switch_mode('welcome')", - "welcome", - tooltip="Show the welcome screen", + "h", + "app.switch_mode('home')", + "home", + tooltip="Show the home screen", ), Binding( "p", @@ -22,6 +26,3 @@ class DemoApp(App): tooltip="A selection of Textual projects", ), ] - - def on_mount(self) -> None: - self.switch_mode("welcome") diff --git a/src/textual/demo2/welcome.py b/src/textual/demo2/home.py similarity index 97% rename from src/textual/demo2/welcome.py rename to src/textual/demo2/home.py index e2d0fca456..a9f0dd543b 100644 --- a/src/textual/demo2/welcome.py +++ b/src/textual/demo2/home.py @@ -26,8 +26,9 @@ * **enter** toggles this collapsible widget * **tab** to focus the next widget * **shift+tab** to focus the previous widget +* **ctrl+p** to summon the command palette -👇 Familiarize yourself with the footer below. +👇 Also see the footer below. `Or… click away with the mouse (no judgement).` @@ -154,7 +155,7 @@ async def get_stars(self): self.forks = repository_json["forks"] except Exception: self.notify( - "Unable to get star count (maybe rate-limited)", + "Unable to update star count (maybe rate-limited)", title="GitHub stars", severity="error", ) @@ -185,9 +186,9 @@ class Content(VerticalScroll, can_focus=False): """Non focusable vertical scroll.""" -class WelcomeScreen(PageScreen): +class HomeScreen(PageScreen): DEFAULT_CSS = """ - WelcomeScreen { + HomeScreen { align-horizontal: center; Content { max-width: 100; From a56225ac47e4612df16013d423053673f35988d6 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 15 Oct 2024 22:05:10 +0100 Subject: [PATCH 022/104] formatting --- src/textual/demo2/home.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/textual/demo2/home.py b/src/textual/demo2/home.py index a9f0dd543b..053880f3e6 100644 --- a/src/textual/demo2/home.py +++ b/src/textual/demo2/home.py @@ -23,10 +23,11 @@ This is a Textual app. Here's what you need to know: -* **enter** toggles this collapsible widget -* **tab** to focus the next widget -* **shift+tab** to focus the previous widget -* **ctrl+p** to summon the command palette +* **enter** `toggle this collapsible widget` +* **tab** `focus the next widget` +* **shift+tab** `focus the previous widget` +* **ctrl+p** `summon the command palette` + 👇 Also see the footer below. From c40c265c12a94fa156d63aa0a4493addd322405e Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Oct 2024 16:41:34 +0100 Subject: [PATCH 023/104] tint fix --- src/textual/color.py | 25 +++++++++++++++++++++++++ src/textual/dom.py | 12 ++++++------ tests/snapshot_tests/test_snapshots.py | 22 +++++++++++++++++----- tests/test_color.py | 24 ++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/textual/color.py b/src/textual/color.py index a0c2c1a7aa..ef159ce698 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -399,6 +399,31 @@ def blend( new_alpha, ) + @lru_cache(maxsize=1024) + def tint(self, color: Color) -> Color: + """Apply a tint to a color. + + Similar to blend, but combines color and alpha. + + Args: + color: A color with alpha component. + + Returns: + New color + """ + r2, g2, b2, a2, ansi2 = color + if ansi2 is not None: + return color + r1, g1, b1, a1, ansi1 = self + if ansi1 is not None: + return color + return Color( + int(r1 + (r2 - r1) * a2), + int(g1 + (g2 - g1) * a2), + int(b1 + (b2 - b1) * a2), + a1, + ) + def __add__(self, other: object) -> Color: if isinstance(other, Color): return self.blend(other, other.a, 1.0) diff --git a/src/textual/dom.py b/src/textual/dom.py index 4608a3681b..f4555b04f5 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1026,11 +1026,11 @@ def rich_style(self) -> Style: has_rule = styles.has_rule opacity *= styles.opacity if has_rule("background"): - text_background = ( - background + styles.background + styles.background_tint + text_background = background + styles.background.tint( + styles.background_tint ) background += ( - styles.background + styles.background_tint + styles.background.tint(styles.background_tint) ).multiply_alpha(opacity) else: text_background = background @@ -1119,7 +1119,7 @@ def background_colors(self) -> tuple[Color, Color]: for node in reversed(self.ancestors_with_self): styles = node.styles base_background = background - background += styles.background + styles.background_tint + background += styles.background.tint(styles.background_tint) return (base_background, background) @property @@ -1135,7 +1135,7 @@ def _opacity_background_colors(self) -> tuple[Color, Color]: styles = node.styles base_background = background opacity *= styles.opacity - background += (styles.background + styles.background_tint).multiply_alpha( + background += styles.background.tint(styles.background_tint).multiply_alpha( opacity ) return (base_background, background) @@ -1152,7 +1152,7 @@ def colors(self) -> tuple[Color, Color, Color, Color]: for node in reversed(self.ancestors_with_self): styles = node.styles base_background = background - background += styles.background + styles.background_tint + background += styles.background.tint(styles.background_tint) if styles.has_rule("color"): base_color = color if styles.auto_color: diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index dca33964cf..71435750d9 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2314,15 +2314,27 @@ def compose(self) -> ComposeResult: def test_background_tint(snap_compare): + """Test background tint with alpha.""" + + # The screen background is dark blue + # The vertical is 20% white + # With no background tint, the verticals will be a light blue + # With a 100% tint, the vertical should be 20% red plus the blue (i.e. purple) + + # tl;dr you should see 4 bars, blue at the top, purple at the bottom, and two shades in betweenm + class BackgroundTintApp(App): CSS = """ + Screen { + background: rgb(0,0,100) + } Vertical { - background: $panel; + background: rgba(255,255,255,0.2); } - #tint1 { background-tint: $foreground 0%; } - #tint2 { background-tint: $foreground 33%; } - #tint3 { background-tint: $foreground 66%; } - #tint4 { background-tint: $foreground 100% } + #tint1 { background-tint: rgb(255,0,0) 0%; } + #tint2 { background-tint: rgb(255,0,0) 33%; } + #tint3 { background-tint: rgb(255,0,0) 66%; } + #tint4 { background-tint: rgb(255,0,0) 100% } """ def compose(self) -> ComposeResult: diff --git a/tests/test_color.py b/tests/test_color.py index e13a7bf726..00e7270116 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -265,3 +265,27 @@ def test_is_transparent(): assert not Color(20, 20, 30, a=0.01).is_transparent assert not Color(20, 20, 30, a=1).is_transparent assert not Color(20, 20, 30, 0, ansi=1).is_transparent + + +@pytest.mark.parametrize( + "base,tint,expected", + [ + ( + Color(0, 0, 0), + Color(10, 20, 30), + Color(10, 20, 30), + ), + ( + Color(0, 0, 0, 0.5), + Color(255, 255, 255, 0.5), + Color(127, 127, 127, 0.5), + ), + ( + Color(100, 0, 0, 0.2), + Color(0, 100, 0, 0.5), + Color(50, 50, 0, 0.2), + ), + ], +) +def test_tint(base: Color, tint: Color, expected: Color) -> None: + assert base.tint(tint) == expected From 71ea907e85f3c6464534f493737c0592142158c9 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Oct 2024 16:49:14 +0100 Subject: [PATCH 024/104] snapshot --- src/textual/color.py | 4 +- .../test_snapshots/test_background_tint.svg | 116 +++++++++--------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/textual/color.py b/src/textual/color.py index ef159ce698..a71de17de8 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -413,10 +413,10 @@ def tint(self, color: Color) -> Color: """ r2, g2, b2, a2, ansi2 = color if ansi2 is not None: - return color + return self r1, g1, b1, a1, ansi1 = self if ansi1 is not None: - return color + return self return Color( int(r1 + (r2 - r1) * a2), int(g1 + (g2 - g1) * a2), diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_background_tint.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_background_tint.svg index 22b4fda3db..e37daad3d7 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_background_tint.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_background_tint.svg @@ -19,134 +19,134 @@ font-weight: 700; } - .terminal-2147534319-matrix { + .terminal-3859065499-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2147534319-title { + .terminal-3859065499-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2147534319-r1 { fill: #e2e3e3 } -.terminal-2147534319-r2 { fill: #c5c8c6 } -.terminal-2147534319-r3 { fill: #ebebec } -.terminal-2147534319-r4 { fill: #f3f3f4 } -.terminal-2147534319-r5 { fill: #fcfcfc } + .terminal-3859065499-r1 { fill: #e4e4ee } +.terminal-3859065499-r2 { fill: #c5c8c6 } +.terminal-3859065499-r3 { fill: #e4e2ec } +.terminal-3859065499-r4 { fill: #e4e0ea } +.terminal-3859065499-r5 { fill: #e4dde8 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BackgroundTintApp + BackgroundTintApp - - - - 0%                                                                               - - - - - -33%                                                                              - - - - - -66%                                                                              - - - - - -100%                                                                             - - - - + + + + 0%                                                                               + + + + + +33%                                                                              + + + + + +66%                                                                              + + + + + +100%                                                                             + + + + From 2c3a6dcc97ea8015cf075da5e78f409e4f3ea3e0 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Oct 2024 16:50:10 +0100 Subject: [PATCH 025/104] reorder --- src/textual/color.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/textual/color.py b/src/textual/color.py index a71de17de8..6568ba8f3e 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -411,12 +411,13 @@ def tint(self, color: Color) -> Color: Returns: New color """ - r2, g2, b2, a2, ansi2 = color - if ansi2 is not None: - return self + r1, g1, b1, a1, ansi1 = self if ansi1 is not None: return self + r2, g2, b2, a2, ansi2 = color + if ansi2 is not None: + return self return Color( int(r1 + (r2 - r1) * a2), int(g1 + (g2 - g1) * a2), From 793dd5b1793b31e3876d52bff9c278ffa2c0271a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Oct 2024 16:51:57 +0100 Subject: [PATCH 026/104] typo --- tests/snapshot_tests/test_snapshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 71435750d9..40832ce97b 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2321,7 +2321,7 @@ def test_background_tint(snap_compare): # With no background tint, the verticals will be a light blue # With a 100% tint, the vertical should be 20% red plus the blue (i.e. purple) - # tl;dr you should see 4 bars, blue at the top, purple at the bottom, and two shades in betweenm + # tl;dr you should see 4 bars, blue at the top, purple at the bottom, and two shades in between class BackgroundTintApp(App): CSS = """ From 32cefdffd3d86ee065487412b0e17912d9e67733 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 17 Oct 2024 12:01:44 +0100 Subject: [PATCH 027/104] widgets page --- src/textual/containers.py | 32 +++++++++++-- src/textual/demo2/demo_app.py | 8 ++++ src/textual/demo2/home.py | 9 ++-- src/textual/demo2/page.py | 4 +- src/textual/demo2/projects.py | 7 +-- src/textual/demo2/widgets.py | 86 ++++++++++++++++++++++++++++++++++ src/textual/widget.py | 5 ++ src/textual/widgets/_button.py | 20 ++++++-- 8 files changed, 156 insertions(+), 15 deletions(-) create mode 100644 src/textual/demo2/widgets.py diff --git a/src/textual/containers.py b/src/textual/containers.py index 2ff19ac3d0..3a63368b5e 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -75,7 +75,7 @@ class ScrollableContainer(Widget, can_focus=True, inherit_bindings=False): class Vertical(Widget, inherit_bindings=False): - """A container with vertical layout and no scrollbars.""" + """An expanding container with vertical layout and no scrollbars.""" DEFAULT_CSS = """ Vertical { @@ -87,6 +87,19 @@ class Vertical(Widget, inherit_bindings=False): """ +class VerticalGroup(Widget, inherit_bindings=False): + """A non-expanding container with vertical layout and no scrollbars.""" + + DEFAULT_CSS = """ + VerticalGroup { + width: 1fr; + height: auto; + layout: vertical; + overflow: hidden hidden; + } + """ + + class VerticalScroll(ScrollableContainer): """A container with vertical layout and an automatic scrollbar on the Y axis.""" @@ -100,7 +113,7 @@ class VerticalScroll(ScrollableContainer): class Horizontal(Widget, inherit_bindings=False): - """A container with horizontal layout and no scrollbars.""" + """An expanding container with horizontal layout and no scrollbars.""" DEFAULT_CSS = """ Horizontal { @@ -112,6 +125,19 @@ class Horizontal(Widget, inherit_bindings=False): """ +class HorizontalGroup(Widget, inherit_bindings=False): + """A non-expanding container with horizontal layout and no scrollbars.""" + + DEFAULT_CSS = """ + HorizontalGroup { + width: 1fr; + height: auto; + layout: horizontal; + overflow: hidden hidden; + } + """ + + class HorizontalScroll(ScrollableContainer): """A container with horizontal layout and an automatic scrollbar on the X axis.""" @@ -178,7 +204,7 @@ class ItemGrid(Widget, inherit_bindings=False): DEFAULT_CSS = """ ItemGrid { width: 1fr; - height: 1fr; + height: auto; layout: grid; } """ diff --git a/src/textual/demo2/demo_app.py b/src/textual/demo2/demo_app.py index ea9ec1d364..ada9d43fe7 100644 --- a/src/textual/demo2/demo_app.py +++ b/src/textual/demo2/demo_app.py @@ -2,6 +2,7 @@ from textual.binding import Binding from textual.demo2.home import HomeScreen from textual.demo2.projects import ProjectsScreen +from textual.demo2.widgets import WidgetsScreen class DemoApp(App): @@ -10,6 +11,7 @@ class DemoApp(App): MODES = { "home": HomeScreen, "projects": ProjectsScreen, + "widgets": WidgetsScreen, } DEFAULT_MODE = "home" BINDINGS = [ @@ -25,4 +27,10 @@ class DemoApp(App): "projects", tooltip="A selection of Textual projects", ), + Binding( + "w", + "app.switch_mode('widgets')", + "widgets", + tooltip="Test the builtin widgets", + ), ] diff --git a/src/textual/demo2/home.py b/src/textual/demo2/home.py index 053880f3e6..8f24549ec4 100644 --- a/src/textual/demo2/home.py +++ b/src/textual/demo2/home.py @@ -12,10 +12,10 @@ WHAT_IS_TEXTUAL_MD = """\ # What is Textual? -**The fastest way to build applications that run *anywhere*.** +Snappy, keyboard-centric, applications that run in the terminal and [the web](https://github.com/Textualize/textual-web). + +🐍 All you need is Python! -Textual apps run on virtually any device from a $5 single-board computer upwards. -Deploy as a terminal application, over SSH, or serve as a [web application](https://github.com/Textualize/textual-web). """ WELCOME_MD = """\ @@ -191,7 +191,8 @@ class HomeScreen(PageScreen): DEFAULT_CSS = """ HomeScreen { align-horizontal: center; - Content { + Content { + margin: 0 1; max-width: 100; overflow-y: auto; height: 1fr; diff --git a/src/textual/demo2/page.py b/src/textual/demo2/page.py index b3463b0a27..f275c093fb 100644 --- a/src/textual/demo2/page.py +++ b/src/textual/demo2/page.py @@ -47,10 +47,10 @@ def on_mount(self): class PageScreen(Screen): DEFAULT_CSS = """ - Page { + PageScreen { width: 100%; height: 1fr; - overflow-y: auto; + overflow-y: auto; } """ BINDINGS = [ diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index f39877f865..b7789237c2 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -192,7 +192,7 @@ def action_open_repository(self) -> None: class ProjectsScreen(PageScreen): AUTO_FOCUS = None DEFAULT_CSS = """ - ProjectsScreen { + ProjectsScreen { align-horizontal: center; ItemGrid { margin: 2 4; @@ -203,8 +203,9 @@ class ProjectsScreen(PageScreen): grid-gutter: 1 1; grid-rows: auto; keyline:thin $foreground 50%; - } - Markdown { max-width: 80; } + } + Center { background: green; } + Markdown { max-width: 100;} } """ diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py new file mode 100644 index 0000000000..c556fb1060 --- /dev/null +++ b/src/textual/demo2/widgets.py @@ -0,0 +1,86 @@ +from textual import containers +from textual.app import ComposeResult +from textual.demo2.page import PageScreen +from textual.widgets import Button, Footer, Markdown + +WIDGETS_MD = """\ +# Widgets + +The Textual library includes a large number of builtin widgets. + +The following list is *not* exhaustive… + +""" + + +class ButtonsWidget(containers.VerticalGroup): + DEFAULT_CLASSES = "column" + + DEFAULT_CSS = """ + ButtonsWidget { + ItemGrid { width: 100%; margin-bottom: 1; } + Button { width: 1fr; } + } + """ + + BUTTONS_MD = """\ +## Buttons + +A simple button, with a number of semantic styles. +May be rendered unclickable by setting `disabled=True`. + + """ + + def compose(self) -> ComposeResult: + yield Markdown(self.BUTTONS_MD) + with containers.ItemGrid(min_column_width=20): + yield Button( + "Default", + tooltip="The default button style", + action="notify('you pressed Default')", + ) + yield Button( + "Primary", + variant="primary", + tooltip="The primary button style - carry out the core action of the dialog", + action="notify('you pressed Primary')", + ) + yield Button( + "Warning", + variant="warning", + tooltip="The warning button style - warn the user that this isn't a typical button", + action="notify('you pressed Warning')", + ) + yield Button( + "Error", + variant="error", + tooltip="The error button style - clicking is a destructive action", + action="notify('you pressed Error')", + ) + with containers.ItemGrid(min_column_width=20): + yield Button("Default", disabled=True) + yield Button("Primary", variant="primary", disabled=True) + yield Button("Warning", variant="warning", disabled=True) + yield Button("Error", variant="error", disabled=True) + + +class WidgetsScreen(PageScreen): + CSS = """ + WidgetsScreen { + align-horizontal: center; + Markdown { max-width: 100; } + .column { + align:center middle; + &>*{ + max-width: 100; + } + } + } + """ + + def compose(self) -> ComposeResult: + with containers.VerticalScroll(): + with containers.Center(): + yield Markdown(WIDGETS_MD) + yield ButtonsWidget() + yield Footer() diff --git a/src/textual/widget.py b/src/textual/widget.py index f9bca1871a..dd11e47831 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -4145,3 +4145,8 @@ def notify( severity=severity, timeout=timeout, ) + + def action_notify( + self, message: str, title: str = "", severity: str = "information" + ) -> None: + self.notify(message, title=title, severity=severity) diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index dc9b4c84f6..67123d8d59 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -36,7 +36,12 @@ class InvalidButtonVariant(Exception): class Button(Widget, can_focus=True): - """A simple clickable button.""" + """A simple clickable button. + + Clicking the button will send a [Button.Pressed][textual.widgets.Button.Pressed] message, + unless the `action` parameter is provided. + + """ DEFAULT_CSS = """ Button { @@ -155,7 +160,7 @@ class Button(Widget, can_focus=True): """The variant name for the button.""" class Pressed(Message): - """Event sent when a `Button` is pressed. + """Event sent when a `Button` is pressed and there is no Button action. Can be handled using `on_button_pressed` in a subclass of [`Button`][textual.widgets.Button] or in a parent widget in the DOM. @@ -184,6 +189,7 @@ def __init__( classes: str | None = None, disabled: bool = False, tooltip: RenderableType | None = None, + action: str | None = None, ): """Create a Button widget. @@ -195,6 +201,7 @@ def __init__( classes: The CSS classes of the button. disabled: Whether the button is disabled or not. tooltip: Optional tooltip. + action: Optional action to run when clicked. """ super().__init__(name=name, id=id, classes=classes, disabled=disabled) @@ -203,8 +210,10 @@ def __init__( self.label = label self.variant = variant + self.action = action self.active_effect_duration = 0.2 """Amount of time in seconds the button 'press' animation lasts.""" + if tooltip is not None: self.tooltip = tooltip @@ -269,7 +278,12 @@ def press(self) -> Self: # Manage the "active" effect: self._start_active_affect() # ...and let other components know that we've just been clicked: - self.post_message(Button.Pressed(self)) + if self.action is None: + self.post_message(Button.Pressed(self)) + else: + self.call_later( + self.app.run_action, self.action, default_namespace=self._parent + ) return self def _start_active_affect(self) -> None: From 1e2602898c064ed19c80c2b2352449b098b64ada Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 17 Oct 2024 17:07:09 +0100 Subject: [PATCH 028/104] widgets --- CHANGELOG.md | 1 + src/textual/containers.py | 7 ++ src/textual/demo2/demo_app.py | 7 ++ src/textual/demo2/home.py | 2 + src/textual/demo2/projects.py | 9 +-- src/textual/demo2/widgets.py | 116 +++++++++++++++++++++++++++++----- src/textual/layouts/grid.py | 5 ++ 7 files changed, 126 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65c821d873..a9e4a3ca2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `open_links` to `Markdown` and `MarkdownViewer` widgets - Added `background-tint` CSS rule https://github.com/Textualize/textual/pull/5117 - Added `App.DEFAULT_MODE` +- Added `Containers.HorizontalGroup` and `Containers.VerticalGroup` ### Fixed diff --git a/src/textual/containers.py b/src/textual/containers.py index 3a63368b5e..70986b9e47 100644 --- a/src/textual/containers.py +++ b/src/textual/containers.py @@ -211,6 +211,7 @@ class ItemGrid(Widget, inherit_bindings=False): stretch_height: reactive[bool] = reactive(True) min_column_width: reactive[int | None] = reactive(None, layout=True) + regular: reactive[bool] = reactive(False) def __init__( self, @@ -221,6 +222,7 @@ def __init__( disabled: bool = False, min_column_width: int | None = None, stretch_height: bool = True, + regular: bool = False, ) -> None: """Initialize a Widget. @@ -230,14 +232,19 @@ def __init__( id: The ID of the widget in the DOM. classes: The CSS classes for the widget. disabled: Whether the widget is disabled or not. + stretch_height: Expand the height of widgets to the row height. + min_column_width: The smallest permitted column width. + regular: All rows should have the same number of items. """ super().__init__( *children, name=name, id=id, classes=classes, disabled=disabled ) self.set_reactive(ItemGrid.stretch_height, stretch_height) self.set_reactive(ItemGrid.min_column_width, min_column_width) + self.set_reactive(ItemGrid.regular, regular) def pre_layout(self, layout: Layout) -> None: if isinstance(layout, GridLayout): layout.stretch_height = self.stretch_height layout.min_column_width = self.min_column_width + layout.regular = self.regular diff --git a/src/textual/demo2/demo_app.py b/src/textual/demo2/demo_app.py index ada9d43fe7..28307d30b1 100644 --- a/src/textual/demo2/demo_app.py +++ b/src/textual/demo2/demo_app.py @@ -8,6 +8,13 @@ class DemoApp(App): """The demo app defines the modes and sets a few bindings.""" + CSS = """ + .column { + align: center top; + &>*{ max-width: 100; } + } + """ + MODES = { "home": HomeScreen, "projects": ProjectsScreen, diff --git a/src/textual/demo2/home.py b/src/textual/demo2/home.py index 8f24549ec4..989d62cf0a 100644 --- a/src/textual/demo2/home.py +++ b/src/textual/demo2/home.py @@ -1,3 +1,4 @@ +import asyncio from importlib.metadata import version import httpx @@ -148,6 +149,7 @@ async def get_stars(self): """Worker to get stars from GitHub API.""" self.loading = True try: + await asyncio.sleep(1) # Time to admire the loading indicator async with httpx.AsyncClient() as client: repository_json = ( await client.get("https://api.github.com/repos/textualize/textual") diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index b7789237c2..045eb24016 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -125,6 +125,8 @@ class ProjectInfo: class Project(Vertical, can_focus=True, can_focus_children=False): + """Display project information and open repo links.""" + ALLOW_MAXIMIZE = True DEFAULT_CSS = """ Project { @@ -191,7 +193,7 @@ def action_open_repository(self) -> None: class ProjectsScreen(PageScreen): AUTO_FOCUS = None - DEFAULT_CSS = """ + CSS = """ ProjectsScreen { align-horizontal: center; ItemGrid { @@ -203,9 +205,8 @@ class ProjectsScreen(PageScreen): grid-gutter: 1 1; grid-rows: auto; keyline:thin $foreground 50%; - } - Center { background: green; } - Markdown { max-width: 100;} + } + Markdown { margin: 0; padding: 0 2; max-width: 100;} } """ diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index c556fb1060..cb31b4cf46 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -1,7 +1,7 @@ from textual import containers from textual.app import ComposeResult from textual.demo2.page import PageScreen -from textual.widgets import Button, Footer, Markdown +from textual.widgets import Button, Checkbox, DataTable, Footer, Markdown, RadioSet WIDGETS_MD = """\ # Widgets @@ -13,12 +13,13 @@ """ -class ButtonsWidget(containers.VerticalGroup): - DEFAULT_CLASSES = "column" +class Buttons(containers.VerticalGroup): + """Buttons demo.""" + DEFAULT_CLASSES = "column" DEFAULT_CSS = """ - ButtonsWidget { - ItemGrid { width: 100%; margin-bottom: 1; } + Buttons { + ItemGrid { margin-bottom: 1;} Button { width: 1fr; } } """ @@ -33,7 +34,7 @@ class ButtonsWidget(containers.VerticalGroup): def compose(self) -> ComposeResult: yield Markdown(self.BUTTONS_MD) - with containers.ItemGrid(min_column_width=20): + with containers.ItemGrid(min_column_width=20, regular=True): yield Button( "Default", tooltip="The default button style", @@ -57,30 +58,111 @@ def compose(self) -> ComposeResult: tooltip="The error button style - clicking is a destructive action", action="notify('you pressed Error')", ) - with containers.ItemGrid(min_column_width=20): + with containers.ItemGrid(min_column_width=20, regular=True): yield Button("Default", disabled=True) yield Button("Primary", variant="primary", disabled=True) yield Button("Warning", variant="warning", disabled=True) yield Button("Error", variant="error", disabled=True) +class Checkboxes(containers.VerticalGroup): + DEFAULT_CLASSES = "column" + DEFAULT_CSS = """ + Checkboxes { + height: auto; + Checkbox, RadioButton { width: 1fr; } + ItemGrid { + margin-bottom: 1; + } + } + + """ + + CHECKBOXES_MD = """\ +## Checkboxes + +A checkbox with two states. + + """ + + def compose(self) -> ComposeResult: + yield Markdown(self.CHECKBOXES_MD) + with containers.ItemGrid(min_column_width=20, regular=True): + yield Checkbox("Arrakis") + yield Checkbox("Caladan") + yield Checkbox("Chusuk") + yield Checkbox("Giedi Prime") + + +class RadioSets(containers.VerticalGroup): + DEFAULT_CLASSES = "column" + RADIOSETS_MD = """\ +## Radiosets + +A group of toggles where only once may be active at a time. + +""" + + def compose(self) -> ComposeResult: + yield Markdown(self.RADIOSETS_MD) + yield RadioSet( + "Amanda", + "Connor MacLeod", + "Duncan MacLeod", + "Heather MacLeod", + "Joe Dawson", + "Kurgan, [bold italic red]The[/]", + "Methos", + "Rachel Ellenstein", + "Ramírez", + ) + + +class Datatables(containers.VerticalGroup): + DEFAULT_CLASSES = "column" + DATATABLES_MD = """\ +## Datatables + +A fully-featured + +""" + ROWS = [ + ("lane", "swimmer", "country", "time"), + (4, "Joseph Schooling", "Singapore", 50.39), + (2, "Michael Phelps", "United States", 51.14), + (5, "Chad le Clos", "South Africa", 51.14), + (6, "László Cseh", "Hungary", 51.14), + (3, "Li Zhuhao", "China", 51.26), + (8, "Mehdy Metella", "France", 51.58), + (7, "Tom Shields", "United States", 51.73), + (1, "Aleksandr Sadovnikov", "Russia", 51.84), + (10, "Darren Burns", "Scotland", 51.84), + ] + + def compose(self) -> ComposeResult: + yield Markdown(self.DATATABLES_MD) + with containers.Center(): + yield DataTable() + + def on_mount(self) -> None: + table = self.query_one(DataTable) + table.add_columns(*self.ROWS[0]) + table.add_rows(self.ROWS[1:]) + + class WidgetsScreen(PageScreen): CSS = """ WidgetsScreen { - align-horizontal: center; - Markdown { max-width: 100; } - .column { - align:center middle; - &>*{ - max-width: 100; - } - } + align-horizontal: center; } """ def compose(self) -> ComposeResult: with containers.VerticalScroll(): - with containers.Center(): + with containers.Center(classes="column"): yield Markdown(WIDGETS_MD) - yield ButtonsWidget() + yield Buttons() + yield Checkboxes() + yield RadioSets() + yield Datatables() yield Footer() diff --git a/src/textual/layouts/grid.py b/src/textual/layouts/grid.py index 310c8b3128..d7dfc95896 100644 --- a/src/textual/layouts/grid.py +++ b/src/textual/layouts/grid.py @@ -20,6 +20,7 @@ class GridLayout(Layout): def __init__(self) -> None: self.min_column_width: int | None = None self.stretch_height: bool = False + self.regular = False def arrange( self, parent: Widget, children: list[Widget], size: Size @@ -35,6 +36,7 @@ def arrange( table_size_columns = max(1, styles.grid_size_columns) min_column_width = self.min_column_width + if min_column_width is not None: container_width = size.width table_size_columns = max( @@ -43,6 +45,9 @@ def arrange( // (min_column_width + gutter_horizontal), ) table_size_columns = min(table_size_columns, len(children)) + if self.regular: + while len(children) % table_size_columns and table_size_columns > 1: + table_size_columns -= 1 table_size_rows = styles.grid_size_rows viewport = parent.screen.size From 03e33b8408c1825d0e627e13241afbebe76bbb82 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 13:38:05 +0100 Subject: [PATCH 029/104] more widgets --- docs/widgets/masked_input.md | 2 +- src/textual/demo2/data.py | 198 +++++++++++++++++++++++++++++++ src/textual/demo2/widgets.py | 134 +++++++++++++++------ src/textual/widgets/__init__.pyi | 1 + 4 files changed, 297 insertions(+), 38 deletions(-) create mode 100644 src/textual/demo2/data.py diff --git a/docs/widgets/masked_input.md b/docs/widgets/masked_input.md index d40350b2c8..426af52937 100644 --- a/docs/widgets/masked_input.md +++ b/docs/widgets/masked_input.md @@ -16,7 +16,7 @@ The example below shows a masked input to ease entering a credit card number. ```{.textual path="docs/examples/widgets/masked_input.py"} ``` -=== "checkbox.py" +=== "masked_input.py" ```python --8<-- "docs/examples/widgets/masked_input.py" diff --git a/src/textual/demo2/data.py b/src/textual/demo2/data.py new file mode 100644 index 0000000000..6023ec83e2 --- /dev/null +++ b/src/textual/demo2/data.py @@ -0,0 +1,198 @@ +COUNTRIES = [ + "Afghanistan", + "Albania", + "Algeria", + "Andorra", + "Angola", + "Antigua and Barbuda", + "Argentina", + "Armenia", + "Australia", + "Austria", + "Azerbaijan", + "Bahamas", + "Bahrain", + "Bangladesh", + "Barbados", + "Belarus", + "Belgium", + "Belize", + "Benin", + "Bhutan", + "Bolivia", + "Bosnia and Herzegovina", + "Botswana", + "Brazil", + "Brunei", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cabo Verde", + "Cambodia", + "Cameroon", + "Canada", + "Central African Republic", + "Chad", + "Chile", + "China", + "Colombia", + "Comoros", + "Congo", + "Costa Rica", + "Croatia", + "Cuba", + "Cyprus", + "Czech Republic", + "Democratic Republic of the Congo", + "Denmark", + "Djibouti", + "Dominica", + "Dominican Republic", + "East Timor", + "Ecuador", + "Egypt", + "El Salvador", + "Equatorial Guinea", + "Eritrea", + "Estonia", + "Eswatini", + "Ethiopia", + "Fiji", + "Finland", + "France", + "Gabon", + "Gambia", + "Georgia", + "Germany", + "Ghana", + "Greece", + "Grenada", + "Guatemala", + "Guinea", + "Guinea-Bissau", + "Guyana", + "Haiti", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Iraq", + "Ireland", + "Israel", + "Italy", + "Ivory Coast", + "Jamaica", + "Japan", + "Jordan", + "Kazakhstan", + "Kenya", + "Kiribati", + "Kuwait", + "Kyrgyzstan", + "Laos", + "Latvia", + "Lebanon", + "Lesotho", + "Liberia", + "Libya", + "Liechtenstein", + "Lithuania", + "Luxembourg", + "Madagascar", + "Malawi", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Marshall Islands", + "Mauritania", + "Mauritius", + "Mexico", + "Micronesia", + "Moldova", + "Monaco", + "Mongolia", + "Montenegro", + "Morocco", + "Mozambique", + "Myanmar", + "Namibia", + "Nauru", + "Nepal", + "Netherlands", + "New Zealand", + "Nicaragua", + "Niger", + "Nigeria", + "North Korea", + "North Macedonia", + "Norway", + "Oman", + "Pakistan", + "Palau", + "Palestine", + "Panama", + "Papua New Guinea", + "Paraguay", + "Peru", + "Philippines", + "Poland", + "Portugal", + "Qatar", + "Romania", + "Russia", + "Rwanda", + "Saint Kitts and Nevis", + "Saint Lucia", + "Saint Vincent and the Grenadines", + "Samoa", + "San Marino", + "Sao Tome and Principe", + "Saudi Arabia", + "Senegal", + "Serbia", + "Seychelles", + "Sierra Leone", + "Singapore", + "Slovakia", + "Slovenia", + "Solomon Islands", + "Somalia", + "South Africa", + "South Korea", + "South Sudan", + "Spain", + "Sri Lanka", + "Sudan", + "Suriname", + "Sweden", + "Switzerland", + "Syria", + "Taiwan", + "Tajikistan", + "Tanzania", + "Thailand", + "Togo", + "Tonga", + "Trinidad and Tobago", + "Tunisia", + "Turkey", + "Turkmenistan", + "Tuvalu", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "United States", + "Uruguay", + "Uzbekistan", + "Vanuatu", + "Vatican City", + "Venezuela", + "Vietnam", + "Yemen", + "Zambia", + "Zimbabwe", +] diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index cb31b4cf46..c31686b85f 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -1,7 +1,20 @@ from textual import containers from textual.app import ComposeResult +from textual.demo2.data import COUNTRIES from textual.demo2.page import PageScreen -from textual.widgets import Button, Checkbox, DataTable, Footer, Markdown, RadioSet +from textual.suggester import SuggestFromList +from textual.widgets import ( + Button, + Checkbox, + DataTable, + Footer, + Input, + Label, + Markdown, + MaskedInput, + RadioButton, + RadioSet, +) WIDGETS_MD = """\ # Widgets @@ -71,51 +84,39 @@ class Checkboxes(containers.VerticalGroup): Checkboxes { height: auto; Checkbox, RadioButton { width: 1fr; } - ItemGrid { - margin-bottom: 1; - } + &>HorizontalGroup > * { width: 1fr; } } """ CHECKBOXES_MD = """\ -## Checkboxes +## Checkboxes, Radio buttons, and Radio sets -A checkbox with two states. +Checkboxes to toggle booleans. +Radio buttons for exclusive booleans. +Radio sets for a managed set of options where only a single option may be selected. """ def compose(self) -> ComposeResult: yield Markdown(self.CHECKBOXES_MD) - with containers.ItemGrid(min_column_width=20, regular=True): - yield Checkbox("Arrakis") - yield Checkbox("Caladan") - yield Checkbox("Chusuk") - yield Checkbox("Giedi Prime") - - -class RadioSets(containers.VerticalGroup): - DEFAULT_CLASSES = "column" - RADIOSETS_MD = """\ -## Radiosets - -A group of toggles where only once may be active at a time. - -""" - - def compose(self) -> ComposeResult: - yield Markdown(self.RADIOSETS_MD) - yield RadioSet( - "Amanda", - "Connor MacLeod", - "Duncan MacLeod", - "Heather MacLeod", - "Joe Dawson", - "Kurgan, [bold italic red]The[/]", - "Methos", - "Rachel Ellenstein", - "Ramírez", - ) + with containers.HorizontalGroup(): + with containers.VerticalGroup(): + yield Checkbox("Arrakis") + yield Checkbox("Caladan") + yield RadioButton("Chusuk") + yield RadioButton("Giedi Prime") + yield RadioSet( + "Amanda", + "Connor MacLeod", + "Duncan MacLeod", + "Heather MacLeod", + "Joe Dawson", + "Kurgan, [bold italic red]The[/]", + "Methos", + "Rachel Ellenstein", + "Ramírez", + ) class Datatables(containers.VerticalGroup): @@ -123,7 +124,8 @@ class Datatables(containers.VerticalGroup): DATATABLES_MD = """\ ## Datatables -A fully-featured +A fully-featured DataTable, with cell, row, and columns cursors. +Cells may be individually styled, and may include Rich renderables. """ ROWS = [ @@ -150,6 +152,59 @@ def on_mount(self) -> None: table.add_rows(self.ROWS[1:]) +class Inputs(containers.VerticalGroup): + DEFAULT_CLASSES = "column" + INPUTS_MD = """\ +## Inputs and MaskedInputs + +Text input fields, with placeholder text, validation, and auto-complete. +Build for intuitive and user-friendly forms. + +""" + DEFAULT_CSS = """ + Inputs { + Grid { + background: $boost; + padding: 1 2; + height: auto; + grid-size: 2; + grid-gutter: 1; + grid-columns: auto 1fr; + border: tall blank; + &:focus-within { + border: tall $accent; + } + Label { + width: 100%; + margin: 1; + text-align: right; + } + } + } + """ + + def compose(self) -> ComposeResult: + yield Markdown(self.INPUTS_MD) + with containers.Grid(): + yield Label("Free") + yield Input(placeholder="Type anything here") + yield Label("Number") + yield Input( + type="number", placeholder="Type a number here", valid_empty=True + ) + yield Label("Credit card") + yield MaskedInput( + "9999-9999-9999-9999;0", + tooltip="Obviously not your real credit card!", + valid_empty=True, + ) + yield Label("Country") + yield Input( + suggester=SuggestFromList(COUNTRIES, case_sensitive=False), + placeholder="Country", + ) + + class WidgetsScreen(PageScreen): CSS = """ WidgetsScreen { @@ -157,12 +212,17 @@ class WidgetsScreen(PageScreen): } """ + BINDINGS = [("escape", "unfocus")] + def compose(self) -> ComposeResult: with containers.VerticalScroll(): with containers.Center(classes="column"): yield Markdown(WIDGETS_MD) yield Buttons() yield Checkboxes() - yield RadioSets() yield Datatables() + yield Inputs() yield Footer() + + def action_unfocus(self) -> None: + self.set_focus(None) diff --git a/src/textual/widgets/__init__.pyi b/src/textual/widgets/__init__.pyi index 141c1df46a..907ae843b8 100644 --- a/src/textual/widgets/__init__.pyi +++ b/src/textual/widgets/__init__.pyi @@ -19,6 +19,7 @@ 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 ._masked_input import MaskedInput as MaskedInput from ._option_list import OptionList as OptionList from ._placeholder import Placeholder as Placeholder from ._pretty import Pretty as Pretty From bd091972ee14a30394452f64828bb75c5c410445 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 15:05:09 +0100 Subject: [PATCH 030/104] new mechanism to expose pseudo classes --- src/textual/app.py | 25 ++++----- src/textual/dom.py | 25 +++++++-- src/textual/widget.py | 128 +++++++++++++++++++++++++++--------------- tests/test_app.py | 6 ++ tests/test_widget.py | 35 ++++++++++++ 5 files changed, 154 insertions(+), 65 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index b49cca30d8..129b18cabf 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -487,6 +487,16 @@ class MyApp(App[None]): INLINE_PADDING: ClassVar[int] = 1 """Number of blank lines above an inline app.""" + PSEUDO_CLASSES: ClassVar[dict[str, Callable[[App], bool]]] = { + "focus": lambda app: app.app_focus, + "blur": lambda app: not app.app_focus, + "dark": lambda app: app.dark, + "light": lambda app: not app.dark, + "inline": lambda app: app.is_inline, + "ansi": lambda app: app.ansi_color, + "nocolor": lambda app: app.no_color, + } # type: ignore[assignment] + title: Reactive[str] = Reactive("", compute=False) """The title of the app, displayed in the header.""" sub_title: Reactive[str] = Reactive("", compute=False) @@ -892,21 +902,6 @@ def _context(self) -> Generator[None, None, None]: active_message_pump.reset(message_pump_reset_token) active_app.reset(app_reset_token) - def get_pseudo_classes(self) -> Iterable[str]: - """Pseudo classes for a widget. - - Returns: - Names of the pseudo classes. - """ - yield "focus" if self.app_focus else "blur" - yield "dark" if self.dark else "light" - if self.is_inline: - yield "inline" - if self.ansi_color: - yield "ansi" - if self.no_color: - yield "nocolor" - def _watch_ansi_color(self, ansi_color: bool) -> None: """Enable or disable the truecolor filter when the reactive changes""" for filter in self._filters: diff --git a/src/textual/dom.py b/src/textual/dom.py index 4608a3681b..57ab3281f1 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -180,6 +180,8 @@ class DOMNode(MessagePump): # Names of potential computed reactives _computes: ClassVar[frozenset[str]] + PSEUDO_CLASSES: ClassVar[dict[str, Callable[[object], bool]]] = {} + def __init__( self, *, @@ -1228,13 +1230,18 @@ def on_dark_change(old_value:bool, new_value:bool) -> None: """ _watch(self, obj, attribute_name, callback, init=init) - def get_pseudo_classes(self) -> Iterable[str]: - """Get any pseudo classes applicable to this Node, e.g. hover, focus. + def get_pseudo_classes(self) -> set[str]: + """Pseudo classes for a widget. Returns: - Iterable of strings, such as a generator. + Names of the pseudo classes. """ - return () + + return { + name + for name, check_class in self.PSEUDO_CLASSES.items() + if check_class(self) + } def reset_styles(self) -> None: """Reset styles back to their initial state.""" @@ -1658,7 +1665,9 @@ def has_pseudo_class(self, class_name: str) -> bool: Returns: `True` if the DOM node has the pseudo class, `False` if not. """ - return class_name in self.get_pseudo_classes() + return class_name in self.PSEUDO_CLASSES and self.PSEUDO_CLASSES[class_name]( + self + ) def has_pseudo_classes(self, class_names: set[str]) -> bool: """Check the node has all the given pseudo classes. @@ -1669,7 +1678,11 @@ def has_pseudo_classes(self, class_names: set[str]) -> bool: Returns: `True` if all pseudo class names are present. """ - return class_names.issubset(self.get_pseudo_classes()) + PSEUDO_CLASSES = self.PSEUDO_CLASSES + return all( + (name in PSEUDO_CLASSES and PSEUDO_CLASSES[name](self)) + for name in class_names + ) def refresh( self, *, repaint: bool = True, layout: bool = False, recompose: bool = False diff --git a/src/textual/widget.py b/src/textual/widget.py index 989d9762ce..0fe9d08264 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -14,6 +14,7 @@ from typing import ( TYPE_CHECKING, AsyncGenerator, + Callable, ClassVar, Collection, Generator, @@ -366,6 +367,25 @@ class Widget(DOMNode): # Default sort order, incremented by constructor _sort_order: ClassVar[int] = 0 + PSEUDO_CLASSES: ClassVar[dict[str, Callable[[Widget], bool]]] = { + "hover": lambda widget: widget.mouse_hover, + "focus": lambda widget: widget.has_focus, + "blur": lambda widget: not widget.has_focus, + "can-focus": lambda widget: widget.can_focus, + "disabled": lambda widget: widget.is_disabled, + "enabled": lambda widget: not widget.is_disabled, + "dark": lambda widget: widget.app.dark, + "light": lambda widget: not widget.app.dark, + "focus-within": lambda widget: widget.has_focus_within, + "inline": lambda widget: widget.app.is_inline, + "ansi": lambda widget: widget.app.ansi_color, + "nocolor": lambda widget: widget.app.no_color, + "first-of-type": lambda widget: widget.first_of_type, + "last-of-type": lambda widget: widget.last_of_type, + "odd": lambda widget: widget.is_odd, + "even": lambda widget: widget.is_even, + } # type: ignore[assignment] + def __init__( self, *children: Widget, @@ -700,6 +720,70 @@ def compose_add_child(self, widget: Widget) -> None: _rich_traceback_omit = True self._pending_children.append(widget) + @property + def is_disabled(self) -> bool: + """Is the widget disabled either because `disabled=True` or an ancestor has `disabled=True`.""" + node: MessagePump | None = self + while isinstance(node, Widget): + if node.disabled: + return True + node = node._parent + return False + + @property + def has_focus_within(self) -> bool: + """Are any descendants focused?""" + try: + focused = self.screen.focused + except NoScreen: + return False + node = focused + while node is not None: + if node is self: + return True + node = node._parent + return False + + @property + def first_of_type(self) -> bool: + """Is this the first widget of its type in its siblings?""" + if self.parent is None: + return False + try: + return self.parent.query_children(self._css_type_name).first() is self + except NoMatches: + return False + + @property + def last_of_type(self) -> bool: + """Is this the last widget of its type in its siblings?""" + if self.parent is None: + return False + try: + return self.parent.query_children(self._css_type_name).last() is self + except NoMatches: + return False + + @property + def is_odd(self) -> bool: + """Is this widget at an oddly numbered position within its siblings?""" + if self.parent is None: + return False + try: + return self.parent.children.index(self) % 2 == 0 + except ValueError: + return False + + @property + def is_even(self) -> bool: + """Is this widget at an evenly numbered position within its siblings?""" + if self.parent is None: + return False + try: + return self.parent.children.index(self) % 2 == 1 + except ValueError: + return False + def __enter__(self) -> Self: """Use as context manager when composing.""" self.app._compose_stacks[-1].append(self) @@ -3232,50 +3316,6 @@ def _arrange_scrollbars(self, region: Region) -> Iterable[tuple[Widget, Region]] scrollbar.window_size = window_region.width yield scrollbar, scrollbar_region - def get_pseudo_classes(self) -> Iterable[str]: - """Pseudo classes for a widget. - - Returns: - Names of the pseudo classes. - """ - app = self.app - if self.mouse_hover: - yield "hover" - if self.has_focus: - yield "focus" - else: - yield "blur" - if self.can_focus: - yield "can-focus" - node: MessagePump | None = self - while isinstance(node, Widget): - if node.disabled: - yield "disabled" - break - node = node._parent - else: - yield "enabled" - try: - focused = self.screen.focused - except NoScreen: - pass - else: - yield "dark" if app.dark else "light" - if focused: - node = focused - while node is not None: - if node is self: - yield "focus-within" - break - node = node._parent - - if app.is_inline: - yield "inline" - if app.ansi_color: - yield "ansi" - if app.no_color: - yield "nocolor" - def get_pseudo_class_state(self) -> PseudoClasses: """Get an object describing whether each pseudo class is present on this object or not. diff --git a/tests/test_app.py b/tests/test_app.py index aeb0a6ac21..914c3e951c 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -37,6 +37,9 @@ async def test_hover_update_styles(): "can-focus", "dark", "enabled", + "first-of-type", + "last-of-type", + "odd", } # Take note of the initial background colour @@ -50,6 +53,9 @@ async def test_hover_update_styles(): "dark", "enabled", "hover", + "first-of-type", + "last-of-type", + "odd", } assert button.styles.background != initial_background diff --git a/tests/test_widget.py b/tests/test_widget.py index 22e784b3b2..8e32ca7726 100644 --- a/tests/test_widget.py +++ b/tests/test_widget.py @@ -594,3 +594,38 @@ def test_lazy_loading() -> None: assert not hasattr(widgets, "foo") assert not hasattr(widgets, "bar") assert hasattr(widgets, "Label") + + +async def test_of_type() -> None: + class MyApp(App): + def compose(self) -> ComposeResult: + for ordinal in range(5): + yield Label(f"Item {ordinal}") + + app = MyApp() + async with app.run_test(): + labels = list(app.query(Label)) + assert labels[0].first_of_type + assert not labels[0].last_of_type + assert labels[0].is_odd + assert not labels[0].is_even + + assert not labels[1].first_of_type + assert not labels[1].last_of_type + assert not labels[1].is_odd + assert labels[1].is_even + + assert not labels[2].first_of_type + assert not labels[2].last_of_type + assert labels[2].is_odd + assert not labels[2].is_even + + assert not labels[3].first_of_type + assert not labels[3].last_of_type + assert not labels[3].is_odd + assert labels[3].is_even + + assert not labels[4].first_of_type + assert labels[4].last_of_type + assert labels[4].is_odd + assert not labels[4].is_even From ebf075a010750c5b1eb3acfe6478fe9675203351 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 15:07:04 +0100 Subject: [PATCH 031/104] test --- tests/test_app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_app.py b/tests/test_app.py index 914c3e951c..67e7ad2c25 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -39,7 +39,7 @@ async def test_hover_update_styles(): "enabled", "first-of-type", "last-of-type", - "odd", + "even", } # Take note of the initial background colour @@ -55,7 +55,7 @@ async def test_hover_update_styles(): "hover", "first-of-type", "last-of-type", - "odd", + "even", } assert button.styles.background != initial_background From 310af26c799ae2be0bdebb92cac397ab150fe537 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 15:10:42 +0100 Subject: [PATCH 032/104] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35395ba0ef..7e34d524d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added `background-tint` CSS rule https://github.com/Textualize/textual/pull/5117 +- Added first-of-type, last-of-type, odd, and even pseudo classes https://github.com/Textualize/textual/pull/5139 ## [0.83.0] - 2024-10-10 From 8dfc3d14a1dc11afb2a21c25d65670a7fc052ad6 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 15:15:52 +0100 Subject: [PATCH 033/104] update docs --- docs/guide/CSS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/guide/CSS.md b/docs/guide/CSS.md index 187a7b015c..2e27db2ca9 100644 --- a/docs/guide/CSS.md +++ b/docs/guide/CSS.md @@ -330,10 +330,14 @@ Here are some other pseudo classes: - `:dark` Matches widgets in dark mode (where `App.dark == True`). - `:disabled` Matches widgets which are in a disabled state. - `:enabled` Matches widgets which are in an enabled state. +- `:even` Matches a widget at an evenly numbered position within its siblings. +- `:first-of-type` Matches a widget that is the first of its type amongst its siblings. - `:focus-within` Matches widgets with a focused child widget. - `:focus` Matches widgets which have input focus. - `:inline` Matches widgets when the app is running in inline mode. +- `:last-of-type` Matches a widget that is the last of its type amongst its siblings. - `:light` Matches widgets in dark mode (where `App.dark == False`). +- `:odd` Matches a widget at an oddly numbered position within its siblings. ## Combinators From 5a6fbdf0bf32d424e8c80596345f42383fc886b4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 15:21:17 +0100 Subject: [PATCH 034/104] private var --- src/textual/app.py | 2 +- src/textual/dom.py | 9 +++++---- src/textual/widget.py | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index 129b18cabf..e829110b60 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -487,7 +487,7 @@ class MyApp(App[None]): INLINE_PADDING: ClassVar[int] = 1 """Number of blank lines above an inline app.""" - PSEUDO_CLASSES: ClassVar[dict[str, Callable[[App], bool]]] = { + _PSEUDO_CLASSES: ClassVar[dict[str, Callable[[App], bool]]] = { "focus": lambda app: app.app_focus, "blur": lambda app: not app.app_focus, "dark": lambda app: app.dark, diff --git a/src/textual/dom.py b/src/textual/dom.py index 57ab3281f1..e215c5116f 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -180,7 +180,8 @@ class DOMNode(MessagePump): # Names of potential computed reactives _computes: ClassVar[frozenset[str]] - PSEUDO_CLASSES: ClassVar[dict[str, Callable[[object], bool]]] = {} + _PSEUDO_CLASSES: ClassVar[dict[str, Callable[[object], bool]]] = {} + """Pseudo class checks.""" def __init__( self, @@ -1239,7 +1240,7 @@ def get_pseudo_classes(self) -> set[str]: return { name - for name, check_class in self.PSEUDO_CLASSES.items() + for name, check_class in self._PSEUDO_CLASSES.items() if check_class(self) } @@ -1665,7 +1666,7 @@ def has_pseudo_class(self, class_name: str) -> bool: Returns: `True` if the DOM node has the pseudo class, `False` if not. """ - return class_name in self.PSEUDO_CLASSES and self.PSEUDO_CLASSES[class_name]( + return class_name in self._PSEUDO_CLASSES and self._PSEUDO_CLASSES[class_name]( self ) @@ -1678,7 +1679,7 @@ def has_pseudo_classes(self, class_names: set[str]) -> bool: Returns: `True` if all pseudo class names are present. """ - PSEUDO_CLASSES = self.PSEUDO_CLASSES + PSEUDO_CLASSES = self._PSEUDO_CLASSES return all( (name in PSEUDO_CLASSES and PSEUDO_CLASSES[name](self)) for name in class_names diff --git a/src/textual/widget.py b/src/textual/widget.py index 0fe9d08264..8985566129 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -367,7 +367,7 @@ class Widget(DOMNode): # Default sort order, incremented by constructor _sort_order: ClassVar[int] = 0 - PSEUDO_CLASSES: ClassVar[dict[str, Callable[[Widget], bool]]] = { + _PSEUDO_CLASSES: ClassVar[dict[str, Callable[[Widget], bool]]] = { "hover": lambda widget: widget.mouse_hover, "focus": lambda widget: widget.has_focus, "blur": lambda widget: not widget.has_focus, From 502051b85236619449908ab5868a36d0dbff9eb4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 17:30:28 +0100 Subject: [PATCH 035/104] cache pseudo classes --- src/textual/widget.py | 62 ++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/textual/widget.py b/src/textual/widget.py index 8985566129..add9d3b17c 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -483,6 +483,13 @@ def __init__( self._cover_widget: Widget | None = None """Widget to render over this widget (used by loading indicator).""" + self._first_of_type: tuple[int, bool] = (-1, False) + """Used to cache :first-of-type pseudoclass state.""" + self._last_of_type: tuple[int, bool] = (-1, False) + """Used to cache :last-of-type pseudoclass state.""" + self._odd: tuple[int, bool] = (-1, False) + """Used to cache :odd pseudoclass state.""" + @property def is_mounted(self) -> bool: """Check if this widget is mounted.""" @@ -747,42 +754,55 @@ def has_focus_within(self) -> bool: @property def first_of_type(self) -> bool: """Is this the first widget of its type in its siblings?""" - if self.parent is None: - return False - try: - return self.parent.query_children(self._css_type_name).first() is self - except NoMatches: - return False + parent = self.parent + if parent is None: + return True + # This pseudo classes only changes when the parent's nodes._updates changes + if parent._nodes._updates == self._first_of_type[0]: + return self._first_of_type[1] + widget_type = type(self) + for node in parent._nodes: + if isinstance(node, widget_type): + self._first_of_type = (parent._nodes._updates, node is self) + return self._first_of_type[1] + return False @property def last_of_type(self) -> bool: """Is this the last widget of its type in its siblings?""" - if self.parent is None: - return False - try: - return self.parent.query_children(self._css_type_name).last() is self - except NoMatches: - return False + parent = self.parent + if parent is None: + return True + # This pseudo classes only changes when the parent's nodes._updates changes + if parent._nodes._updates == self._last_of_type[0]: + return self._last_of_type[1] + widget_type = type(self) + for node in reversed(parent.children): + if isinstance(node, widget_type): + self._last_of_type = (parent._nodes._updates, node is self) + return self._last_of_type[1] + return False @property def is_odd(self) -> bool: """Is this widget at an oddly numbered position within its siblings?""" - if self.parent is None: - return False + parent = self.parent + if parent is None: + return True + # This pseudo classes only changes when the parent's nodes._updates changes + if parent._nodes._updates == self._odd[0]: + return self._odd[1] try: - return self.parent.children.index(self) % 2 == 0 + is_odd = parent.children.index(self) % 2 == 0 + self._odd = (parent._nodes._updates, is_odd) + return is_odd except ValueError: return False @property def is_even(self) -> bool: """Is this widget at an evenly numbered position within its siblings?""" - if self.parent is None: - return False - try: - return self.parent.children.index(self) % 2 == 1 - except ValueError: - return False + return not self.is_odd def __enter__(self) -> Self: """Use as context manager when composing.""" From 2913ae62e842c1016c5c979c85c4a6097d287473 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 17:35:29 +0100 Subject: [PATCH 036/104] simpler --- src/textual/widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/widget.py b/src/textual/widget.py index add9d3b17c..adb5b6497f 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -793,7 +793,7 @@ def is_odd(self) -> bool: if parent._nodes._updates == self._odd[0]: return self._odd[1] try: - is_odd = parent.children.index(self) % 2 == 0 + is_odd = parent._nodes.index(self) % 2 == 0 self._odd = (parent._nodes._updates, is_odd) return is_odd except ValueError: From 43cc9910a3c485ede220c2d47a70a5cf8e42d281 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 19:27:10 +0100 Subject: [PATCH 037/104] improved caching --- src/textual/css/stylesheet.py | 34 +++++++++++++++++++++++----------- src/textual/dom.py | 14 ++++++++++++++ src/textual/widget.py | 9 +++++++++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py index 399d06b1fb..bf7bd35776 100644 --- a/src/textual/css/stylesheet.py +++ b/src/textual/css/stylesheet.py @@ -432,6 +432,16 @@ def _check_rule( if _check_selectors(selector_set.selectors, css_path_nodes): yield selector_set.specificity + # pseudo classes which iterate over many nodes + # these have the potential to be slow, and shouldn't be used in a cache key + EXPENSIVE_PSEUDO_CLASSES = { + "first-of-type", + "last-of_type", + "odd", + "even", + "focus-within", + } + def apply( self, node: DOMNode, @@ -468,13 +478,18 @@ def apply( } rules = list(filter(limit_rules.__contains__, reversed(self.rules))) - node._has_hover_style = any("hover" in rule.pseudo_classes for rule in rules) - node._has_focus_within = any( - "focus-within" in rule.pseudo_classes for rule in rules - ) + all_pseudo_classes = set() + for rule in rules: + all_pseudo_classes |= rule.pseudo_classes - cache_key: tuple | None - if cache is not None: + node._has_hover_style = "hover" in all_pseudo_classes + node._has_focus_within = "focus-within" in all_pseudo_classes + + cache_key: tuple | None = None + + if cache is not None and all_pseudo_classes.isdisjoint( + self.EXPENSIVE_PSEUDO_CLASSES + ): cache_key = ( node._parent, ( @@ -483,7 +498,7 @@ def apply( else (node._id if f"#{node._id}" in rules_map else None) ), node.classes, - node.pseudo_classes, + node._pseudo_classes_cache_key, node._css_type_name, ) cached_result: RulesMap | None = cache.get(cache_key) @@ -491,8 +506,6 @@ def apply( self.replace_rules(node, cached_result, animate=animate) self._process_component_classes(node) return - else: - cache_key = None _check_rule = self._check_rule css_path_nodes = node.css_path_nodes @@ -561,8 +574,7 @@ def apply( rule_value = getattr(_DEFAULT_STYLES, initial_rule_name) node_rules[initial_rule_name] = rule_value # type: ignore[literal-required] - if cache is not None: - assert cache_key is not None + if cache_key is not None: cache[cache_key] = node_rules self.replace_rules(node, node_rules, animate=animate) self._process_component_classes(node) diff --git a/src/textual/dom.py b/src/textual/dom.py index e215c5116f..79261bb0f3 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1685,6 +1685,20 @@ def has_pseudo_classes(self, class_names: set[str]) -> bool: for name in class_names ) + _CACHEABLE_PSEUDO_CLASSES = [ + "hover", + "focus", + "can-focus", + "disabled", + "dark", + "focus-within", + ] + + @property + def _pseudo_classes_cache_key(self) -> tuple[int, ...]: + """A cache key used when updating a number of nodes from the stylesheet.""" + return () + def refresh( self, *, repaint: bool = True, layout: bool = False, recompose: bool = False ) -> Self: diff --git a/src/textual/widget.py b/src/textual/widget.py index adb5b6497f..f6f05f84ba 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -3357,6 +3357,15 @@ def get_pseudo_class_state(self) -> PseudoClasses: ) return pseudo_classes + @property + def _pseudo_classes_cache_key(self) -> tuple[int, ...]: + """A cache key that changes when the pseudo-classes change.""" + return ( + self.mouse_hover, + self.has_focus, + self.is_disabled, + ) + def _get_rich_justify(self) -> JustifyMethod | None: """Get the justify method that may be passed to a Rich renderable.""" text_justify: JustifyMethod | None = None From 9cf56a94b6caa547d535153b07415309b91f1c69 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Oct 2024 19:31:00 +0100 Subject: [PATCH 038/104] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e34d524d4..5ca26cb4c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Added `background-tint` CSS rule https://github.com/Textualize/textual/pull/5117 -- Added first-of-type, last-of-type, odd, and even pseudo classes https://github.com/Textualize/textual/pull/5139 +- Added `:first-of-type`, `:last-of-type`, `:odd`, and `:even` pseudo classes https://github.com/Textualize/textual/pull/5139 ## [0.83.0] - 2024-10-10 From 9e0b047121bafc2d3b4333988cadeafeac62d705 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 11:16:00 +0100 Subject: [PATCH 039/104] snapshot test --- src/textual/app.py | 3 +- src/textual/css/constants.py | 4 + src/textual/css/stylesheet.py | 4 +- src/textual/dom.py | 2 + src/textual/widget.py | 10 +- .../test_snapshots/test_pseudo_classes.svg | 153 ++++++++++++++++++ tests/snapshot_tests/test_snapshots.py | 31 ++++ 7 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 tests/snapshot_tests/__snapshots__/test_snapshots/test_pseudo_classes.svg diff --git a/src/textual/app.py b/src/textual/app.py index e829110b60..bbb33b4e41 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -3153,7 +3153,8 @@ def _register( self._register_child(parent, widget, before, after) if widget._nodes: self._register(widget, *widget._nodes, cache=cache) - apply_stylesheet(widget, cache=cache) + for widget in widget_list: + apply_stylesheet(widget, cache=cache) if not self._running: # If the app is not running, prevent awaiting of the widget tasks diff --git a/src/textual/css/constants.py b/src/textual/css/constants.py index 026e78be45..82af4e4912 100644 --- a/src/textual/css/constants.py +++ b/src/textual/css/constants.py @@ -72,6 +72,10 @@ "inline", "light", "nocolor", + "first-of-type", + "last-of-type", + "odd", + "even", } VALID_OVERLAY: Final = {"none", "screen"} VALID_CONSTRAIN: Final = {"inflect", "inside", "none"} diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py index bf7bd35776..27f11508f9 100644 --- a/src/textual/css/stylesheet.py +++ b/src/textual/css/stylesheet.py @@ -439,7 +439,6 @@ def _check_rule( "last-of_type", "odd", "even", - "focus-within", } def apply( @@ -484,6 +483,9 @@ def apply( node._has_hover_style = "hover" in all_pseudo_classes node._has_focus_within = "focus-within" in all_pseudo_classes + node._has_order_style = not all_pseudo_classes.isdisjoint( + {"first-of-type", "last-of-type", "odd", "even"} + ) cache_key: tuple | None = None diff --git a/src/textual/dom.py b/src/textual/dom.py index 79261bb0f3..99c0c31fd6 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -220,6 +220,8 @@ def __init__( ) self._has_hover_style: bool = False self._has_focus_within: bool = False + self._has_order_style: bool = False + """The node has an ordered dependent pseudo-style (`:odd`, `:even`, `:first-of-type`, `:last-of-type`)""" self._reactive_connect: ( dict[str, tuple[MessagePump, Reactive[object] | object]] | None ) = None diff --git a/src/textual/widget.py b/src/textual/widget.py index f6f05f84ba..cf072cba6e 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -777,7 +777,7 @@ def last_of_type(self) -> bool: if parent._nodes._updates == self._last_of_type[0]: return self._last_of_type[1] widget_type = type(self) - for node in reversed(parent.children): + for node in reversed(parent._nodes): if isinstance(node, widget_type): self._last_of_type = (parent._nodes._updates, node is self) return self._last_of_type[1] @@ -1166,8 +1166,16 @@ def mount( parent, *widgets, before=insert_before, after=insert_after ) + def update_styles(children: Iterable[DOMNode]) -> None: + """Update order related CSS""" + for child in children: + if child._has_order_style: + child._update_styles() + + self.call_later(update_styles, list(self.children)) await_mount = AwaitMount(self, mounted) self.call_next(await_mount) + return await_mount def mount_all( diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_pseudo_classes.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_pseudo_classes.svg new file mode 100644 index 0000000000..013fc91c3f --- /dev/null +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_pseudo_classes.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PSApp + + + + + + + + + + ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +Item 1 + +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +Item 2 + + + +Item 3 + + + +Item 4 + + + +Item 5 + + + +┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +HELLO + +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + + diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 8ec68c0414..a9d3a9ec0b 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2374,3 +2374,34 @@ def compose(self) -> ComposeResult: yield Label("A margin of 4, should be 4 cells around the text") assert snap_compare(FRApp()) + + +def test_pseudo_classes(snap_compare): + """Test pseudo classes added in https://github.com/Textualize/textual/pull/5139 + + You should see 6 bars, with alternating green and red backgrounds. + + The first bar should have a red border. + + The last bar should have a green border. + + """ + + class PSApp(App): + CSS = """ + Label { width: 1fr; height: 1fr; } + Label:first-of-type { border:heavy red; } + Label:last-of-type { border:heavy green; } + Label:odd {background: $success 20%; } + Label:even {background: $error 20%; } + """ + + def compose(self) -> ComposeResult: + for item_number in range(5): + yield Label(f"Item {item_number+1}") + + def on_mount(self) -> None: + # Mounting a new widget should updated previous widgets, as the last of type has changed + self.mount(Label("HELLO")) + + assert snap_compare(PSApp()) From 794d230162bb0dd69d05fabe1b62adc523f5be19 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 11:19:20 +0100 Subject: [PATCH 040/104] optimization --- src/textual/css/stylesheet.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py index 27f11508f9..6ad76e44db 100644 --- a/src/textual/css/stylesheet.py +++ b/src/textual/css/stylesheet.py @@ -476,11 +476,7 @@ def apply( for rule in rules_map[name] } rules = list(filter(limit_rules.__contains__, reversed(self.rules))) - - all_pseudo_classes = set() - for rule in rules: - all_pseudo_classes |= rule.pseudo_classes - + all_pseudo_classes = set().union(*[rule.pseudo_classes for rule in rules]) node._has_hover_style = "hover" in all_pseudo_classes node._has_focus_within = "focus-within" in all_pseudo_classes node._has_order_style = not all_pseudo_classes.isdisjoint( From 78e387e036a492ef22e12dbf87d621ec4030f69c Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 11:23:07 +0100 Subject: [PATCH 041/104] remove data --- src/textual/dom.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/textual/dom.py b/src/textual/dom.py index 99c0c31fd6..a3cb616a82 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1687,15 +1687,6 @@ def has_pseudo_classes(self, class_names: set[str]) -> bool: for name in class_names ) - _CACHEABLE_PSEUDO_CLASSES = [ - "hover", - "focus", - "can-focus", - "disabled", - "dark", - "focus-within", - ] - @property def _pseudo_classes_cache_key(self) -> tuple[int, ...]: """A cache key used when updating a number of nodes from the stylesheet.""" From 2d922b5e678b0da80d0af6e15b74d08e08af143f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 11:30:54 +0100 Subject: [PATCH 042/104] simplify --- src/textual/dom.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/textual/dom.py b/src/textual/dom.py index a3cb616a82..b2490908db 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1681,9 +1681,14 @@ def has_pseudo_classes(self, class_names: set[str]) -> bool: Returns: `True` if all pseudo class names are present. """ - PSEUDO_CLASSES = self._PSEUDO_CLASSES + get_pseudo_class_callable = self._PSEUDO_CLASSES.get + + def missing_pseudo_class(node: DOMNode) -> bool: + """Callable when a pseudo class is missing.""" + return False + return all( - (name in PSEUDO_CLASSES and PSEUDO_CLASSES[name](self)) + get_pseudo_class_callable(name, missing_pseudo_class)(self) for name in class_names ) From f996e8308ae1a82b3d2754d95d3d07edf6936b31 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 11:34:24 +0100 Subject: [PATCH 043/104] apply styles to new widgets in one go --- src/textual/app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/textual/app.py b/src/textual/app.py index bbb33b4e41..e02da7e7e0 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -3143,6 +3143,8 @@ def _register( widget_list = widgets apply_stylesheet = self.stylesheet.apply + new_widgets: list[DOMNode] = [] + add_new_widget = new_widgets.append for widget in widget_list: widget._closing = False widget._closed = False @@ -3150,10 +3152,11 @@ def _register( if not isinstance(widget, Widget): raise AppError(f"Can't register {widget!r}; expected a Widget instance") if widget not in self._registry: + add_new_widget(widget) self._register_child(parent, widget, before, after) if widget._nodes: self._register(widget, *widget._nodes, cache=cache) - for widget in widget_list: + for widget in new_widgets: apply_stylesheet(widget, cache=cache) if not self._running: From 698fffead204b5992a18f354eee200cd51f660b5 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 11:37:10 +0100 Subject: [PATCH 044/104] typing tweak --- src/textual/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/textual/app.py b/src/textual/app.py index e02da7e7e0..386dce4ca0 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -3143,7 +3143,7 @@ def _register( widget_list = widgets apply_stylesheet = self.stylesheet.apply - new_widgets: list[DOMNode] = [] + new_widgets: list[Widget] = [] add_new_widget = new_widgets.append for widget in widget_list: widget._closing = False From 3f97a8a645fe74698a3ab86a316b018d8dab3d3a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 11:48:05 +0100 Subject: [PATCH 045/104] simplify --- src/textual/dom.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/textual/dom.py b/src/textual/dom.py index b2490908db..ecdd58d2b9 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1681,17 +1681,12 @@ def has_pseudo_classes(self, class_names: set[str]) -> bool: Returns: `True` if all pseudo class names are present. """ - get_pseudo_class_callable = self._PSEUDO_CLASSES.get - - def missing_pseudo_class(node: DOMNode) -> bool: - """Callable when a pseudo class is missing.""" + PSEUDO_CLASSES = self._PSEUDO_CLASSES + try: + return all(PSEUDO_CLASSES[name](self) for name in class_names) + except KeyError: return False - return all( - get_pseudo_class_callable(name, missing_pseudo_class)(self) - for name in class_names - ) - @property def _pseudo_classes_cache_key(self) -> tuple[int, ...]: """A cache key used when updating a number of nodes from the stylesheet.""" From be25255a69f3a61bb502f284edef0b0b04f0f653 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 11:49:54 +0100 Subject: [PATCH 046/104] simplify --- src/textual/dom.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/textual/dom.py b/src/textual/dom.py index ecdd58d2b9..ef23e9fdda 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1668,9 +1668,10 @@ def has_pseudo_class(self, class_name: str) -> bool: Returns: `True` if the DOM node has the pseudo class, `False` if not. """ - return class_name in self._PSEUDO_CLASSES and self._PSEUDO_CLASSES[class_name]( - self - ) + try: + return self._PSEUDO_CLASSES[class_name](self) + except KeyError: + return False def has_pseudo_classes(self, class_names: set[str]) -> bool: """Check the node has all the given pseudo classes. From f9aba5da970b7c9278330d1514611e1038778e01 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sat, 19 Oct 2024 19:31:27 +0100 Subject: [PATCH 047/104] universal selector now doesn't match scrollbars --- CHANGELOG.md | 1 + src/textual/css/model.py | 2 +- src/textual/css/parse.py | 1 - src/textual/css/stylesheet.py | 1 + src/textual/demo2/widgets.py | 7 ++++++- src/textual/scrollbar.py | 2 ++ 6 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5743d67b9..cb5dda1e16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Grid will now size children to the maximum height of a row - Markdown links will be opened with `App.open_url` automatically +- The universal selector (`*`) will now not match widgets with the class `-textual-system` (scrollbars, notifications etc) ### Added diff --git a/src/textual/css/model.py b/src/textual/css/model.py index e752b3fc1d..05c64d9a79 100644 --- a/src/textual/css/model.py +++ b/src/textual/css/model.py @@ -56,7 +56,7 @@ def _check_universal(name: str, node: DOMNode) -> bool: Returns: `True` if the selector matches. """ - return True + return not node.has_class("-textual-system") def _check_type(name: str, node: DOMNode) -> bool: diff --git a/src/textual/css/parse.py b/src/textual/css/parse.py index e7fd0c0353..d0365c825f 100644 --- a/src/textual/css/parse.py +++ b/src/textual/css/parse.py @@ -170,7 +170,6 @@ def parse_rule_set( while True: token = next(tokens) - token_name = token.name if token_name in ("whitespace", "declaration_end"): continue diff --git a/src/textual/css/stylesheet.py b/src/textual/css/stylesheet.py index 6ad76e44db..b366efe603 100644 --- a/src/textual/css/stylesheet.py +++ b/src/textual/css/stylesheet.py @@ -255,6 +255,7 @@ def _parse_rules( tie_breaker=tie_breaker, ) ) + except TokenError: raise except Exception as error: diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index c31686b85f..7f943be3dc 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -208,7 +208,12 @@ def compose(self) -> ComposeResult: class WidgetsScreen(PageScreen): CSS = """ WidgetsScreen { - align-horizontal: center; + align-horizontal: center; + & > VerticalScroll > * { + &:last-of-type { margin-bottom: 2; } + &:even { background: $boost; } + padding-bottom: 1; + } } """ diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index 79ee1c5d9e..78aa968d00 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -246,6 +246,8 @@ class MyScrollBarRender(ScrollBarRender): ... ``` """ + DEFAULT_CLASSES = "-textual-system" + def __init__( self, vertical: bool = True, name: str | None = None, *, thickness: int = 1 ) -> None: From 4afdb3f6d02416fcae0c28fcab8dd4ba583b74fa Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 20 Oct 2024 13:44:56 +0100 Subject: [PATCH 048/104] maor widgets --- src/textual/demo2/home.py | 11 ++++--- src/textual/demo2/widgets.py | 49 ++++++++++++++++++++++++++++++- src/textual/renderables/digits.py | 16 ++++++++-- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/textual/demo2/home.py b/src/textual/demo2/home.py index 989d62cf0a..53611c7aaf 100644 --- a/src/textual/demo2/home.py +++ b/src/textual/demo2/home.py @@ -192,10 +192,13 @@ class Content(VerticalScroll, can_focus=False): class HomeScreen(PageScreen): DEFAULT_CSS = """ HomeScreen { - align-horizontal: center; - Content { - margin: 0 1; - max-width: 100; + + Content { + align-horizontal: center; + & > * { + max-width: 100; + } + margin: 0 1; overflow-y: auto; height: 1fr; scrollbar-gutter: stable; diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index 7f943be3dc..8f71326f7d 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -7,11 +7,15 @@ Button, Checkbox, DataTable, + Digits, Footer, Input, Label, + ListItem, + ListView, Markdown, MaskedInput, + OptionList, RadioButton, RadioSet, ) @@ -176,7 +180,7 @@ class Inputs(containers.VerticalGroup): } Label { width: 100%; - margin: 1; + padding: 1; text-align: right; } } @@ -205,6 +209,48 @@ def compose(self) -> ComposeResult: ) +class ListViews(containers.VerticalGroup): + DEFAULT_CLASSES = "column" + LISTS_MD = """\ +## List Views and Option Lists + +A List View turns any widget in to a user-navigable and selectable list. +An Option List for a for field to present a list of strings to select from. + + """ + + DEFAULT_CSS = """\ + ListViews { + ListView { + width: 1fr; + height: auto; + margin: 0 2; + background: $panel; + } + OptionList { + max-height: 15; + } + + Digits { + padding: 1 2; + width: 1fr; + + } + } + + """ + + def compose(self) -> ComposeResult: + yield Markdown(self.LISTS_MD) + with containers.HorizontalGroup(): + yield ListView( + ListItem(Digits("$50.00")), + ListItem(Digits("$100.00")), + ListItem(Digits("$500.00")), + ) + yield OptionList(*COUNTRIES) + + class WidgetsScreen(PageScreen): CSS = """ WidgetsScreen { @@ -227,6 +273,7 @@ def compose(self) -> ComposeResult: yield Checkboxes() yield Datatables() yield Inputs() + yield ListViews() yield Footer() def action_unfocus(self) -> None: diff --git a/src/textual/renderables/digits.py b/src/textual/renderables/digits.py index 9fd2044404..8d7fb6fefb 100644 --- a/src/textual/renderables/digits.py +++ b/src/textual/renderables/digits.py @@ -5,7 +5,7 @@ from rich.segment import Segment from rich.style import Style, StyleType -DIGITS = " 0123456789+-^x:ABCDEF" +DIGITS = " 0123456789+-^x:ABCDEF$£" DIGITS3X3_BOLD = """\ @@ -73,7 +73,12 @@ ╭─╴ ├─ ╵ - +╭╫╴ +╰╫╮ +╶╫╯ +╭─╮ +┼─ +╰─╴ """.splitlines() @@ -144,7 +149,12 @@ ╭─╴ ├─ ╵ - +╭╫╴ +╰╫╮ +╶╫╯ +╭─╮ +┼─ +╰─╴ """.splitlines() From d0467589ef6014e844a60c60d9899fc20920e28f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 20 Oct 2024 14:09:52 +0100 Subject: [PATCH 049/104] change --- CHANGELOG.md | 1 + src/textual/demo2/widgets.py | 11 ++--------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb5dda1e16..fbb23a29eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `background-tint` CSS rule https://github.com/Textualize/textual/pull/5117 - Added `App.DEFAULT_MODE` - Added `Containers.HorizontalGroup` and `Containers.VerticalGroup` +- Added `$` and `£` symbols to Digits ### Fixed diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index 8f71326f7d..94ff42031f 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -227,15 +227,8 @@ class ListViews(containers.VerticalGroup): margin: 0 2; background: $panel; } - OptionList { - max-height: 15; - } - - Digits { - padding: 1 2; - width: 1fr; - - } + OptionList { max-height: 15; } + Digits { padding: 1 2; width: 1fr; } } """ From 4f9ad305084d5c3b0e6a19001bb939079cc00eff Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 20 Oct 2024 17:03:32 +0100 Subject: [PATCH 050/104] updated digits --- CHANGELOG.md | 2 +- src/textual/demo2/widgets.py | 4 ++-- src/textual/renderables/digits.py | 16 +++++++++++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbb23a29eb..818a205e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `background-tint` CSS rule https://github.com/Textualize/textual/pull/5117 - Added `App.DEFAULT_MODE` - Added `Containers.HorizontalGroup` and `Containers.VerticalGroup` -- Added `$` and `£` symbols to Digits +- Added `$`, `£`, `€` symbols to Digits ### Fixed diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index 94ff42031f..2d6e29b05e 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -238,8 +238,8 @@ def compose(self) -> ComposeResult: with containers.HorizontalGroup(): yield ListView( ListItem(Digits("$50.00")), - ListItem(Digits("$100.00")), - ListItem(Digits("$500.00")), + ListItem(Digits("£100.00")), + ListItem(Digits("€500.00")), ) yield OptionList(*COUNTRIES) diff --git a/src/textual/renderables/digits.py b/src/textual/renderables/digits.py index 8d7fb6fefb..dcd18ff959 100644 --- a/src/textual/renderables/digits.py +++ b/src/textual/renderables/digits.py @@ -5,7 +5,7 @@ from rich.segment import Segment from rich.style import Style, StyleType -DIGITS = " 0123456789+-^x:ABCDEF$£" +DIGITS = " 0123456789+-^x:ABCDEF$£€" DIGITS3X3_BOLD = """\ @@ -77,8 +77,11 @@ ╰╫╮ ╶╫╯ ╭─╮ -┼─ -╰─╴ +╪═ +└─╴ +╭─╮ +╪═ +╰─╯ """.splitlines() @@ -153,8 +156,11 @@ ╰╫╮ ╶╫╯ ╭─╮ -┼─ -╰─╴ +╪═ +└─╴ +╭─╮ +╪═ +╰─╯ """.splitlines() From 0ffcd6fb82429269965ae88abcf9ed6f1f802feb Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 20 Oct 2024 17:28:29 +0100 Subject: [PATCH 051/104] digit tweaks --- src/textual/demo2/widgets.py | 3 +-- src/textual/renderables/digits.py | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index 2d6e29b05e..6a1eded388 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -260,8 +260,7 @@ class WidgetsScreen(PageScreen): def compose(self) -> ComposeResult: with containers.VerticalScroll(): - with containers.Center(classes="column"): - yield Markdown(WIDGETS_MD) + yield Markdown(WIDGETS_MD, classes="column") yield Buttons() yield Checkboxes() yield Datatables() diff --git a/src/textual/renderables/digits.py b/src/textual/renderables/digits.py index dcd18ff959..0285e30f3e 100644 --- a/src/textual/renderables/digits.py +++ b/src/textual/renderables/digits.py @@ -173,6 +173,8 @@ class Digits: """ + REPLACEMENTS = str.maketrans({".": "•"}) + def __init__(self, text: str, style: StyleType = "") -> None: self._text = text self._style = style @@ -202,7 +204,7 @@ def render(self, style: Style) -> RenderResult: else: digits = DIGITS3X3 - for character in self._text: + for character in self._text.translate(self.REPLACEMENTS): try: position = DIGITS.index(character) * 3 except ValueError: From 6c63918dbdd0eed521080f814909294f16aa878f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Sun, 20 Oct 2024 20:12:23 +0100 Subject: [PATCH 052/104] currency tweaks --- src/textual/renderables/digits.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/textual/renderables/digits.py b/src/textual/renderables/digits.py index 0285e30f3e..f751387cf9 100644 --- a/src/textual/renderables/digits.py +++ b/src/textual/renderables/digits.py @@ -73,12 +73,12 @@ ╭─╴ ├─ ╵ -╭╫╴ +╭╫╮ ╰╫╮ -╶╫╯ +╰╫╯ ╭─╮ ╪═ -└─╴ +┴─╴ ╭─╮ ╪═ ╰─╯ @@ -152,12 +152,12 @@ ╭─╴ ├─ ╵ -╭╫╴ +╭╫╮ ╰╫╮ -╶╫╯ +╰╫╯ ╭─╮ ╪═ -└─╴ +┴─╴ ╭─╮ ╪═ ╰─╯ From 55f267d7b48e83d7fbd0d8f27591ef6df167ea5d Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 12:14:33 +0100 Subject: [PATCH 053/104] fix infinite loop in cropping --- src/textual/strip.py | 3 +++ src/textual/widgets/_text_area.py | 4 +--- tests/test_strip.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/textual/strip.py b/src/textual/strip.py index 71bfd78089..c7644898f8 100644 --- a/src/textual/strip.py +++ b/src/textual/strip.py @@ -372,10 +372,13 @@ def crop(self, start: int, end: int | None = None) -> Strip: Returns: A new Strip. """ + start = max(0, start) end = self.cell_length if end is None else min(self.cell_length, end) if start == 0 and end == self.cell_length: return self + if end <= start: + return Strip([], 0) cache_key = (start, end) cached = self._crop_cache.get(cache_key) if cached is not None: diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index b147c27ee5..0e7da87543 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -1236,9 +1236,7 @@ def render_line(self, y: int) -> Strip: # Crop the line to show only the visible part (some may be scrolled out of view) if not self.soft_wrap: - text_strip = text_strip.crop( - scroll_x, scroll_x + virtual_width - gutter_width - ) + text_strip = text_strip.crop(scroll_x, scroll_x + virtual_width) # Stylize the line the cursor is currently on. if cursor_row == line_index: diff --git a/tests/test_strip.py b/tests/test_strip.py index 7f6d9bae63..b88c68ecc5 100644 --- a/tests/test_strip.py +++ b/tests/test_strip.py @@ -131,7 +131,7 @@ def test_crop(): assert Strip([Segment("foo")]).crop(1, 3) == Strip([Segment("oo")]) assert Strip([Segment("foo")]).crop(1, 2) == Strip([Segment("o")]) - assert Strip([Segment("foo")]).crop(1, 1) == Strip([Segment("")]) + assert Strip([Segment("foo")]).crop(1, 1) == Strip([]) assert Strip([Segment("foo💩"), Segment("b💩ar"), Segment("ba💩z")]).crop( 1, 6 From a441aed1a66ddbde059a5d8d8da4c4cf5071f578 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:01:10 +0100 Subject: [PATCH 054/104] version bump --- CHANGELOG.md | 4 +++- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ca26cb4c1..65c16656e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,12 @@ 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/). -## Unreleased +## [0.84.0] ### Fixed - Fixed `RadioSet` not being scrollable https://github.com/Textualize/textual/issues/5100 +- Fixed infinite loop in TextArea https://github.com/Textualize/textual/pull/5154 ### Added @@ -2453,6 +2454,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040 - New handler system for messages that doesn't require inheritance - Improved traceback handling +[0.84.0]: https://github.com/Textualize/textual/compare/v0.83.0...v0.84.0 [0.83.0]: https://github.com/Textualize/textual/compare/v0.82.0...v0.83.0 [0.82.0]: https://github.com/Textualize/textual/compare/v0.81.0...v0.82.0 [0.81.0]: https://github.com/Textualize/textual/compare/v0.80.1...v0.81.0 diff --git a/pyproject.toml b/pyproject.toml index 3b88d9a1a6..ea2cf45af2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual" -version = "0.83.0" +version = "0.84.0" homepage = "https://github.com/Textualize/textual" repository = "https://github.com/Textualize/textual" documentation = "https://textual.textualize.io/" From f627fb1964258ce634d249c606a6253bcd0e76bc Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:02:53 +0100 Subject: [PATCH 055/104] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65c16656e1..1a7487fe28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ 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.84.0] +## [0.84.0] - 2024-10-22 ### Fixed From 8633bd27f9344cc62af89531372d0d8a890015b0 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:09:15 +0100 Subject: [PATCH 056/104] depenencies --- poetry.lock | 1401 +++++++++++++++++++++++++++++---------------------- 1 file changed, 785 insertions(+), 616 deletions(-) diff --git a/poetry.lock b/poetry.lock index a8e6709daf..d971d5cc4b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,114 +1,114 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" -version = "2.4.0" +version = "2.4.3" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"}, - {file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"}, + {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, + {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, ] [[package]] name = "aiohttp" -version = "3.10.5" +version = "3.10.10" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, - {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, - {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, - {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, - {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, - {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, - {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, - {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, - {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"}, - {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"}, - {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, - {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, - {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, - {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68"}, + {file = "aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257"}, + {file = "aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a"}, + {file = "aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94"}, + {file = "aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205"}, + {file = "aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628"}, + {file = "aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b"}, + {file = "aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8"}, + {file = "aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b"}, + {file = "aiohttp-3.10.10-cp38-cp38-win32.whl", hash = "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c"}, + {file = "aiohttp-3.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91"}, + {file = "aiohttp-3.10.10-cp39-cp39-win32.whl", hash = "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983"}, + {file = "aiohttp-3.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23"}, + {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"}, ] [package.dependencies] @@ -118,7 +118,7 @@ async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" +yarl = ">=1.12.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] @@ -154,13 +154,13 @@ frozenlist = ">=1.1.0" [[package]] name = "anyio" -version = "4.4.0" +version = "4.5.2" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, + {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, ] [package.dependencies] @@ -170,9 +170,9 @@ sniffio = ">=1.1" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "async-timeout" @@ -312,101 +312,116 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -523,13 +538,13 @@ toml = ["tomli"] [[package]] name = "distlib" -version = "0.3.8" +version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] @@ -562,18 +577,18 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "filelock" -version = "3.16.0" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] @@ -782,13 +797,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "identify" -version = "2.6.0" +version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] [package.extras] @@ -796,33 +811,40 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.8" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ - {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, - {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "importlib-metadata" -version = "8.4.0" +version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, - {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [package.dependencies] -zipp = ">=0.5" +zipp = ">=3.20" [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] [[package]] name = "iniconfig" @@ -1001,13 +1023,13 @@ files = [ [[package]] name = "mdit-py-plugins" -version = "0.4.1" +version = "0.4.2" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ - {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, - {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, + {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, + {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, ] [package.dependencies] @@ -1119,13 +1141,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-git-revision-date-localized-plugin" -version = "1.2.8" +version = "1.2.9" description = "Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_git_revision_date_localized_plugin-1.2.8-py3-none-any.whl", hash = "sha256:c7ec3b1481ca23134269e84927bd8a5dc1aa359c0e515b832dbd5d25019b5748"}, - {file = "mkdocs_git_revision_date_localized_plugin-1.2.8.tar.gz", hash = "sha256:6e09c308bb27bcf36b211d17b74152ecc2837cdfc351237f70cffc723ef0fd99"}, + {file = "mkdocs_git_revision_date_localized_plugin-1.2.9-py3-none-any.whl", hash = "sha256:dea5c8067c23df30275702a1708885500fadf0abfb595b60e698bffc79c7a423"}, + {file = "mkdocs_git_revision_date_localized_plugin-1.2.9.tar.gz", hash = "sha256:df9a50873fba3a42ce9123885f8c53d589e90ef6c2443fe3280ef1e8d33c8f65"}, ] [package.dependencies] @@ -1141,13 +1163,13 @@ dev = ["click", "codecov", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkd [[package]] name = "mkdocs-material" -version = "9.5.34" +version = "9.5.42" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.34-py3-none-any.whl", hash = "sha256:54caa8be708de2b75167fd4d3b9f3d949579294f49cb242515d4653dbee9227e"}, - {file = "mkdocs_material-9.5.34.tar.gz", hash = "sha256:1e60ddf716cfb5679dfd65900b8a25d277064ed82d9a53cd5190e3f894df7840"}, + {file = "mkdocs_material-9.5.42-py3-none-any.whl", hash = "sha256:452a7c5d21284b373f36b981a2cbebfff59263feebeede1bc28652e9c5bbe316"}, + {file = "mkdocs_material-9.5.42.tar.gz", hash = "sha256:92779b5e9b5934540c574c11647131d217dc540dce72b05feeda088c8eb1b8f2"}, ] [package.dependencies] @@ -1245,202 +1267,220 @@ mkdocstrings = ">=0.20" [[package]] name = "msgpack" -version = "1.0.8" +version = "1.1.0" description = "MessagePack serializer" optional = false python-versions = ">=3.8" files = [ - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, - {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, - {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, - {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, - {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, - {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, - {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, - {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, - {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, - {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, - {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, + {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, + {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, + {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, + {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, + {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, + {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, + {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, + {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, + {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, + {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, + {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, + {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, + {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, ] [[package]] name = "multidict" -version = "6.0.5" +version = "6.1.0" description = "multidict implementation" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + [[package]] name = "mypy" -version = "1.11.2" +version = "1.12.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, - {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, - {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, - {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, - {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, - {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, - {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, - {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, - {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, - {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, - {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, - {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, - {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, - {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, - {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, - {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, - {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, - {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, - {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, + {file = "mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801"}, + {file = "mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5"}, + {file = "mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1"}, + {file = "mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627"}, + {file = "mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20"}, + {file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"}, + {file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"}, + {file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"}, + {file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"}, + {file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"}, + {file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"}, + {file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"}, + {file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"}, + {file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"}, + {file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"}, + {file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"}, + {file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"}, + {file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"}, + {file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"}, + {file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"}, + {file = "mypy-1.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b16fe09f9c741d85a2e3b14a5257a27a4f4886c171d562bc5a5e90d8591906b8"}, + {file = "mypy-1.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0dcc1e843d58f444fce19da4cce5bd35c282d4bde232acdeca8279523087088a"}, + {file = "mypy-1.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10ba7de5c616e44ad21005fa13450cd0de7caaa303a626147d45307492e4f2d"}, + {file = "mypy-1.12.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e6fe449223fa59fbee351db32283838a8fee8059e0028e9e6494a03802b4004"}, + {file = "mypy-1.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:dc6e2a2195a290a7fd5bac3e60b586d77fc88e986eba7feced8b778c373f9afe"}, + {file = "mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19"}, + {file = "mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e"}, + {file = "mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d"}, + {file = "mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd"}, + {file = "mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810"}, + {file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"}, + {file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"}, ] [package.dependencies] @@ -1515,13 +1555,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.3.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, - {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] @@ -1562,6 +1602,113 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "propcache" +version = "0.2.0" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.8" +files = [ + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, + {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, + {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, + {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, + {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, + {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, + {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, + {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, + {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, + {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, + {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, + {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, + {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, + {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, + {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, +] + [[package]] name = "pygments" version = "2.18.0" @@ -1578,13 +1725,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.9" +version = "10.11.2" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, - {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, + {file = "pymdown_extensions-10.11.2-py3-none-any.whl", hash = "sha256:41cdde0a77290e480cf53892f5c5e50921a7ee3e5cd60ba91bf19837b33badcf"}, + {file = "pymdown_extensions-10.11.2.tar.gz", hash = "sha256:bc8847ecc9e784a098efd35e20cba772bc5a1b529dfcef9dc1972db9021a1049"}, ] [package.dependencies] @@ -1596,13 +1743,13 @@ extra = ["pygments (>=2.12)"] [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] @@ -1706,13 +1853,13 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2024.1" +version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] [[package]] @@ -1793,90 +1940,105 @@ pyyaml = "*" [[package]] name = "regex" -version = "2024.7.24" +version = "2024.9.11" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b0d3f567fafa0633aee87f08b9276c7062da9616931382993c03808bb68ce"}, - {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3426de3b91d1bc73249042742f45c2148803c111d1175b283270177fdf669024"}, - {file = "regex-2024.7.24-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f273674b445bcb6e4409bf8d1be67bc4b58e8b46fd0d560055d515b8830063cd"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23acc72f0f4e1a9e6e9843d6328177ae3074b4182167e34119ec7233dfeccf53"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65fd3d2e228cae024c411c5ccdffae4c315271eee4a8b839291f84f796b34eca"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c414cbda77dbf13c3bc88b073a1a9f375c7b0cb5e115e15d4b73ec3a2fbc6f59"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7a89eef64b5455835f5ed30254ec19bf41f7541cd94f266ab7cbd463f00c41"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19c65b00d42804e3fbea9708f0937d157e53429a39b7c61253ff15670ff62cb5"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7a5486ca56c8869070a966321d5ab416ff0f83f30e0e2da1ab48815c8d165d46"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f51f9556785e5a203713f5efd9c085b4a45aecd2a42573e2b5041881b588d1f"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a4997716674d36a82eab3e86f8fa77080a5d8d96a389a61ea1d0e3a94a582cf7"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c0abb5e4e8ce71a61d9446040c1e86d4e6d23f9097275c5bd49ed978755ff0fe"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:18300a1d78cf1290fa583cd8b7cde26ecb73e9f5916690cf9d42de569c89b1ce"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:416c0e4f56308f34cdb18c3f59849479dde5b19febdcd6e6fa4d04b6c31c9faa"}, - {file = "regex-2024.7.24-cp310-cp310-win32.whl", hash = "sha256:fb168b5924bef397b5ba13aabd8cf5df7d3d93f10218d7b925e360d436863f66"}, - {file = "regex-2024.7.24-cp310-cp310-win_amd64.whl", hash = "sha256:6b9fc7e9cc983e75e2518496ba1afc524227c163e43d706688a6bb9eca41617e"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e"}, - {file = "regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c"}, - {file = "regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38"}, - {file = "regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc"}, - {file = "regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:66b4c0731a5c81921e938dcf1a88e978264e26e6ac4ec96a4d21ae0354581ae0"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:88ecc3afd7e776967fa16c80f974cb79399ee8dc6c96423321d6f7d4b881c92b"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64bd50cf16bcc54b274e20235bf8edbb64184a30e1e53873ff8d444e7ac656b2"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb462f0e346fcf41a901a126b50f8781e9a474d3927930f3490f38a6e73b6950"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a82465ebbc9b1c5c50738536fdfa7cab639a261a99b469c9d4c7dcbb2b3f1e57"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68a8f8c046c6466ac61a36b65bb2395c74451df2ffb8458492ef49900efed293"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac8e84fff5d27420f3c1e879ce9929108e873667ec87e0c8eeb413a5311adfe"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba2537ef2163db9e6ccdbeb6f6424282ae4dea43177402152c67ef869cf3978b"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:43affe33137fcd679bdae93fb25924979517e011f9dea99163f80b82eadc7e53"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c9bb87fdf2ab2370f21e4d5636e5317775e5d51ff32ebff2cf389f71b9b13750"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:945352286a541406f99b2655c973852da7911b3f4264e010218bbc1cc73168f2"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8bc593dcce679206b60a538c302d03c29b18e3d862609317cb560e18b66d10cf"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3f3b6ca8eae6d6c75a6cff525c8530c60e909a71a15e1b731723233331de4169"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c51edc3541e11fbe83f0c4d9412ef6c79f664a3745fab261457e84465ec9d5a8"}, - {file = "regex-2024.7.24-cp38-cp38-win32.whl", hash = "sha256:d0a07763776188b4db4c9c7fb1b8c494049f84659bb387b71c73bbc07f189e96"}, - {file = "regex-2024.7.24-cp38-cp38-win_amd64.whl", hash = "sha256:8fd5afd101dcf86a270d254364e0e8dddedebe6bd1ab9d5f732f274fa00499a5"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0ffe3f9d430cd37d8fa5632ff6fb36d5b24818c5c986893063b4e5bdb84cdf24"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25419b70ba00a16abc90ee5fce061228206173231f004437730b67ac77323f0d"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33e2614a7ce627f0cdf2ad104797d1f68342d967de3695678c0cb84f530709f8"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d33a0021893ede5969876052796165bab6006559ab845fd7b515a30abdd990dc"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04ce29e2c5fedf296b1a1b0acc1724ba93a36fb14031f3abfb7abda2806c1535"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b16582783f44fbca6fcf46f61347340c787d7530d88b4d590a397a47583f31dd"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:836d3cc225b3e8a943d0b02633fb2f28a66e281290302a79df0e1eaa984ff7c1"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:438d9f0f4bc64e8dea78274caa5af971ceff0f8771e1a2333620969936ba10be"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:973335b1624859cb0e52f96062a28aa18f3a5fc77a96e4a3d6d76e29811a0e6e"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c5e69fd3eb0b409432b537fe3c6f44ac089c458ab6b78dcec14478422879ec5f"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fbf8c2f00904eaf63ff37718eb13acf8e178cb940520e47b2f05027f5bb34ce3"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2757ace61bc4061b69af19e4689fa4416e1a04840f33b441034202b5cd02d4"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:44fc61b99035fd9b3b9453f1713234e5a7c92a04f3577252b45feefe1b327759"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:84c312cdf839e8b579f504afcd7b65f35d60b6285d892b19adea16355e8343c9"}, - {file = "regex-2024.7.24-cp39-cp39-win32.whl", hash = "sha256:ca5b2028c2f7af4e13fb9fc29b28d0ce767c38c7facdf64f6c2cd040413055f1"}, - {file = "regex-2024.7.24-cp39-cp39-win_amd64.whl", hash = "sha256:7c479f5ae937ec9985ecaf42e2e10631551d909f203e31308c12d703922742f9"}, - {file = "regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, + {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, + {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, ] [[package]] @@ -1919,37 +2081,37 @@ idna2008 = ["idna"] [[package]] name = "rich" -version = "13.8.0" +version = "13.9.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" files = [ - {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, - {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, + {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, + {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "setuptools" -version = "74.1.2" +version = "75.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-74.1.2-py3-none-any.whl", hash = "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308"}, - {file = "setuptools-74.1.2.tar.gz", hash = "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6"}, + {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, + {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, ] [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] @@ -1991,13 +2153,13 @@ files = [ [[package]] name = "syrupy" -version = "4.7.1" +version = "4.7.2" description = "Pytest Snapshot Test Utility" optional = false python-versions = ">=3.8.1" files = [ - {file = "syrupy-4.7.1-py3-none-any.whl", hash = "sha256:be002267a512a4bedddfae2e026c93df1ea928ae10baadc09640516923376d41"}, - {file = "syrupy-4.7.1.tar.gz", hash = "sha256:f9d4485f3f27d0e5df6ed299cac6fa32eb40a441915d988e82be5a4bdda335c8"}, + {file = "syrupy-4.7.2-py3-none-any.whl", hash = "sha256:eae7ba6be5aed190237caa93be288e97ca1eec5ca58760e4818972a10c4acc64"}, + {file = "syrupy-4.7.2.tar.gz", hash = "sha256:ea45e099f242de1bb53018c238f408a5bb6c82007bc687aefcbeaa0e1c2e935a"}, ] [package.dependencies] @@ -2024,13 +2186,13 @@ typing-extensions = ">=4.4.0,<5.0.0" [[package]] name = "textual-serve" -version = "1.1.0" +version = "1.1.1" description = "Turn your Textual TUIs in to web applications" optional = false python-versions = ">=3.8" files = [ - {file = "textual_serve-1.1.0-py3-none-any.whl", hash = "sha256:aaa40bc4b75f6a1e73a2d7f6e667f5555a552d8040129a95e52789a3de5d9154"}, - {file = "textual_serve-1.1.0.tar.gz", hash = "sha256:9332716464a1fca38bb5421ef29e38bdc5d304e5a15faa3ae1f8dcb185a7885c"}, + {file = "textual_serve-1.1.1-py3-none-any.whl", hash = "sha256:568782f1c0e60e3f7039d9121e1cb5c2f4ca1aaf6d6bd7aeb833d5763a534cb2"}, + {file = "textual_serve-1.1.1.tar.gz", hash = "sha256:71c662472c462e5e368defc660ee6e8eae3bfda88ca40c050c55474686eb0c54"}, ] [package.dependencies] @@ -2042,13 +2204,13 @@ textual = ">=0.66.0" [[package]] name = "tomli" -version = "2.0.1" +version = "2.0.2" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] [[package]] @@ -2259,13 +2421,13 @@ files = [ [[package]] name = "tzdata" -version = "2024.1" +version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] [[package]] @@ -2284,13 +2446,13 @@ test = ["coverage", "pytest", "pytest-cov"] [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -2301,13 +2463,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.4" +version = "20.27.0" description = "Virtual Python Environment builder" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, - {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, + {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, + {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, ] [package.dependencies] @@ -2368,118 +2530,125 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "yarl" -version = "1.11.0" +version = "1.15.2" description = "Yet another URL library" optional = false python-versions = ">=3.8" files = [ - {file = "yarl-1.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0a657db1b9982f3dac0e360614d0e8945d2873da6e681fb7fca23ef1c3eb37f8"}, - {file = "yarl-1.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65a1a05efca52b102691e64db5fcf973030a1c88fee393804ff91f99c95a6e74"}, - {file = "yarl-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f4cb417d380e2d77961eecec75aaaf6f7ab14e6de26eb3a498f498029a6556a1"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8aee7c8378c6aa3103b99d1eb9995268ef730fa9f88ea68b9eee4341e204eec9"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84624db40e2358cfd5cf2558b1aaffd93366d27ee32228a97785f2ec87d44a17"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a596bb15e036952549871a4ccd2205679902dc7f241e3ced6b2ab2e44c55795"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d4d2cc4b076c8ad0175a15ee9482a387b3303c97d4b71062db7356b2ac04c7"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f8bc849004122591104793a576e9c747b0e5d9486d6a30225521b817255748"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e38176a559edde0cfff4b663791a007a5f9f90c73aee1d6f7ddbcf6bfb7287b3"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:706ac0f77b45e9e0278ec6c98929764e119d3ce3136792b6475e7ae961da53ec"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:48bac099586cf75ae5837b0ac17a674450d01f451f38afcb02acfc940110b60b"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:540fd5f62fe21f3d1d9efe8af5c4d9dbbb184ce03ce95acb0289500e46215dd2"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05ab59db0bb64e847972373c5cda8924e6605480f6b13cc04573fa0d87bfc637"}, - {file = "yarl-1.11.0-cp310-cp310-win32.whl", hash = "sha256:ddab47748933ac9cf5f29d6e9e2e2060cff40b2751d02c55129661ea4e577152"}, - {file = "yarl-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:976d02274e6d88b24c7131e7b26a083412b2592f2bbcef53d3b00b2508cad26c"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:39e3087e1ef70862de81e22af9eb299faee580f41673ef92829949022791b521"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7fd535cc41b81a566ad347081b671ab5c7e5f5b6a15526d85b4e748baf065cf0"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f7cc02d8e9a612174869f4b983f159e87659096f7e2dc1fe9effd9902e408739"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30f391ccf4b1b1e0ba4880075ba337d41a619a5350f67053927f67ebe764bf44"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c19a0d95943bb2c914b4e71043803be34bc75c08c4a6ca232bdc649a1e9ef1b"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ead4d89eade0e09b8ef97877664abb0e2e8704787db5564f83658fdee5c36497"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:195f7791bc23d5f2480efe53f935daf8a61661000dfbfbdd70dbd06397594fff"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01a7905e662665ca8e058635377522bc3c98bdb873be761ff42c86eb72b03914"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:53c80b1927b75aed208d7fd965a3a705dc8c1db4d50b9112418fa0f7784363e6"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:11af21bbf807688d49b7d4915bb28cbc2e3aa028a2ee194738477eabcc413c65"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:732d56da00ea7a5da4f0d15adbbd22dcb37da7825510aafde40112e53f6baa52"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7bd54d79025b59d1dc5fb26a09734d6a9cc651a04bc381966ed264b28331a168"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aacd62ff67efd54cb18cea2aa7ae4fb83cfbca19a07055d4777266b70561defe"}, - {file = "yarl-1.11.0-cp311-cp311-win32.whl", hash = "sha256:68e14ae71e5b51c8282ae5db53ccb3baffc40e1551370a8a2361f1c1d8a0bf8c"}, - {file = "yarl-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:3ade2265716667b6bd4123d6f684b5f7cf4a8d83dcf1d5581ac44643466bb00a"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6e73dab98e3c3b5441720153e72a5f28e717aac2d22f1ec4b08ef33417d9987e"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4a0d090d296ced05edfe29c6ff34869412fa6a97d0928c12b00939c4842884cd"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d29e446cfb0a82d3df7745968b9fa286665a9be8b4d68de46bcc32d917cb218e"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8dc0efcf8266ecfe057b95e01f43eb62516196a4bbf3918fd1dcb8d0dc0dff"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:202f5ec49ff163dcc767426deb55020a28078e61d6bbe1f80331d92bca53b236"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8055b0d78ce1cafa657c4b455e22661e8d3b2834de66a0753c3567da47fcc4aa"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60ed3c7f64e820959d7f682ec2f559b4f4df723dc09df619d269853a4214a4b4"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2371510367d39d74997acfdcd1dead17938c79c99365482821627f7838a8eba0"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e24bb6a8be89ccc3ce8c47e8940fdfcb7429e9efbf65ce6fa3e7d122fcf0bcf0"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:18ec42da256cfcb9b4cd5d253e04c291f69911a5228d1438a7d431c15ba0ae40"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:418eeb8f228ea36c368bf6782ebd6016ecebfb1a8b90145ef6726ffcbba65ef8"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:07e8cfb1dd7669a129f8fd5df1da65efa73aea77582bde2a3a837412e2863543"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c458483711d393dad51340505c3fab3194748fd06bab311d2f8b5b7a7349e9a"}, - {file = "yarl-1.11.0-cp312-cp312-win32.whl", hash = "sha256:5b008c3127382503e7a1e12b4c3a3236e3dd833a4c62a066f4a0fbd650c655d2"}, - {file = "yarl-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc94be7472b9f88d7441340534a3ecae05c86ccfec7ba75ce5b6e4778b2bfc6e"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a45e51ba3777031e0b20c1e7ab59114ed4e1884b3c1db48962c1d8d08aefb418"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:765128029218eade3a01187cdd7f375977cc827505ed31828196c8ae9b622928"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2516e238daf0339c8ac4dfab9d7cda9afad652ff073517f200d653d5d8371f7e"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d10be62bee117f05b1ad75a6c2538ca9e5367342dc8a4f3c206c87dadbc1189c"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50ceaeda771ee3e382291168c90c7ede62b63ecf3e181024bcfeb35c0ea6c84f"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a601c99fc20fd0eea84e7bc0dc9e7f196f55a0ded67242d724988c754295538"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ff79371614764fc0a4ab8eaba9adb493bf9ad856e2a4664f6c754fc907a903"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93fca4c9f88c17ead902b3f3285b2d039fc8f26d117e1441973ba64315109b54"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e7dddf5f41395c84fc59e0ed5493b24bfeb39fb04823e880b52c8c55085d4695"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ea501ea07e14ba6364ff2621bfc8b2381e5b1e10353927fa9a607057fd2b98e5"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a4f7e470f2c9c8b8774a5bda72adfb8e9dc4ec32311fe9bdaa4921e36cf6659b"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:361fdb3993431157302b7104d525092b5df4d7d346df5a5ffeee2d1ca8e0d15b"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e300eaf5e0329ad31b3d53e2f3d26b4b6dff1217207c6ab1d4212967b54b2185"}, - {file = "yarl-1.11.0-cp313-cp313-win32.whl", hash = "sha256:f1e2d4ce72e06e38a16da3e9c24a0520dbc19018a69ef6ed57b6b38527cb275c"}, - {file = "yarl-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:fa9de2f87be58f714a230bd1f3ef3aad1ed65c9931146e3fc55f85fcbe6bacc3"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:24da0b38274727fe9266d09229987e7f0efdb97beb94c0bb2d327d65f112e78d"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0310eb2e63872de66047e05ad9982f2e53ad6405dc42fa60d7cc670bf6ca8aa8"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:52433604340a4ab3d1f32281c6eb9ad9b47c99435b4212f763121bf7348c8c00"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98e2eb182d59f0845a79434003f94b4f61cd69465248f9388c2e5bf2191c9f7f"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dd10f0fe0e0f659926c1da791de5bef05fd48974ad74618c9168e302e2b7cc"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:121d3798e4bb35a4321b2422cb887f80ea39f94bf52f0eb5cb2c168bb0043c9b"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8bbac56c80610dd659ace534765d7bcd2488f6600023f6984f35108b2b3f4f0"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79d420399f0e82e302236a762d8b8ceec89761ce3b30c83ac1d4d6e29f811444"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a726fb50588307dfe1d233b67535d493fb0bb157bdbfda6bb34e04189f2f57"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9057f5de2fade7440e6db358913bc7ae8de43ba72c83cf95420a1fc1a6c6b59e"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:6471d747d0ac8059895e66d32ca8630c8db5b572ca7763150d0927eaa257df67"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:d97cb22ad380850754fa16ef8d490d9340d8573d81f73429f3975e8e87db0586"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fe78dec8caeda1e7b353cbd8aa0cc5a5bc182b22998d64ec8fa9ee59c898ab3b"}, - {file = "yarl-1.11.0-cp38-cp38-win32.whl", hash = "sha256:7ff371002fbbb79613269d76a2932c99979dac15fac30107064ef70d25f35474"}, - {file = "yarl-1.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:4fa9d762eee63eed767895d68b994c58e29f809292a4d0fca483e9cc6fdc22c8"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4ae63bc65e5bf8843bd1eca46e75eaa9eb157e0312fb362123181512892daad8"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d1bd3262e00043907e0a6d7d4f7b7a4815281acc25699a2384552870c79f1f0"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0c58656c2e0b41b5d325130b8da4f8e216aad10029e7de5c523a6be25faa9fe8"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9425c333575fce5e0fb414b766492c6ba4aa335ef910a7540dbdefe58a78232e"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dc66e2420e1e282105071934883bbb9c37c16901b5b8aa0a8aee370b477eac6"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2949067359d1ef5bf3228c7f1deb102c209832a13df5419239f99449bc1d3fa9"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006fe73f851cf20b9986b3b4cc15239795bd5da9c3fda76bb3e043da5bec4ff"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969ad4ee3892e893471b6572bbf2bbb091f93e7c81de25d6b3a5c0a5126e5ccb"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c9fbe9dc6ee8bfe1af34137e3add6f0e49799dd5467dd6af189d27616879161e"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69a45c711fea9b783b592a75f26f6dc59b2e4a923b97bf6eec357566fcb1d922"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1a29b82c42a7791ffe53ee6dfbf29acc61ea7ec05643dcacc50510ed6187b897"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ed0c090f00c3fc024f7b0799cab9dd7c419fcd8f1a00634d1f9952bab7e7bfb2"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:31df9d9b3fe6e15decee629fc7976a5fb21eaa39e290f60e57e1d422827194c6"}, - {file = "yarl-1.11.0-cp39-cp39-win32.whl", hash = "sha256:fcb7c36ba8b663a5900e6d40533f0e698ba0f38f744aad5410d4e38129e41a70"}, - {file = "yarl-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:c6c0d640bad721834a737e25267fb71d296684ada21ca7d5ad2e63da7b73f1b7"}, - {file = "yarl-1.11.0-py3-none-any.whl", hash = "sha256:03717a6627e55934b2a1d9caf24f299b461a2e8d048a90920f42ad5c20ae1b82"}, - {file = "yarl-1.11.0.tar.gz", hash = "sha256:f86f4f4a57a29ef08fa70c4667d04c5e3ba513500da95586208b285437cb9592"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e4ee8b8639070ff246ad3649294336b06db37a94bdea0d09ea491603e0be73b8"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a7cf963a357c5f00cb55b1955df8bbe68d2f2f65de065160a1c26b85a1e44172"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:43ebdcc120e2ca679dba01a779333a8ea76b50547b55e812b8b92818d604662c"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3433da95b51a75692dcf6cc8117a31410447c75a9a8187888f02ad45c0a86c50"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38d0124fa992dbacd0c48b1b755d3ee0a9f924f427f95b0ef376556a24debf01"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ded1b1803151dd0f20a8945508786d57c2f97a50289b16f2629f85433e546d47"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace4cad790f3bf872c082366c9edd7f8f8f77afe3992b134cfc810332206884f"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c77494a2f2282d9bbbbcab7c227a4d1b4bb829875c96251f66fb5f3bae4fb053"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b7f227ca6db5a9fda0a2b935a2ea34a7267589ffc63c8045f0e4edb8d8dcf956"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:31561a5b4d8dbef1559b3600b045607cf804bae040f64b5f5bca77da38084a8a"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3e52474256a7db9dcf3c5f4ca0b300fdea6c21cca0148c8891d03a025649d935"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1af74a9529a1137c67c887ed9cde62cff53aa4d84a3adbec329f9ec47a3936"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:15c87339490100c63472a76d87fe7097a0835c705eb5ae79fd96e343473629ed"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:74abb8709ea54cc483c4fb57fb17bb66f8e0f04438cff6ded322074dbd17c7ec"}, + {file = "yarl-1.15.2-cp310-cp310-win32.whl", hash = "sha256:ffd591e22b22f9cb48e472529db6a47203c41c2c5911ff0a52e85723196c0d75"}, + {file = "yarl-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:1695497bb2a02a6de60064c9f077a4ae9c25c73624e0d43e3aa9d16d983073c2"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9fcda20b2de7042cc35cf911702fa3d8311bd40055a14446c1e62403684afdc5"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0545de8c688fbbf3088f9e8b801157923be4bf8e7b03e97c2ecd4dfa39e48e0e"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbda058a9a68bec347962595f50546a8a4a34fd7b0654a7b9697917dc2bf810d"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ac2bc069f4a458634c26b101c2341b18da85cb96afe0015990507efec2e417"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd126498171f752dd85737ab1544329a4520c53eed3997f9b08aefbafb1cc53b"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3db817b4e95eb05c362e3b45dafe7144b18603e1211f4a5b36eb9522ecc62bcf"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:076b1ed2ac819933895b1a000904f62d615fe4533a5cf3e052ff9a1da560575c"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8cfd847e6b9ecf9f2f2531c8427035f291ec286c0a4944b0a9fce58c6446046"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:32b66be100ac5739065496c74c4b7f3015cef792c3174982809274d7e51b3e04"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:34a2d76a1984cac04ff8b1bfc939ec9dc0914821264d4a9c8fd0ed6aa8d4cfd2"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0afad2cd484908f472c8fe2e8ef499facee54a0a6978be0e0cff67b1254fd747"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c68e820879ff39992c7f148113b46efcd6ec765a4865581f2902b3c43a5f4bbb"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:98f68df80ec6ca3015186b2677c208c096d646ef37bbf8b49764ab4a38183931"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56ec1eacd0a5d35b8a29f468659c47f4fe61b2cab948ca756c39b7617f0aa5"}, + {file = "yarl-1.15.2-cp311-cp311-win32.whl", hash = "sha256:eedc3f247ee7b3808ea07205f3e7d7879bc19ad3e6222195cd5fbf9988853e4d"}, + {file = "yarl-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:0ccaa1bc98751fbfcf53dc8dfdb90d96e98838010fc254180dd6707a6e8bb179"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82d5161e8cb8f36ec778fd7ac4d740415d84030f5b9ef8fe4da54784a1f46c94"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa2bea05ff0a8fb4d8124498e00e02398f06d23cdadd0fe027d84a3f7afde31e"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99e12d2bf587b44deb74e0d6170fec37adb489964dbca656ec41a7cd8f2ff178"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:243fbbbf003754fe41b5bdf10ce1e7f80bcc70732b5b54222c124d6b4c2ab31c"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:856b7f1a7b98a8c31823285786bd566cf06226ac4f38b3ef462f593c608a9bd6"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:553dad9af802a9ad1a6525e7528152a015b85fb8dbf764ebfc755c695f488367"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30c3ff305f6e06650a761c4393666f77384f1cc6c5c0251965d6bfa5fbc88f7f"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:353665775be69bbfc6d54c8d134bfc533e332149faeddd631b0bc79df0897f46"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f4fe99ce44128c71233d0d72152db31ca119711dfc5f2c82385ad611d8d7f897"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9c1e3ff4b89cdd2e1a24c214f141e848b9e0451f08d7d4963cb4108d4d798f1f"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:711bdfae4e699a6d4f371137cbe9e740dc958530cb920eb6f43ff9551e17cfbc"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4388c72174868884f76affcdd3656544c426407e0043c89b684d22fb265e04a5"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f0e1844ad47c7bd5d6fa784f1d4accc5f4168b48999303a868fe0f8597bde715"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5cafb02cf097a82d74403f7e0b6b9df3ffbfe8edf9415ea816314711764a27b"}, + {file = "yarl-1.15.2-cp312-cp312-win32.whl", hash = "sha256:156ececdf636143f508770bf8a3a0498de64da5abd890c7dbb42ca9e3b6c05b8"}, + {file = "yarl-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:435aca062444a7f0c884861d2e3ea79883bd1cd19d0a381928b69ae1b85bc51d"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:416f2e3beaeae81e2f7a45dc711258be5bdc79c940a9a270b266c0bec038fb84"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:173563f3696124372831007e3d4b9821746964a95968628f7075d9231ac6bb33"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ce2e0f6123a60bd1a7f5ae3b2c49b240c12c132847f17aa990b841a417598a2"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaea112aed589131f73d50d570a6864728bd7c0c66ef6c9154ed7b59f24da611"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4ca3b9f370f218cc2a0309542cab8d0acdfd66667e7c37d04d617012485f904"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23ec1d3c31882b2a8a69c801ef58ebf7bae2553211ebbddf04235be275a38548"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75119badf45f7183e10e348edff5a76a94dc19ba9287d94001ff05e81475967b"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e6fdc976ec966b99e4daa3812fac0274cc28cd2b24b0d92462e2e5ef90d368"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8657d3f37f781d987037f9cc20bbc8b40425fa14380c87da0cb8dfce7c92d0fb"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:93bed8a8084544c6efe8856c362af08a23e959340c87a95687fdbe9c9f280c8b"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:69d5856d526802cbda768d3e6246cd0d77450fa2a4bc2ea0ea14f0d972c2894b"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ccad2800dfdff34392448c4bf834be124f10a5bc102f254521d931c1c53c455a"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a880372e2e5dbb9258a4e8ff43f13888039abb9dd6d515f28611c54361bc5644"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c998d0558805860503bc3a595994895ca0f7835e00668dadc673bbf7f5fbfcbe"}, + {file = "yarl-1.15.2-cp313-cp313-win32.whl", hash = "sha256:533a28754e7f7439f217550a497bb026c54072dbe16402b183fdbca2431935a9"}, + {file = "yarl-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:5838f2b79dc8f96fdc44077c9e4e2e33d7089b10788464609df788eb97d03aad"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fbbb63bed5fcd70cd3dd23a087cd78e4675fb5a2963b8af53f945cbbca79ae16"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2e93b88ecc8f74074012e18d679fb2e9c746f2a56f79cd5e2b1afcf2a8a786b"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af8ff8d7dc07ce873f643de6dfbcd45dc3db2c87462e5c387267197f59e6d776"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66f629632220a4e7858b58e4857927dd01a850a4cef2fb4044c8662787165cf7"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:833547179c31f9bec39b49601d282d6f0ea1633620701288934c5f66d88c3e50"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2aa738e0282be54eede1e3f36b81f1e46aee7ec7602aa563e81e0e8d7b67963f"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a13a07532e8e1c4a5a3afff0ca4553da23409fad65def1b71186fb867eeae8d"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c45817e3e6972109d1a2c65091504a537e257bc3c885b4e78a95baa96df6a3f8"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:670eb11325ed3a6209339974b276811867defe52f4188fe18dc49855774fa9cf"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:d417a4f6943112fae3924bae2af7112562285848d9bcee737fc4ff7cbd450e6c"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bc8936d06cd53fddd4892677d65e98af514c8d78c79864f418bbf78a4a2edde4"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:954dde77c404084c2544e572f342aef384240b3e434e06cecc71597e95fd1ce7"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5bc0df728e4def5e15a754521e8882ba5a5121bd6b5a3a0ff7efda5d6558ab3d"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b71862a652f50babab4a43a487f157d26b464b1dedbcc0afda02fd64f3809d04"}, + {file = "yarl-1.15.2-cp38-cp38-win32.whl", hash = "sha256:63eab904f8630aed5a68f2d0aeab565dcfc595dc1bf0b91b71d9ddd43dea3aea"}, + {file = "yarl-1.15.2-cp38-cp38-win_amd64.whl", hash = "sha256:2cf441c4b6e538ba0d2591574f95d3fdd33f1efafa864faa077d9636ecc0c4e9"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a32d58f4b521bb98b2c0aa9da407f8bd57ca81f34362bcb090e4a79e9924fefc"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:766dcc00b943c089349d4060b935c76281f6be225e39994c2ccec3a2a36ad627"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bed1b5dbf90bad3bfc19439258c97873eab453c71d8b6869c136346acfe497e7"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed20a4bdc635f36cb19e630bfc644181dd075839b6fc84cac51c0f381ac472e2"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d538df442c0d9665664ab6dd5fccd0110fa3b364914f9c85b3ef9b7b2e157980"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c6cf1d92edf936ceedc7afa61b07e9d78a27b15244aa46bbcd534c7458ee1b"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce44217ad99ffad8027d2fde0269ae368c86db66ea0571c62a000798d69401fb"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47a6000a7e833ebfe5886b56a31cb2ff12120b1efd4578a6fcc38df16cc77bd"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e52f77a0cd246086afde8815039f3e16f8d2be51786c0a39b57104c563c5cbb0"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:f9ca0e6ce7774dc7830dc0cc4bb6b3eec769db667f230e7c770a628c1aa5681b"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:136f9db0f53c0206db38b8cd0c985c78ded5fd596c9a86ce5c0b92afb91c3a19"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:173866d9f7409c0fb514cf6e78952e65816600cb888c68b37b41147349fe0057"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:6e840553c9c494a35e449a987ca2c4f8372668ee954a03a9a9685075228e5036"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:458c0c65802d816a6b955cf3603186de79e8fdb46d4f19abaec4ef0a906f50a7"}, + {file = "yarl-1.15.2-cp39-cp39-win32.whl", hash = "sha256:5b48388ded01f6f2429a8c55012bdbd1c2a0c3735b3e73e221649e524c34a58d"}, + {file = "yarl-1.15.2-cp39-cp39-win_amd64.whl", hash = "sha256:81dadafb3aa124f86dc267a2168f71bbd2bfb163663661ab0038f6e4b8edb810"}, + {file = "yarl-1.15.2-py3-none-any.whl", hash = "sha256:0d3105efab7c5c091609abacad33afff33bdff0035bece164c98bcf5a85ef90a"}, + {file = "yarl-1.15.2.tar.gz", hash = "sha256:a39c36f4218a5bb668b4f06874d676d35a035ee668e6e7e3538835c703634b84"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [[package]] name = "zipp" -version = "3.20.1" +version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"}, - {file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"}, + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] From 228c1c035ee0afc79fef97334e27f4c42b69b5d9 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:39:57 +0100 Subject: [PATCH 057/104] snapshot --- tests/snapshot_tests/test_snapshots.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 0516429c6b..b184b9a247 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2417,3 +2417,8 @@ def on_mount(self) -> None: self.mount(Label("HELLO")) assert snap_compare(PSApp()) + + +def test_split_segments_infinite_loop(snap_compare): + """Regression test for https://github.com/Textualize/textual/issues/5151""" + assert snap_compare(SNAPSHOT_APPS_DIR / "split_segments.py") From 09985d5c10d3b519ac45dc855846b5936a281afd Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:40:14 +0100 Subject: [PATCH 058/104] snapshot --- .../snapshot_tests/snapshot_apps/split_segments.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/snapshot_tests/snapshot_apps/split_segments.py diff --git a/tests/snapshot_tests/snapshot_apps/split_segments.py b/tests/snapshot_tests/snapshot_apps/split_segments.py new file mode 100644 index 0000000000..cb80b63e84 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/split_segments.py @@ -0,0 +1,14 @@ +from textual.app import App, ComposeResult +from textual.widgets import TextArea + +FAIL_TEXT = "x " + + +class CodeApp(App): + def compose(self) -> ComposeResult: + yield TextArea(FAIL_TEXT, soft_wrap=False, show_line_numbers=True) + + +if __name__ == "__main__": + app = CodeApp() + app.run() From dd9b2bccde5d1f7feb82d819d730a61429df5dd1 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:50:37 +0100 Subject: [PATCH 059/104] snapshot tests --- .../test_split_segments_infinite_loop.svg | 154 ++++++++++++++++++ .../snapshot_apps/split_segments.py | 2 +- tests/snapshot_tests/test_snapshots.py | 6 +- 3 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg new file mode 100644 index 0000000000..fb874be20f --- /dev/null +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CodeApp + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +1  x + + + + + + + + + + + + + + + + + + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + + + diff --git a/tests/snapshot_tests/snapshot_apps/split_segments.py b/tests/snapshot_tests/snapshot_apps/split_segments.py index cb80b63e84..9acb6fe28b 100644 --- a/tests/snapshot_tests/snapshot_apps/split_segments.py +++ b/tests/snapshot_tests/snapshot_apps/split_segments.py @@ -1,7 +1,7 @@ from textual.app import App, ComposeResult from textual.widgets import TextArea -FAIL_TEXT = "x " +FAIL_TEXT = "x" class CodeApp(App): diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index b184b9a247..019f49f505 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2420,5 +2420,9 @@ def on_mount(self) -> None: def test_split_segments_infinite_loop(snap_compare): - """Regression test for https://github.com/Textualize/textual/issues/5151""" + """Regression test for https://github.com/Textualize/textual/issues/5151 + + Should be a bare-bones text editor containing "x" + + """ assert snap_compare(SNAPSHOT_APPS_DIR / "split_segments.py") From 0ce8988a67e3bba0ebb2a875780c4d1d2dc418b4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 15:49:50 +0100 Subject: [PATCH 060/104] hover effects --- src/textual/app.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/textual/app.py b/src/textual/app.py index 843d4affd7..f07c9b9c16 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -775,6 +775,11 @@ def __init__( self._previous_inline_height: int | None = None """Size of previous inline update.""" + self._paused_hover_effects: bool = False + """Have the hover effects been paused?""" + + self._hover_effects_timer: Timer | None = None + if self.ENABLE_COMMAND_PALETTE: for _key, binding in self._bindings: if binding.action in {"command_palette", "app.command_palette"}: @@ -2693,12 +2698,28 @@ def set_focus(self, widget: Widget | None, scroll_visible: bool = True) -> None: """ self.screen.set_focus(widget, scroll_visible) + def _pause_hover_effects(self): + self._paused_hover_effects = True + + def _resume_hover_effects(self): + if self._paused_hover_effects: + self._paused_hover_effects = False + try: + widget, _ = self.screen.get_widget_at(*self.mouse_position) + except NoWidget: + pass + else: + if widget is not self.mouse_over: + self._set_mouse_over(widget) + def _set_mouse_over(self, widget: Widget | None) -> None: """Called when the mouse is over another widget. Args: widget: Widget under mouse, or None for no widgets. """ + if self._paused_hover_effects: + return if widget is None: if self.mouse_over is not None: try: From 444a878bd8a4fb2f68f35075e862f69597fa2642 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 17:03:58 +0100 Subject: [PATCH 061/104] hover effects timer --- src/textual/app.py | 11 +++++++++++ src/textual/widget.py | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/textual/app.py b/src/textual/app.py index f07c9b9c16..ae77d5ebe8 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -2699,11 +2699,22 @@ def set_focus(self, widget: Widget | None, scroll_visible: bool = True) -> None: self.screen.set_focus(widget, scroll_visible) def _pause_hover_effects(self): + """Pause any hover effects based on Enter and Leave events for 200ms.""" self._paused_hover_effects = True + if self._hover_effects_timer is None: + self._hover_effects_timer = self.set_interval( + 0.2, self._resume_hover_effects + ) + else: + self._hover_effects_timer.reset() + self._hover_effects_timer.resume() def _resume_hover_effects(self): + """Resume sending Enter and Leave for hover effects.""" if self._paused_hover_effects: self._paused_hover_effects = False + if self._hover_effects_timer is not None: + self._hover_effects_timer.pause() try: widget, _ = self.screen.get_widget_at(*self.mouse_position) except NoWidget: diff --git a/src/textual/widget.py b/src/textual/widget.py index a60c6aee9a..ccc9e14f25 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -2353,6 +2353,9 @@ def _scroll_to( if on_complete is not None: self.call_after_refresh(on_complete) + if scrolled_x or scrolled_y: + self.app._pause_hover_effects() + return scrolled_x or scrolled_y def pre_layout(self, layout: Layout) -> None: From 52128cdd5527a25b554b13b6933d48f66e6c3b97 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 23 Oct 2024 11:56:34 +0100 Subject: [PATCH 062/104] remove -screen-suspended class --- src/textual/screen.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/textual/screen.py b/src/textual/screen.py index 1f1c3b1d22..8391534817 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -1210,7 +1210,6 @@ def _screen_resized(self, size: Size): def _on_screen_resume(self) -> None: """Screen has resumed.""" - self.remove_class("-screen-suspended") self.stack_updates += 1 self.app._refresh_notifications() size = self.app.size @@ -1230,7 +1229,6 @@ def _on_screen_resume(self) -> None: def _on_screen_suspend(self) -> None: """Screen has suspended.""" - self.add_class("-screen-suspended") self.app._set_mouse_over(None) self._clear_tooltip() self.stack_updates += 1 From ea22e836d63780b611f672393d212a35ad8d455a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 23 Oct 2024 14:09:22 +0100 Subject: [PATCH 063/104] compromise --- src/textual/app.py | 3 +++ src/textual/screen.py | 4 ++++ tests/snapshot_tests/test_snapshots.py | 1 + 3 files changed, 8 insertions(+) diff --git a/src/textual/app.py b/src/textual/app.py index 386dce4ca0..850a60e14b 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -487,6 +487,9 @@ class MyApp(App[None]): INLINE_PADDING: ClassVar[int] = 1 """Number of blank lines above an inline app.""" + SUSPENDED_SCREEN_CLASS: ClassVar[str] = "" + """Class to apply to suspended screens, or empty string for no class.""" + _PSEUDO_CLASSES: ClassVar[dict[str, Callable[[App], bool]]] = { "focus": lambda app: app.app_focus, "blur": lambda app: not app.app_focus, diff --git a/src/textual/screen.py b/src/textual/screen.py index 8391534817..c25d17c24e 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -1210,6 +1210,8 @@ def _screen_resized(self, size: Size): def _on_screen_resume(self) -> None: """Screen has resumed.""" + if self.app.SUSPENDED_SCREEN_CLASS: + self.remove_class(self.app.SUSPENDED_SCREEN_CLASS) self.stack_updates += 1 self.app._refresh_notifications() size = self.app.size @@ -1229,6 +1231,8 @@ def _on_screen_resume(self) -> None: def _on_screen_suspend(self) -> None: """Screen has suspended.""" + if self.app.SUSPENDED_SCREEN_CLASS: + self.add_class(self.app.SUSPENDED_SCREEN_CLASS) self.app._set_mouse_over(None) self._clear_tooltip() self.stack_updates += 1 diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 019f49f505..31d45ad122 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -1960,6 +1960,7 @@ def test_ansi_command_palette(snap_compare): """Test command palette on top of ANSI colors.""" class CommandPaletteApp(App[None]): + SUSPENDED_SCREEN_CLASS = "-screen-suspended" CSS = """ Label { width: 1fr; From 09b636b257effef15bfd123dfa100e545608cc89 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Oct 2024 16:41:34 +0100 Subject: [PATCH 064/104] tint fix --- src/textual/color.py | 25 +++++++++++++++++++++++++ src/textual/dom.py | 12 ++++++------ tests/snapshot_tests/test_snapshots.py | 22 +++++++++++++++++----- tests/test_color.py | 24 ++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/textual/color.py b/src/textual/color.py index 23c5a4e6dd..10d5d070e3 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -399,6 +399,31 @@ def blend( new_alpha, ) + @lru_cache(maxsize=1024) + def tint(self, color: Color) -> Color: + """Apply a tint to a color. + + Similar to blend, but combines color and alpha. + + Args: + color: A color with alpha component. + + Returns: + New color + """ + r2, g2, b2, a2, ansi2 = color + if ansi2 is not None: + return color + r1, g1, b1, a1, ansi1 = self + if ansi1 is not None: + return color + return Color( + int(r1 + (r2 - r1) * a2), + int(g1 + (g2 - g1) * a2), + int(b1 + (b2 - b1) * a2), + a1, + ) + def __add__(self, other: object) -> Color: if isinstance(other, Color): return self.blend(other, other.a, 1.0) diff --git a/src/textual/dom.py b/src/textual/dom.py index 5cab83d230..aa2fdb3983 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -1036,11 +1036,11 @@ def rich_style(self) -> Style: has_rule = styles.has_rule opacity *= styles.opacity if has_rule("background"): - text_background = ( - background + styles.background + styles.background_tint + text_background = background + styles.background.tint( + styles.background_tint ) background += ( - styles.background + styles.background_tint + styles.background.tint(styles.background_tint) ).multiply_alpha(opacity) else: text_background = background @@ -1129,7 +1129,7 @@ def background_colors(self) -> tuple[Color, Color]: for node in reversed(self.ancestors_with_self): styles = node.styles base_background = background - background += styles.background + styles.background_tint + background += styles.background.tint(styles.background_tint) return (base_background, background) @property @@ -1145,7 +1145,7 @@ def _opacity_background_colors(self) -> tuple[Color, Color]: styles = node.styles base_background = background opacity *= styles.opacity - background += (styles.background + styles.background_tint).multiply_alpha( + background += styles.background.tint(styles.background_tint).multiply_alpha( opacity ) return (base_background, background) @@ -1162,7 +1162,7 @@ def colors(self) -> tuple[Color, Color, Color, Color]: for node in reversed(self.ancestors_with_self): styles = node.styles base_background = background - background += styles.background + styles.background_tint + background += styles.background.tint(styles.background_tint) if styles.has_rule("color"): base_color = color if styles.auto_color: diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index a9d3a9ec0b..6793129e50 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2314,15 +2314,27 @@ def compose(self) -> ComposeResult: def test_background_tint(snap_compare): + """Test background tint with alpha.""" + + # The screen background is dark blue + # The vertical is 20% white + # With no background tint, the verticals will be a light blue + # With a 100% tint, the vertical should be 20% red plus the blue (i.e. purple) + + # tl;dr you should see 4 bars, blue at the top, purple at the bottom, and two shades in betweenm + class BackgroundTintApp(App): CSS = """ + Screen { + background: rgb(0,0,100) + } Vertical { - background: $panel; + background: rgba(255,255,255,0.2); } - #tint1 { background-tint: $foreground 0%; } - #tint2 { background-tint: $foreground 33%; } - #tint3 { background-tint: $foreground 66%; } - #tint4 { background-tint: $foreground 100% } + #tint1 { background-tint: rgb(255,0,0) 0%; } + #tint2 { background-tint: rgb(255,0,0) 33%; } + #tint3 { background-tint: rgb(255,0,0) 66%; } + #tint4 { background-tint: rgb(255,0,0) 100% } """ def compose(self) -> ComposeResult: diff --git a/tests/test_color.py b/tests/test_color.py index e13a7bf726..00e7270116 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -265,3 +265,27 @@ def test_is_transparent(): assert not Color(20, 20, 30, a=0.01).is_transparent assert not Color(20, 20, 30, a=1).is_transparent assert not Color(20, 20, 30, 0, ansi=1).is_transparent + + +@pytest.mark.parametrize( + "base,tint,expected", + [ + ( + Color(0, 0, 0), + Color(10, 20, 30), + Color(10, 20, 30), + ), + ( + Color(0, 0, 0, 0.5), + Color(255, 255, 255, 0.5), + Color(127, 127, 127, 0.5), + ), + ( + Color(100, 0, 0, 0.2), + Color(0, 100, 0, 0.5), + Color(50, 50, 0, 0.2), + ), + ], +) +def test_tint(base: Color, tint: Color, expected: Color) -> None: + assert base.tint(tint) == expected From e3bb452df0ea0bd1d8bdff231afcda37f1299eaa Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Oct 2024 16:49:14 +0100 Subject: [PATCH 065/104] snapshot --- src/textual/color.py | 4 +- .../test_snapshots/test_background_tint.svg | 116 +++++++++--------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/textual/color.py b/src/textual/color.py index 10d5d070e3..a93eaa4288 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -413,10 +413,10 @@ def tint(self, color: Color) -> Color: """ r2, g2, b2, a2, ansi2 = color if ansi2 is not None: - return color + return self r1, g1, b1, a1, ansi1 = self if ansi1 is not None: - return color + return self return Color( int(r1 + (r2 - r1) * a2), int(g1 + (g2 - g1) * a2), diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_background_tint.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_background_tint.svg index 22b4fda3db..e37daad3d7 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_background_tint.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_background_tint.svg @@ -19,134 +19,134 @@ font-weight: 700; } - .terminal-2147534319-matrix { + .terminal-3859065499-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2147534319-title { + .terminal-3859065499-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2147534319-r1 { fill: #e2e3e3 } -.terminal-2147534319-r2 { fill: #c5c8c6 } -.terminal-2147534319-r3 { fill: #ebebec } -.terminal-2147534319-r4 { fill: #f3f3f4 } -.terminal-2147534319-r5 { fill: #fcfcfc } + .terminal-3859065499-r1 { fill: #e4e4ee } +.terminal-3859065499-r2 { fill: #c5c8c6 } +.terminal-3859065499-r3 { fill: #e4e2ec } +.terminal-3859065499-r4 { fill: #e4e0ea } +.terminal-3859065499-r5 { fill: #e4dde8 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - BackgroundTintApp + BackgroundTintApp - - - - 0%                                                                               - - - - - -33%                                                                              - - - - - -66%                                                                              - - - - - -100%                                                                             - - - - + + + + 0%                                                                               + + + + + +33%                                                                              + + + + + +66%                                                                              + + + + + +100%                                                                             + + + + From e6c97c7f9040c6698fbf3da2bcc551e4a763dc6a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Oct 2024 16:50:10 +0100 Subject: [PATCH 066/104] reorder --- src/textual/color.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/textual/color.py b/src/textual/color.py index a93eaa4288..2de8aa1bf5 100644 --- a/src/textual/color.py +++ b/src/textual/color.py @@ -411,12 +411,13 @@ def tint(self, color: Color) -> Color: Returns: New color """ - r2, g2, b2, a2, ansi2 = color - if ansi2 is not None: - return self + r1, g1, b1, a1, ansi1 = self if ansi1 is not None: return self + r2, g2, b2, a2, ansi2 = color + if ansi2 is not None: + return self return Color( int(r1 + (r2 - r1) * a2), int(g1 + (g2 - g1) * a2), From 89b112e2b307199cd4f81249fca18fcd967fa7fa Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 16 Oct 2024 16:51:57 +0100 Subject: [PATCH 067/104] typo --- tests/snapshot_tests/test_snapshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 6793129e50..0516429c6b 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2321,7 +2321,7 @@ def test_background_tint(snap_compare): # With no background tint, the verticals will be a light blue # With a 100% tint, the vertical should be 20% red plus the blue (i.e. purple) - # tl;dr you should see 4 bars, blue at the top, purple at the bottom, and two shades in betweenm + # tl;dr you should see 4 bars, blue at the top, purple at the bottom, and two shades in between class BackgroundTintApp(App): CSS = """ From bc69bdfca7e8bf5b20237e93197dc880e72e4925 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 12:14:33 +0100 Subject: [PATCH 068/104] fix infinite loop in cropping --- src/textual/strip.py | 3 +++ src/textual/widgets/_text_area.py | 4 +--- tests/test_strip.py | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/textual/strip.py b/src/textual/strip.py index 71bfd78089..c7644898f8 100644 --- a/src/textual/strip.py +++ b/src/textual/strip.py @@ -372,10 +372,13 @@ def crop(self, start: int, end: int | None = None) -> Strip: Returns: A new Strip. """ + start = max(0, start) end = self.cell_length if end is None else min(self.cell_length, end) if start == 0 and end == self.cell_length: return self + if end <= start: + return Strip([], 0) cache_key = (start, end) cached = self._crop_cache.get(cache_key) if cached is not None: diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index b147c27ee5..0e7da87543 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -1236,9 +1236,7 @@ def render_line(self, y: int) -> Strip: # Crop the line to show only the visible part (some may be scrolled out of view) if not self.soft_wrap: - text_strip = text_strip.crop( - scroll_x, scroll_x + virtual_width - gutter_width - ) + text_strip = text_strip.crop(scroll_x, scroll_x + virtual_width) # Stylize the line the cursor is currently on. if cursor_row == line_index: diff --git a/tests/test_strip.py b/tests/test_strip.py index 7f6d9bae63..b88c68ecc5 100644 --- a/tests/test_strip.py +++ b/tests/test_strip.py @@ -131,7 +131,7 @@ def test_crop(): assert Strip([Segment("foo")]).crop(1, 3) == Strip([Segment("oo")]) assert Strip([Segment("foo")]).crop(1, 2) == Strip([Segment("o")]) - assert Strip([Segment("foo")]).crop(1, 1) == Strip([Segment("")]) + assert Strip([Segment("foo")]).crop(1, 1) == Strip([]) assert Strip([Segment("foo💩"), Segment("b💩ar"), Segment("ba💩z")]).crop( 1, 6 From 6c395331feda69af89a75fb77c3566e76314bd40 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:01:10 +0100 Subject: [PATCH 069/104] version bump --- CHANGELOG.md | 4 +++- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 818a205e82..fb17ee2645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ 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/). -## Unreleased +## [0.84.0] ### Changed @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fixed `RadioSet` not being scrollable https://github.com/Textualize/textual/issues/5100 +- Fixed infinite loop in TextArea https://github.com/Textualize/textual/pull/5154 ### Added @@ -2469,6 +2470,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040 - New handler system for messages that doesn't require inheritance - Improved traceback handling +[0.84.0]: https://github.com/Textualize/textual/compare/v0.83.0...v0.84.0 [0.83.0]: https://github.com/Textualize/textual/compare/v0.82.0...v0.83.0 [0.82.0]: https://github.com/Textualize/textual/compare/v0.81.0...v0.82.0 [0.81.0]: https://github.com/Textualize/textual/compare/v0.80.1...v0.81.0 diff --git a/pyproject.toml b/pyproject.toml index 3b88d9a1a6..ea2cf45af2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual" -version = "0.83.0" +version = "0.84.0" homepage = "https://github.com/Textualize/textual" repository = "https://github.com/Textualize/textual" documentation = "https://textual.textualize.io/" From b4fa813a46bbdeaad2f02030b3f5533ca96a2874 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:02:53 +0100 Subject: [PATCH 070/104] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb17ee2645..0e8d38f745 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ 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.84.0] +## [0.84.0] - 2024-10-22 ### Changed From 536d2dd67b04b46ce92fd35398a4a6311a380fb1 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:09:15 +0100 Subject: [PATCH 071/104] depenencies --- poetry.lock | 1401 +++++++++++++++++++++++++++++---------------------- 1 file changed, 785 insertions(+), 616 deletions(-) diff --git a/poetry.lock b/poetry.lock index a8e6709daf..d971d5cc4b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,114 +1,114 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" -version = "2.4.0" +version = "2.4.3" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"}, - {file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"}, + {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, + {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, ] [[package]] name = "aiohttp" -version = "3.10.5" +version = "3.10.10" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, - {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, - {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, - {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, - {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, - {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, - {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, - {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, - {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"}, - {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"}, - {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"}, - {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"}, - {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"}, - {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, - {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, - {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, - {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:be7443669ae9c016b71f402e43208e13ddf00912f47f623ee5994e12fc7d4b3f"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7b06b7843929e41a94ea09eb1ce3927865387e3e23ebe108e0d0d09b08d25be9"}, + {file = "aiohttp-3.10.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:333cf6cf8e65f6a1e06e9eb3e643a0c515bb850d470902274239fea02033e9a8"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:274cfa632350225ce3fdeb318c23b4a10ec25c0e2c880eff951a3842cf358ac1"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9e5e4a85bdb56d224f412d9c98ae4cbd032cc4f3161818f692cd81766eee65a"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b606353da03edcc71130b52388d25f9a30a126e04caef1fd637e31683033abd"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"}, + {file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578a4b875af3e0daaf1ac6fa983d93e0bbfec3ead753b6d6f33d467100cdc67b"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8105fd8a890df77b76dd3054cddf01a879fc13e8af576805d667e0fa0224c35d"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3bcd391d083f636c06a68715e69467963d1f9600f85ef556ea82e9ef25f043f7"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fbc6264158392bad9df19537e872d476f7c57adf718944cc1e4495cbabf38e2a"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e48d5021a84d341bcaf95c8460b152cfbad770d28e5fe14a768988c461b821bc"}, + {file = "aiohttp-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2609e9ab08474702cc67b7702dbb8a80e392c54613ebe80db7e8dbdb79837c68"}, + {file = "aiohttp-3.10.10-cp310-cp310-win32.whl", hash = "sha256:84afcdea18eda514c25bc68b9af2a2b1adea7c08899175a51fe7c4fb6d551257"}, + {file = "aiohttp-3.10.10-cp310-cp310-win_amd64.whl", hash = "sha256:9c72109213eb9d3874f7ac8c0c5fa90e072d678e117d9061c06e30c85b4cf0e6"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c30a0eafc89d28e7f959281b58198a9fa5e99405f716c0289b7892ca345fe45f"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:258c5dd01afc10015866114e210fb7365f0d02d9d059c3c3415382ab633fcbcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:15ecd889a709b0080f02721255b3f80bb261c2293d3c748151274dfea93ac871"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3935f82f6f4a3820270842e90456ebad3af15810cf65932bd24da4463bc0a4c"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:413251f6fcf552a33c981c4709a6bba37b12710982fec8e558ae944bfb2abd38"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1720b4f14c78a3089562b8875b53e36b51c97c51adc53325a69b79b4b48ebcb"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:679abe5d3858b33c2cf74faec299fda60ea9de62916e8b67e625d65bf069a3b7"}, + {file = "aiohttp-3.10.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79019094f87c9fb44f8d769e41dbb664d6e8fcfd62f665ccce36762deaa0e911"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe2fb38c2ed905a2582948e2de560675e9dfbee94c6d5ccdb1301c6d0a5bf092"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a3f00003de6eba42d6e94fabb4125600d6e484846dbf90ea8e48a800430cc142"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1bbb122c557a16fafc10354b9d99ebf2f2808a660d78202f10ba9d50786384b9"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:30ca7c3b94708a9d7ae76ff281b2f47d8eaf2579cd05971b5dc681db8caac6e1"}, + {file = "aiohttp-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:df9270660711670e68803107d55c2b5949c2e0f2e4896da176e1ecfc068b974a"}, + {file = "aiohttp-3.10.10-cp311-cp311-win32.whl", hash = "sha256:aafc8ee9b742ce75044ae9a4d3e60e3d918d15a4c2e08a6c3c3e38fa59b92d94"}, + {file = "aiohttp-3.10.10-cp311-cp311-win_amd64.whl", hash = "sha256:362f641f9071e5f3ee6f8e7d37d5ed0d95aae656adf4ef578313ee585b585959"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9294bbb581f92770e6ed5c19559e1e99255e4ca604a22c5c6397b2f9dd3ee42c"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a8fa23fe62c436ccf23ff930149c047f060c7126eae3ccea005f0483f27b2e28"}, + {file = "aiohttp-3.10.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c6a5b8c7926ba5d8545c7dd22961a107526562da31a7a32fa2456baf040939f"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:007ec22fbc573e5eb2fb7dec4198ef8f6bf2fe4ce20020798b2eb5d0abda6138"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9627cc1a10c8c409b5822a92d57a77f383b554463d1884008e051c32ab1b3742"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:50edbcad60d8f0e3eccc68da67f37268b5144ecc34d59f27a02f9611c1d4eec7"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a45d85cf20b5e0d0aa5a8dca27cce8eddef3292bc29d72dcad1641f4ed50aa16"}, + {file = "aiohttp-3.10.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b00807e2605f16e1e198f33a53ce3c4523114059b0c09c337209ae55e3823a8"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f2d4324a98062be0525d16f768a03e0bbb3b9fe301ceee99611dc9a7953124e6"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:438cd072f75bb6612f2aca29f8bd7cdf6e35e8f160bc312e49fbecab77c99e3a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:baa42524a82f75303f714108fea528ccacf0386af429b69fff141ffef1c534f9"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a7d8d14fe962153fc681f6366bdec33d4356f98a3e3567782aac1b6e0e40109a"}, + {file = "aiohttp-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1277cd707c465cd09572a774559a3cc7c7a28802eb3a2a9472588f062097205"}, + {file = "aiohttp-3.10.10-cp312-cp312-win32.whl", hash = "sha256:59bb3c54aa420521dc4ce3cc2c3fe2ad82adf7b09403fa1f48ae45c0cbde6628"}, + {file = "aiohttp-3.10.10-cp312-cp312-win_amd64.whl", hash = "sha256:0e1b370d8007c4ae31ee6db7f9a2fe801a42b146cec80a86766e7ad5c4a259cf"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ad7593bb24b2ab09e65e8a1d385606f0f47c65b5a2ae6c551db67d6653e78c28"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1eb89d3d29adaf533588f209768a9c02e44e4baf832b08118749c5fad191781d"}, + {file = "aiohttp-3.10.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3fe407bf93533a6fa82dece0e74dbcaaf5d684e5a51862887f9eaebe6372cd79"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50aed5155f819873d23520919e16703fc8925e509abbb1a1491b0087d1cd969e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f05e9727ce409358baa615dbeb9b969db94324a79b5a5cea45d39bdb01d82e6"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dffb610a30d643983aeb185ce134f97f290f8935f0abccdd32c77bed9388b42"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6658732517ddabe22c9036479eabce6036655ba87a0224c612e1ae6af2087e"}, + {file = "aiohttp-3.10.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:741a46d58677d8c733175d7e5aa618d277cd9d880301a380fd296975a9cdd7bc"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e00e3505cd80440f6c98c6d69269dcc2a119f86ad0a9fd70bccc59504bebd68a"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ffe595f10566f8276b76dc3a11ae4bb7eba1aac8ddd75811736a15b0d5311414"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdfcf6443637c148c4e1a20c48c566aa694fa5e288d34b20fcdc58507882fed3"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d183cf9c797a5291e8301790ed6d053480ed94070637bfaad914dd38b0981f67"}, + {file = "aiohttp-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:77abf6665ae54000b98b3c742bc6ea1d1fb31c394bcabf8b5d2c1ac3ebfe7f3b"}, + {file = "aiohttp-3.10.10-cp313-cp313-win32.whl", hash = "sha256:4470c73c12cd9109db8277287d11f9dd98f77fc54155fc71a7738a83ffcc8ea8"}, + {file = "aiohttp-3.10.10-cp313-cp313-win_amd64.whl", hash = "sha256:486f7aabfa292719a2753c016cc3a8f8172965cabb3ea2e7f7436c7f5a22a151"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1b66ccafef7336a1e1f0e389901f60c1d920102315a56df85e49552308fc0486"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:acd48d5b80ee80f9432a165c0ac8cbf9253eaddb6113269a5e18699b33958dbb"}, + {file = "aiohttp-3.10.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3455522392fb15ff549d92fbf4b73b559d5e43dc522588f7eb3e54c3f38beee7"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c3b868724137f713a38376fef8120c166d1eadd50da1855c112fe97954aed8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da1dee8948d2137bb51fbb8a53cce6b1bcc86003c6b42565f008438b806cccd8"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5ce2ce7c997e1971b7184ee37deb6ea9922ef5163c6ee5aa3c274b05f9e12fa"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28529e08fde6f12eba8677f5a8608500ed33c086f974de68cc65ab218713a59d"}, + {file = "aiohttp-3.10.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7db54c7914cc99d901d93a34704833568d86c20925b2762f9fa779f9cd2e70f"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a42ac7895406220124c88911ebee31ba8b2d24c98507f4a8bf826b2937c7f2"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:7e338c0523d024fad378b376a79faff37fafb3c001872a618cde1d322400a572"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:038f514fe39e235e9fef6717fbf944057bfa24f9b3db9ee551a7ecf584b5b480"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:64f6c17757251e2b8d885d728b6433d9d970573586a78b78ba8929b0f41d045a"}, + {file = "aiohttp-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:93429602396f3383a797a2a70e5f1de5df8e35535d7806c9f91df06f297e109b"}, + {file = "aiohttp-3.10.10-cp38-cp38-win32.whl", hash = "sha256:c823bc3971c44ab93e611ab1a46b1eafeae474c0c844aff4b7474287b75fe49c"}, + {file = "aiohttp-3.10.10-cp38-cp38-win_amd64.whl", hash = "sha256:54ca74df1be3c7ca1cf7f4c971c79c2daf48d9aa65dea1a662ae18926f5bc8ce"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01948b1d570f83ee7bbf5a60ea2375a89dfb09fd419170e7f5af029510033d24"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9fc1500fd2a952c5c8e3b29aaf7e3cc6e27e9cfc0a8819b3bce48cc1b849e4cc"}, + {file = "aiohttp-3.10.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f614ab0c76397661b90b6851a030004dac502e48260ea10f2441abd2207fbcc7"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00819de9e45d42584bed046314c40ea7e9aea95411b38971082cad449392b08c"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05646ebe6b94cc93407b3bf34b9eb26c20722384d068eb7339de802154d61bc5"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:998f3bd3cfc95e9424a6acd7840cbdd39e45bc09ef87533c006f94ac47296090"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9010c31cd6fa59438da4e58a7f19e4753f7f264300cd152e7f90d4602449762"}, + {file = "aiohttp-3.10.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ea7ffc6d6d6f8a11e6f40091a1040995cdff02cfc9ba4c2f30a516cb2633554"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ef9c33cc5cbca35808f6c74be11eb7f5f6b14d2311be84a15b594bd3e58b5527"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce0cdc074d540265bfeb31336e678b4e37316849d13b308607efa527e981f5c2"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:597a079284b7ee65ee102bc3a6ea226a37d2b96d0418cc9047490f231dc09fe8"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:7789050d9e5d0c309c706953e5e8876e38662d57d45f936902e176d19f1c58ab"}, + {file = "aiohttp-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e7f8b04d83483577fd9200461b057c9f14ced334dcb053090cea1da9c8321a91"}, + {file = "aiohttp-3.10.10-cp39-cp39-win32.whl", hash = "sha256:c02a30b904282777d872266b87b20ed8cc0d1501855e27f831320f471d54d983"}, + {file = "aiohttp-3.10.10-cp39-cp39-win_amd64.whl", hash = "sha256:edfe3341033a6b53a5c522c802deb2079eee5cbfbb0af032a55064bd65c73a23"}, + {file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"}, ] [package.dependencies] @@ -118,7 +118,7 @@ async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" +yarl = ">=1.12.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] @@ -154,13 +154,13 @@ frozenlist = ">=1.1.0" [[package]] name = "anyio" -version = "4.4.0" +version = "4.5.2" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.8" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, + {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, ] [package.dependencies] @@ -170,9 +170,9 @@ sniffio = ">=1.1" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "async-timeout" @@ -312,101 +312,116 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -523,13 +538,13 @@ toml = ["tomli"] [[package]] name = "distlib" -version = "0.3.8" +version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] @@ -562,18 +577,18 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "filelock" -version = "3.16.0" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] @@ -782,13 +797,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "identify" -version = "2.6.0" +version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] [package.extras] @@ -796,33 +811,40 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.8" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ - {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, - {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "importlib-metadata" -version = "8.4.0" +version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, - {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [package.dependencies] -zipp = ">=0.5" +zipp = ">=3.20" [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] [[package]] name = "iniconfig" @@ -1001,13 +1023,13 @@ files = [ [[package]] name = "mdit-py-plugins" -version = "0.4.1" +version = "0.4.2" description = "Collection of plugins for markdown-it-py" optional = false python-versions = ">=3.8" files = [ - {file = "mdit_py_plugins-0.4.1-py3-none-any.whl", hash = "sha256:1020dfe4e6bfc2c79fb49ae4e3f5b297f5ccd20f010187acc52af2921e27dc6a"}, - {file = "mdit_py_plugins-0.4.1.tar.gz", hash = "sha256:834b8ac23d1cd60cec703646ffd22ae97b7955a6d596eb1d304be1e251ae499c"}, + {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, + {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, ] [package.dependencies] @@ -1119,13 +1141,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-git-revision-date-localized-plugin" -version = "1.2.8" +version = "1.2.9" description = "Mkdocs plugin that enables displaying the localized date of the last git modification of a markdown file." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_git_revision_date_localized_plugin-1.2.8-py3-none-any.whl", hash = "sha256:c7ec3b1481ca23134269e84927bd8a5dc1aa359c0e515b832dbd5d25019b5748"}, - {file = "mkdocs_git_revision_date_localized_plugin-1.2.8.tar.gz", hash = "sha256:6e09c308bb27bcf36b211d17b74152ecc2837cdfc351237f70cffc723ef0fd99"}, + {file = "mkdocs_git_revision_date_localized_plugin-1.2.9-py3-none-any.whl", hash = "sha256:dea5c8067c23df30275702a1708885500fadf0abfb595b60e698bffc79c7a423"}, + {file = "mkdocs_git_revision_date_localized_plugin-1.2.9.tar.gz", hash = "sha256:df9a50873fba3a42ce9123885f8c53d589e90ef6c2443fe3280ef1e8d33c8f65"}, ] [package.dependencies] @@ -1141,13 +1163,13 @@ dev = ["click", "codecov", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkd [[package]] name = "mkdocs-material" -version = "9.5.34" +version = "9.5.42" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.34-py3-none-any.whl", hash = "sha256:54caa8be708de2b75167fd4d3b9f3d949579294f49cb242515d4653dbee9227e"}, - {file = "mkdocs_material-9.5.34.tar.gz", hash = "sha256:1e60ddf716cfb5679dfd65900b8a25d277064ed82d9a53cd5190e3f894df7840"}, + {file = "mkdocs_material-9.5.42-py3-none-any.whl", hash = "sha256:452a7c5d21284b373f36b981a2cbebfff59263feebeede1bc28652e9c5bbe316"}, + {file = "mkdocs_material-9.5.42.tar.gz", hash = "sha256:92779b5e9b5934540c574c11647131d217dc540dce72b05feeda088c8eb1b8f2"}, ] [package.dependencies] @@ -1245,202 +1267,220 @@ mkdocstrings = ">=0.20" [[package]] name = "msgpack" -version = "1.0.8" +version = "1.1.0" description = "MessagePack serializer" optional = false python-versions = ">=3.8" files = [ - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, - {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, - {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, - {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, - {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, - {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, - {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, - {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, - {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, - {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, - {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, + {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, + {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, + {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, + {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, + {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, + {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, + {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, + {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, + {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, + {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, + {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, + {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, + {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, ] [[package]] name = "multidict" -version = "6.0.5" +version = "6.1.0" description = "multidict implementation" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + [[package]] name = "mypy" -version = "1.11.2" +version = "1.12.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, - {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, - {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, - {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, - {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, - {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, - {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, - {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, - {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, - {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, - {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, - {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, - {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, - {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, - {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, - {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, - {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, - {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, - {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, + {file = "mypy-1.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d7d4371829184e22fda4015278fbfdef0327a4b955a483012bd2d423a788801"}, + {file = "mypy-1.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f59f1dfbf497d473201356966e353ef09d4daec48caeacc0254db8ef633a28a5"}, + {file = "mypy-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b947097fae68004b8328c55161ac9db7d3566abfef72d9d41b47a021c2fba6b1"}, + {file = "mypy-1.12.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96af62050971c5241afb4701c15189ea9507db89ad07794a4ee7b4e092dc0627"}, + {file = "mypy-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:d90da248f4c2dba6c44ddcfea94bb361e491962f05f41990ff24dbd09969ce20"}, + {file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"}, + {file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"}, + {file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"}, + {file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"}, + {file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"}, + {file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"}, + {file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"}, + {file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"}, + {file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"}, + {file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"}, + {file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"}, + {file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"}, + {file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"}, + {file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"}, + {file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"}, + {file = "mypy-1.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b16fe09f9c741d85a2e3b14a5257a27a4f4886c171d562bc5a5e90d8591906b8"}, + {file = "mypy-1.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0dcc1e843d58f444fce19da4cce5bd35c282d4bde232acdeca8279523087088a"}, + {file = "mypy-1.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e10ba7de5c616e44ad21005fa13450cd0de7caaa303a626147d45307492e4f2d"}, + {file = "mypy-1.12.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e6fe449223fa59fbee351db32283838a8fee8059e0028e9e6494a03802b4004"}, + {file = "mypy-1.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:dc6e2a2195a290a7fd5bac3e60b586d77fc88e986eba7feced8b778c373f9afe"}, + {file = "mypy-1.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:de5b2a8988b4e1269a98beaf0e7cc71b510d050dce80c343b53b4955fff45f19"}, + {file = "mypy-1.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843826966f1d65925e8b50d2b483065c51fc16dc5d72647e0236aae51dc8d77e"}, + {file = "mypy-1.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe20f89da41a95e14c34b1ddb09c80262edcc295ad891f22cc4b60013e8f78d"}, + {file = "mypy-1.12.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8135ffec02121a75f75dc97c81af7c14aa4ae0dda277132cfcd6abcd21551bfd"}, + {file = "mypy-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a7b76fa83260824300cc4834a3ab93180db19876bce59af921467fd03e692810"}, + {file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"}, + {file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"}, ] [package.dependencies] @@ -1515,13 +1555,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.3.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, - {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] @@ -1562,6 +1602,113 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "propcache" +version = "0.2.0" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.8" +files = [ + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, + {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, + {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, + {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, + {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, + {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, + {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, + {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, + {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, + {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, + {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, + {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, + {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, + {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, + {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, +] + [[package]] name = "pygments" version = "2.18.0" @@ -1578,13 +1725,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymdown-extensions" -version = "10.9" +version = "10.11.2" description = "Extension pack for Python Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, - {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, + {file = "pymdown_extensions-10.11.2-py3-none-any.whl", hash = "sha256:41cdde0a77290e480cf53892f5c5e50921a7ee3e5cd60ba91bf19837b33badcf"}, + {file = "pymdown_extensions-10.11.2.tar.gz", hash = "sha256:bc8847ecc9e784a098efd35e20cba772bc5a1b529dfcef9dc1972db9021a1049"}, ] [package.dependencies] @@ -1596,13 +1743,13 @@ extra = ["pygments (>=2.12)"] [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, + {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, ] [package.dependencies] @@ -1706,13 +1853,13 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2024.1" +version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] [[package]] @@ -1793,90 +1940,105 @@ pyyaml = "*" [[package]] name = "regex" -version = "2024.7.24" +version = "2024.9.11" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b0d3f567fafa0633aee87f08b9276c7062da9616931382993c03808bb68ce"}, - {file = "regex-2024.7.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3426de3b91d1bc73249042742f45c2148803c111d1175b283270177fdf669024"}, - {file = "regex-2024.7.24-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f273674b445bcb6e4409bf8d1be67bc4b58e8b46fd0d560055d515b8830063cd"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23acc72f0f4e1a9e6e9843d6328177ae3074b4182167e34119ec7233dfeccf53"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65fd3d2e228cae024c411c5ccdffae4c315271eee4a8b839291f84f796b34eca"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c414cbda77dbf13c3bc88b073a1a9f375c7b0cb5e115e15d4b73ec3a2fbc6f59"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7a89eef64b5455835f5ed30254ec19bf41f7541cd94f266ab7cbd463f00c41"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19c65b00d42804e3fbea9708f0937d157e53429a39b7c61253ff15670ff62cb5"}, - {file = "regex-2024.7.24-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7a5486ca56c8869070a966321d5ab416ff0f83f30e0e2da1ab48815c8d165d46"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f51f9556785e5a203713f5efd9c085b4a45aecd2a42573e2b5041881b588d1f"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a4997716674d36a82eab3e86f8fa77080a5d8d96a389a61ea1d0e3a94a582cf7"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c0abb5e4e8ce71a61d9446040c1e86d4e6d23f9097275c5bd49ed978755ff0fe"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:18300a1d78cf1290fa583cd8b7cde26ecb73e9f5916690cf9d42de569c89b1ce"}, - {file = "regex-2024.7.24-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:416c0e4f56308f34cdb18c3f59849479dde5b19febdcd6e6fa4d04b6c31c9faa"}, - {file = "regex-2024.7.24-cp310-cp310-win32.whl", hash = "sha256:fb168b5924bef397b5ba13aabd8cf5df7d3d93f10218d7b925e360d436863f66"}, - {file = "regex-2024.7.24-cp310-cp310-win_amd64.whl", hash = "sha256:6b9fc7e9cc983e75e2518496ba1afc524227c163e43d706688a6bb9eca41617e"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b"}, - {file = "regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51"}, - {file = "regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1"}, - {file = "regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e"}, - {file = "regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c"}, - {file = "regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad"}, - {file = "regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440"}, - {file = "regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94"}, - {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38"}, - {file = "regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc"}, - {file = "regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:66b4c0731a5c81921e938dcf1a88e978264e26e6ac4ec96a4d21ae0354581ae0"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:88ecc3afd7e776967fa16c80f974cb79399ee8dc6c96423321d6f7d4b881c92b"}, - {file = "regex-2024.7.24-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64bd50cf16bcc54b274e20235bf8edbb64184a30e1e53873ff8d444e7ac656b2"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb462f0e346fcf41a901a126b50f8781e9a474d3927930f3490f38a6e73b6950"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a82465ebbc9b1c5c50738536fdfa7cab639a261a99b469c9d4c7dcbb2b3f1e57"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68a8f8c046c6466ac61a36b65bb2395c74451df2ffb8458492ef49900efed293"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac8e84fff5d27420f3c1e879ce9929108e873667ec87e0c8eeb413a5311adfe"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba2537ef2163db9e6ccdbeb6f6424282ae4dea43177402152c67ef869cf3978b"}, - {file = "regex-2024.7.24-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:43affe33137fcd679bdae93fb25924979517e011f9dea99163f80b82eadc7e53"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c9bb87fdf2ab2370f21e4d5636e5317775e5d51ff32ebff2cf389f71b9b13750"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:945352286a541406f99b2655c973852da7911b3f4264e010218bbc1cc73168f2"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8bc593dcce679206b60a538c302d03c29b18e3d862609317cb560e18b66d10cf"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3f3b6ca8eae6d6c75a6cff525c8530c60e909a71a15e1b731723233331de4169"}, - {file = "regex-2024.7.24-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c51edc3541e11fbe83f0c4d9412ef6c79f664a3745fab261457e84465ec9d5a8"}, - {file = "regex-2024.7.24-cp38-cp38-win32.whl", hash = "sha256:d0a07763776188b4db4c9c7fb1b8c494049f84659bb387b71c73bbc07f189e96"}, - {file = "regex-2024.7.24-cp38-cp38-win_amd64.whl", hash = "sha256:8fd5afd101dcf86a270d254364e0e8dddedebe6bd1ab9d5f732f274fa00499a5"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0ffe3f9d430cd37d8fa5632ff6fb36d5b24818c5c986893063b4e5bdb84cdf24"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25419b70ba00a16abc90ee5fce061228206173231f004437730b67ac77323f0d"}, - {file = "regex-2024.7.24-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33e2614a7ce627f0cdf2ad104797d1f68342d967de3695678c0cb84f530709f8"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d33a0021893ede5969876052796165bab6006559ab845fd7b515a30abdd990dc"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04ce29e2c5fedf296b1a1b0acc1724ba93a36fb14031f3abfb7abda2806c1535"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b16582783f44fbca6fcf46f61347340c787d7530d88b4d590a397a47583f31dd"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:836d3cc225b3e8a943d0b02633fb2f28a66e281290302a79df0e1eaa984ff7c1"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:438d9f0f4bc64e8dea78274caa5af971ceff0f8771e1a2333620969936ba10be"}, - {file = "regex-2024.7.24-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:973335b1624859cb0e52f96062a28aa18f3a5fc77a96e4a3d6d76e29811a0e6e"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c5e69fd3eb0b409432b537fe3c6f44ac089c458ab6b78dcec14478422879ec5f"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fbf8c2f00904eaf63ff37718eb13acf8e178cb940520e47b2f05027f5bb34ce3"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2757ace61bc4061b69af19e4689fa4416e1a04840f33b441034202b5cd02d4"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:44fc61b99035fd9b3b9453f1713234e5a7c92a04f3577252b45feefe1b327759"}, - {file = "regex-2024.7.24-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:84c312cdf839e8b579f504afcd7b65f35d60b6285d892b19adea16355e8343c9"}, - {file = "regex-2024.7.24-cp39-cp39-win32.whl", hash = "sha256:ca5b2028c2f7af4e13fb9fc29b28d0ce767c38c7facdf64f6c2cd040413055f1"}, - {file = "regex-2024.7.24-cp39-cp39-win_amd64.whl", hash = "sha256:7c479f5ae937ec9985ecaf42e2e10631551d909f203e31308c12d703922742f9"}, - {file = "regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, + {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, + {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, ] [[package]] @@ -1919,37 +2081,37 @@ idna2008 = ["idna"] [[package]] name = "rich" -version = "13.8.0" +version = "13.9.2" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" files = [ - {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, - {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, + {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, + {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "setuptools" -version = "74.1.2" +version = "75.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-74.1.2-py3-none-any.whl", hash = "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308"}, - {file = "setuptools-74.1.2.tar.gz", hash = "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6"}, + {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, + {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, ] [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] @@ -1991,13 +2153,13 @@ files = [ [[package]] name = "syrupy" -version = "4.7.1" +version = "4.7.2" description = "Pytest Snapshot Test Utility" optional = false python-versions = ">=3.8.1" files = [ - {file = "syrupy-4.7.1-py3-none-any.whl", hash = "sha256:be002267a512a4bedddfae2e026c93df1ea928ae10baadc09640516923376d41"}, - {file = "syrupy-4.7.1.tar.gz", hash = "sha256:f9d4485f3f27d0e5df6ed299cac6fa32eb40a441915d988e82be5a4bdda335c8"}, + {file = "syrupy-4.7.2-py3-none-any.whl", hash = "sha256:eae7ba6be5aed190237caa93be288e97ca1eec5ca58760e4818972a10c4acc64"}, + {file = "syrupy-4.7.2.tar.gz", hash = "sha256:ea45e099f242de1bb53018c238f408a5bb6c82007bc687aefcbeaa0e1c2e935a"}, ] [package.dependencies] @@ -2024,13 +2186,13 @@ typing-extensions = ">=4.4.0,<5.0.0" [[package]] name = "textual-serve" -version = "1.1.0" +version = "1.1.1" description = "Turn your Textual TUIs in to web applications" optional = false python-versions = ">=3.8" files = [ - {file = "textual_serve-1.1.0-py3-none-any.whl", hash = "sha256:aaa40bc4b75f6a1e73a2d7f6e667f5555a552d8040129a95e52789a3de5d9154"}, - {file = "textual_serve-1.1.0.tar.gz", hash = "sha256:9332716464a1fca38bb5421ef29e38bdc5d304e5a15faa3ae1f8dcb185a7885c"}, + {file = "textual_serve-1.1.1-py3-none-any.whl", hash = "sha256:568782f1c0e60e3f7039d9121e1cb5c2f4ca1aaf6d6bd7aeb833d5763a534cb2"}, + {file = "textual_serve-1.1.1.tar.gz", hash = "sha256:71c662472c462e5e368defc660ee6e8eae3bfda88ca40c050c55474686eb0c54"}, ] [package.dependencies] @@ -2042,13 +2204,13 @@ textual = ">=0.66.0" [[package]] name = "tomli" -version = "2.0.1" +version = "2.0.2" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] [[package]] @@ -2259,13 +2421,13 @@ files = [ [[package]] name = "tzdata" -version = "2024.1" +version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] [[package]] @@ -2284,13 +2446,13 @@ test = ["coverage", "pytest", "pytest-cov"] [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -2301,13 +2463,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.4" +version = "20.27.0" description = "Virtual Python Environment builder" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, - {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, + {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, + {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, ] [package.dependencies] @@ -2368,118 +2530,125 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "yarl" -version = "1.11.0" +version = "1.15.2" description = "Yet another URL library" optional = false python-versions = ">=3.8" files = [ - {file = "yarl-1.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0a657db1b9982f3dac0e360614d0e8945d2873da6e681fb7fca23ef1c3eb37f8"}, - {file = "yarl-1.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65a1a05efca52b102691e64db5fcf973030a1c88fee393804ff91f99c95a6e74"}, - {file = "yarl-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f4cb417d380e2d77961eecec75aaaf6f7ab14e6de26eb3a498f498029a6556a1"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8aee7c8378c6aa3103b99d1eb9995268ef730fa9f88ea68b9eee4341e204eec9"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84624db40e2358cfd5cf2558b1aaffd93366d27ee32228a97785f2ec87d44a17"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a596bb15e036952549871a4ccd2205679902dc7f241e3ced6b2ab2e44c55795"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d4d2cc4b076c8ad0175a15ee9482a387b3303c97d4b71062db7356b2ac04c7"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f8bc849004122591104793a576e9c747b0e5d9486d6a30225521b817255748"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e38176a559edde0cfff4b663791a007a5f9f90c73aee1d6f7ddbcf6bfb7287b3"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:706ac0f77b45e9e0278ec6c98929764e119d3ce3136792b6475e7ae961da53ec"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:48bac099586cf75ae5837b0ac17a674450d01f451f38afcb02acfc940110b60b"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:540fd5f62fe21f3d1d9efe8af5c4d9dbbb184ce03ce95acb0289500e46215dd2"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05ab59db0bb64e847972373c5cda8924e6605480f6b13cc04573fa0d87bfc637"}, - {file = "yarl-1.11.0-cp310-cp310-win32.whl", hash = "sha256:ddab47748933ac9cf5f29d6e9e2e2060cff40b2751d02c55129661ea4e577152"}, - {file = "yarl-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:976d02274e6d88b24c7131e7b26a083412b2592f2bbcef53d3b00b2508cad26c"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:39e3087e1ef70862de81e22af9eb299faee580f41673ef92829949022791b521"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7fd535cc41b81a566ad347081b671ab5c7e5f5b6a15526d85b4e748baf065cf0"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f7cc02d8e9a612174869f4b983f159e87659096f7e2dc1fe9effd9902e408739"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30f391ccf4b1b1e0ba4880075ba337d41a619a5350f67053927f67ebe764bf44"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c19a0d95943bb2c914b4e71043803be34bc75c08c4a6ca232bdc649a1e9ef1b"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ead4d89eade0e09b8ef97877664abb0e2e8704787db5564f83658fdee5c36497"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:195f7791bc23d5f2480efe53f935daf8a61661000dfbfbdd70dbd06397594fff"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01a7905e662665ca8e058635377522bc3c98bdb873be761ff42c86eb72b03914"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:53c80b1927b75aed208d7fd965a3a705dc8c1db4d50b9112418fa0f7784363e6"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:11af21bbf807688d49b7d4915bb28cbc2e3aa028a2ee194738477eabcc413c65"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:732d56da00ea7a5da4f0d15adbbd22dcb37da7825510aafde40112e53f6baa52"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7bd54d79025b59d1dc5fb26a09734d6a9cc651a04bc381966ed264b28331a168"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aacd62ff67efd54cb18cea2aa7ae4fb83cfbca19a07055d4777266b70561defe"}, - {file = "yarl-1.11.0-cp311-cp311-win32.whl", hash = "sha256:68e14ae71e5b51c8282ae5db53ccb3baffc40e1551370a8a2361f1c1d8a0bf8c"}, - {file = "yarl-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:3ade2265716667b6bd4123d6f684b5f7cf4a8d83dcf1d5581ac44643466bb00a"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6e73dab98e3c3b5441720153e72a5f28e717aac2d22f1ec4b08ef33417d9987e"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4a0d090d296ced05edfe29c6ff34869412fa6a97d0928c12b00939c4842884cd"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d29e446cfb0a82d3df7745968b9fa286665a9be8b4d68de46bcc32d917cb218e"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8dc0efcf8266ecfe057b95e01f43eb62516196a4bbf3918fd1dcb8d0dc0dff"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:202f5ec49ff163dcc767426deb55020a28078e61d6bbe1f80331d92bca53b236"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8055b0d78ce1cafa657c4b455e22661e8d3b2834de66a0753c3567da47fcc4aa"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60ed3c7f64e820959d7f682ec2f559b4f4df723dc09df619d269853a4214a4b4"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2371510367d39d74997acfdcd1dead17938c79c99365482821627f7838a8eba0"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e24bb6a8be89ccc3ce8c47e8940fdfcb7429e9efbf65ce6fa3e7d122fcf0bcf0"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:18ec42da256cfcb9b4cd5d253e04c291f69911a5228d1438a7d431c15ba0ae40"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:418eeb8f228ea36c368bf6782ebd6016ecebfb1a8b90145ef6726ffcbba65ef8"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:07e8cfb1dd7669a129f8fd5df1da65efa73aea77582bde2a3a837412e2863543"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c458483711d393dad51340505c3fab3194748fd06bab311d2f8b5b7a7349e9a"}, - {file = "yarl-1.11.0-cp312-cp312-win32.whl", hash = "sha256:5b008c3127382503e7a1e12b4c3a3236e3dd833a4c62a066f4a0fbd650c655d2"}, - {file = "yarl-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc94be7472b9f88d7441340534a3ecae05c86ccfec7ba75ce5b6e4778b2bfc6e"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a45e51ba3777031e0b20c1e7ab59114ed4e1884b3c1db48962c1d8d08aefb418"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:765128029218eade3a01187cdd7f375977cc827505ed31828196c8ae9b622928"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2516e238daf0339c8ac4dfab9d7cda9afad652ff073517f200d653d5d8371f7e"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d10be62bee117f05b1ad75a6c2538ca9e5367342dc8a4f3c206c87dadbc1189c"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50ceaeda771ee3e382291168c90c7ede62b63ecf3e181024bcfeb35c0ea6c84f"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a601c99fc20fd0eea84e7bc0dc9e7f196f55a0ded67242d724988c754295538"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ff79371614764fc0a4ab8eaba9adb493bf9ad856e2a4664f6c754fc907a903"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93fca4c9f88c17ead902b3f3285b2d039fc8f26d117e1441973ba64315109b54"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e7dddf5f41395c84fc59e0ed5493b24bfeb39fb04823e880b52c8c55085d4695"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ea501ea07e14ba6364ff2621bfc8b2381e5b1e10353927fa9a607057fd2b98e5"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a4f7e470f2c9c8b8774a5bda72adfb8e9dc4ec32311fe9bdaa4921e36cf6659b"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:361fdb3993431157302b7104d525092b5df4d7d346df5a5ffeee2d1ca8e0d15b"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e300eaf5e0329ad31b3d53e2f3d26b4b6dff1217207c6ab1d4212967b54b2185"}, - {file = "yarl-1.11.0-cp313-cp313-win32.whl", hash = "sha256:f1e2d4ce72e06e38a16da3e9c24a0520dbc19018a69ef6ed57b6b38527cb275c"}, - {file = "yarl-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:fa9de2f87be58f714a230bd1f3ef3aad1ed65c9931146e3fc55f85fcbe6bacc3"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:24da0b38274727fe9266d09229987e7f0efdb97beb94c0bb2d327d65f112e78d"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0310eb2e63872de66047e05ad9982f2e53ad6405dc42fa60d7cc670bf6ca8aa8"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:52433604340a4ab3d1f32281c6eb9ad9b47c99435b4212f763121bf7348c8c00"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98e2eb182d59f0845a79434003f94b4f61cd69465248f9388c2e5bf2191c9f7f"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dd10f0fe0e0f659926c1da791de5bef05fd48974ad74618c9168e302e2b7cc"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:121d3798e4bb35a4321b2422cb887f80ea39f94bf52f0eb5cb2c168bb0043c9b"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8bbac56c80610dd659ace534765d7bcd2488f6600023f6984f35108b2b3f4f0"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79d420399f0e82e302236a762d8b8ceec89761ce3b30c83ac1d4d6e29f811444"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a726fb50588307dfe1d233b67535d493fb0bb157bdbfda6bb34e04189f2f57"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9057f5de2fade7440e6db358913bc7ae8de43ba72c83cf95420a1fc1a6c6b59e"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:6471d747d0ac8059895e66d32ca8630c8db5b572ca7763150d0927eaa257df67"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:d97cb22ad380850754fa16ef8d490d9340d8573d81f73429f3975e8e87db0586"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fe78dec8caeda1e7b353cbd8aa0cc5a5bc182b22998d64ec8fa9ee59c898ab3b"}, - {file = "yarl-1.11.0-cp38-cp38-win32.whl", hash = "sha256:7ff371002fbbb79613269d76a2932c99979dac15fac30107064ef70d25f35474"}, - {file = "yarl-1.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:4fa9d762eee63eed767895d68b994c58e29f809292a4d0fca483e9cc6fdc22c8"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4ae63bc65e5bf8843bd1eca46e75eaa9eb157e0312fb362123181512892daad8"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d1bd3262e00043907e0a6d7d4f7b7a4815281acc25699a2384552870c79f1f0"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0c58656c2e0b41b5d325130b8da4f8e216aad10029e7de5c523a6be25faa9fe8"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9425c333575fce5e0fb414b766492c6ba4aa335ef910a7540dbdefe58a78232e"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dc66e2420e1e282105071934883bbb9c37c16901b5b8aa0a8aee370b477eac6"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2949067359d1ef5bf3228c7f1deb102c209832a13df5419239f99449bc1d3fa9"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006fe73f851cf20b9986b3b4cc15239795bd5da9c3fda76bb3e043da5bec4ff"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969ad4ee3892e893471b6572bbf2bbb091f93e7c81de25d6b3a5c0a5126e5ccb"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c9fbe9dc6ee8bfe1af34137e3add6f0e49799dd5467dd6af189d27616879161e"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69a45c711fea9b783b592a75f26f6dc59b2e4a923b97bf6eec357566fcb1d922"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1a29b82c42a7791ffe53ee6dfbf29acc61ea7ec05643dcacc50510ed6187b897"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ed0c090f00c3fc024f7b0799cab9dd7c419fcd8f1a00634d1f9952bab7e7bfb2"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:31df9d9b3fe6e15decee629fc7976a5fb21eaa39e290f60e57e1d422827194c6"}, - {file = "yarl-1.11.0-cp39-cp39-win32.whl", hash = "sha256:fcb7c36ba8b663a5900e6d40533f0e698ba0f38f744aad5410d4e38129e41a70"}, - {file = "yarl-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:c6c0d640bad721834a737e25267fb71d296684ada21ca7d5ad2e63da7b73f1b7"}, - {file = "yarl-1.11.0-py3-none-any.whl", hash = "sha256:03717a6627e55934b2a1d9caf24f299b461a2e8d048a90920f42ad5c20ae1b82"}, - {file = "yarl-1.11.0.tar.gz", hash = "sha256:f86f4f4a57a29ef08fa70c4667d04c5e3ba513500da95586208b285437cb9592"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e4ee8b8639070ff246ad3649294336b06db37a94bdea0d09ea491603e0be73b8"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a7cf963a357c5f00cb55b1955df8bbe68d2f2f65de065160a1c26b85a1e44172"}, + {file = "yarl-1.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:43ebdcc120e2ca679dba01a779333a8ea76b50547b55e812b8b92818d604662c"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3433da95b51a75692dcf6cc8117a31410447c75a9a8187888f02ad45c0a86c50"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38d0124fa992dbacd0c48b1b755d3ee0a9f924f427f95b0ef376556a24debf01"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ded1b1803151dd0f20a8945508786d57c2f97a50289b16f2629f85433e546d47"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace4cad790f3bf872c082366c9edd7f8f8f77afe3992b134cfc810332206884f"}, + {file = "yarl-1.15.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c77494a2f2282d9bbbbcab7c227a4d1b4bb829875c96251f66fb5f3bae4fb053"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b7f227ca6db5a9fda0a2b935a2ea34a7267589ffc63c8045f0e4edb8d8dcf956"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:31561a5b4d8dbef1559b3600b045607cf804bae040f64b5f5bca77da38084a8a"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3e52474256a7db9dcf3c5f4ca0b300fdea6c21cca0148c8891d03a025649d935"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1af74a9529a1137c67c887ed9cde62cff53aa4d84a3adbec329f9ec47a3936"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:15c87339490100c63472a76d87fe7097a0835c705eb5ae79fd96e343473629ed"}, + {file = "yarl-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:74abb8709ea54cc483c4fb57fb17bb66f8e0f04438cff6ded322074dbd17c7ec"}, + {file = "yarl-1.15.2-cp310-cp310-win32.whl", hash = "sha256:ffd591e22b22f9cb48e472529db6a47203c41c2c5911ff0a52e85723196c0d75"}, + {file = "yarl-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:1695497bb2a02a6de60064c9f077a4ae9c25c73624e0d43e3aa9d16d983073c2"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9fcda20b2de7042cc35cf911702fa3d8311bd40055a14446c1e62403684afdc5"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0545de8c688fbbf3088f9e8b801157923be4bf8e7b03e97c2ecd4dfa39e48e0e"}, + {file = "yarl-1.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fbda058a9a68bec347962595f50546a8a4a34fd7b0654a7b9697917dc2bf810d"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ac2bc069f4a458634c26b101c2341b18da85cb96afe0015990507efec2e417"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd126498171f752dd85737ab1544329a4520c53eed3997f9b08aefbafb1cc53b"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3db817b4e95eb05c362e3b45dafe7144b18603e1211f4a5b36eb9522ecc62bcf"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:076b1ed2ac819933895b1a000904f62d615fe4533a5cf3e052ff9a1da560575c"}, + {file = "yarl-1.15.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f8cfd847e6b9ecf9f2f2531c8427035f291ec286c0a4944b0a9fce58c6446046"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:32b66be100ac5739065496c74c4b7f3015cef792c3174982809274d7e51b3e04"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:34a2d76a1984cac04ff8b1bfc939ec9dc0914821264d4a9c8fd0ed6aa8d4cfd2"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0afad2cd484908f472c8fe2e8ef499facee54a0a6978be0e0cff67b1254fd747"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c68e820879ff39992c7f148113b46efcd6ec765a4865581f2902b3c43a5f4bbb"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:98f68df80ec6ca3015186b2677c208c096d646ef37bbf8b49764ab4a38183931"}, + {file = "yarl-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56ec1eacd0a5d35b8a29f468659c47f4fe61b2cab948ca756c39b7617f0aa5"}, + {file = "yarl-1.15.2-cp311-cp311-win32.whl", hash = "sha256:eedc3f247ee7b3808ea07205f3e7d7879bc19ad3e6222195cd5fbf9988853e4d"}, + {file = "yarl-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:0ccaa1bc98751fbfcf53dc8dfdb90d96e98838010fc254180dd6707a6e8bb179"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82d5161e8cb8f36ec778fd7ac4d740415d84030f5b9ef8fe4da54784a1f46c94"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa2bea05ff0a8fb4d8124498e00e02398f06d23cdadd0fe027d84a3f7afde31e"}, + {file = "yarl-1.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99e12d2bf587b44deb74e0d6170fec37adb489964dbca656ec41a7cd8f2ff178"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:243fbbbf003754fe41b5bdf10ce1e7f80bcc70732b5b54222c124d6b4c2ab31c"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:856b7f1a7b98a8c31823285786bd566cf06226ac4f38b3ef462f593c608a9bd6"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:553dad9af802a9ad1a6525e7528152a015b85fb8dbf764ebfc755c695f488367"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30c3ff305f6e06650a761c4393666f77384f1cc6c5c0251965d6bfa5fbc88f7f"}, + {file = "yarl-1.15.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:353665775be69bbfc6d54c8d134bfc533e332149faeddd631b0bc79df0897f46"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f4fe99ce44128c71233d0d72152db31ca119711dfc5f2c82385ad611d8d7f897"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9c1e3ff4b89cdd2e1a24c214f141e848b9e0451f08d7d4963cb4108d4d798f1f"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:711bdfae4e699a6d4f371137cbe9e740dc958530cb920eb6f43ff9551e17cfbc"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4388c72174868884f76affcdd3656544c426407e0043c89b684d22fb265e04a5"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f0e1844ad47c7bd5d6fa784f1d4accc5f4168b48999303a868fe0f8597bde715"}, + {file = "yarl-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5cafb02cf097a82d74403f7e0b6b9df3ffbfe8edf9415ea816314711764a27b"}, + {file = "yarl-1.15.2-cp312-cp312-win32.whl", hash = "sha256:156ececdf636143f508770bf8a3a0498de64da5abd890c7dbb42ca9e3b6c05b8"}, + {file = "yarl-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:435aca062444a7f0c884861d2e3ea79883bd1cd19d0a381928b69ae1b85bc51d"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:416f2e3beaeae81e2f7a45dc711258be5bdc79c940a9a270b266c0bec038fb84"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:173563f3696124372831007e3d4b9821746964a95968628f7075d9231ac6bb33"}, + {file = "yarl-1.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ce2e0f6123a60bd1a7f5ae3b2c49b240c12c132847f17aa990b841a417598a2"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaea112aed589131f73d50d570a6864728bd7c0c66ef6c9154ed7b59f24da611"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4ca3b9f370f218cc2a0309542cab8d0acdfd66667e7c37d04d617012485f904"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23ec1d3c31882b2a8a69c801ef58ebf7bae2553211ebbddf04235be275a38548"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75119badf45f7183e10e348edff5a76a94dc19ba9287d94001ff05e81475967b"}, + {file = "yarl-1.15.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e6fdc976ec966b99e4daa3812fac0274cc28cd2b24b0d92462e2e5ef90d368"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8657d3f37f781d987037f9cc20bbc8b40425fa14380c87da0cb8dfce7c92d0fb"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:93bed8a8084544c6efe8856c362af08a23e959340c87a95687fdbe9c9f280c8b"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:69d5856d526802cbda768d3e6246cd0d77450fa2a4bc2ea0ea14f0d972c2894b"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ccad2800dfdff34392448c4bf834be124f10a5bc102f254521d931c1c53c455a"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a880372e2e5dbb9258a4e8ff43f13888039abb9dd6d515f28611c54361bc5644"}, + {file = "yarl-1.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c998d0558805860503bc3a595994895ca0f7835e00668dadc673bbf7f5fbfcbe"}, + {file = "yarl-1.15.2-cp313-cp313-win32.whl", hash = "sha256:533a28754e7f7439f217550a497bb026c54072dbe16402b183fdbca2431935a9"}, + {file = "yarl-1.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:5838f2b79dc8f96fdc44077c9e4e2e33d7089b10788464609df788eb97d03aad"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fbbb63bed5fcd70cd3dd23a087cd78e4675fb5a2963b8af53f945cbbca79ae16"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2e93b88ecc8f74074012e18d679fb2e9c746f2a56f79cd5e2b1afcf2a8a786b"}, + {file = "yarl-1.15.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af8ff8d7dc07ce873f643de6dfbcd45dc3db2c87462e5c387267197f59e6d776"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66f629632220a4e7858b58e4857927dd01a850a4cef2fb4044c8662787165cf7"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:833547179c31f9bec39b49601d282d6f0ea1633620701288934c5f66d88c3e50"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2aa738e0282be54eede1e3f36b81f1e46aee7ec7602aa563e81e0e8d7b67963f"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a13a07532e8e1c4a5a3afff0ca4553da23409fad65def1b71186fb867eeae8d"}, + {file = "yarl-1.15.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c45817e3e6972109d1a2c65091504a537e257bc3c885b4e78a95baa96df6a3f8"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:670eb11325ed3a6209339974b276811867defe52f4188fe18dc49855774fa9cf"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:d417a4f6943112fae3924bae2af7112562285848d9bcee737fc4ff7cbd450e6c"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bc8936d06cd53fddd4892677d65e98af514c8d78c79864f418bbf78a4a2edde4"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:954dde77c404084c2544e572f342aef384240b3e434e06cecc71597e95fd1ce7"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:5bc0df728e4def5e15a754521e8882ba5a5121bd6b5a3a0ff7efda5d6558ab3d"}, + {file = "yarl-1.15.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b71862a652f50babab4a43a487f157d26b464b1dedbcc0afda02fd64f3809d04"}, + {file = "yarl-1.15.2-cp38-cp38-win32.whl", hash = "sha256:63eab904f8630aed5a68f2d0aeab565dcfc595dc1bf0b91b71d9ddd43dea3aea"}, + {file = "yarl-1.15.2-cp38-cp38-win_amd64.whl", hash = "sha256:2cf441c4b6e538ba0d2591574f95d3fdd33f1efafa864faa077d9636ecc0c4e9"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a32d58f4b521bb98b2c0aa9da407f8bd57ca81f34362bcb090e4a79e9924fefc"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:766dcc00b943c089349d4060b935c76281f6be225e39994c2ccec3a2a36ad627"}, + {file = "yarl-1.15.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bed1b5dbf90bad3bfc19439258c97873eab453c71d8b6869c136346acfe497e7"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed20a4bdc635f36cb19e630bfc644181dd075839b6fc84cac51c0f381ac472e2"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d538df442c0d9665664ab6dd5fccd0110fa3b364914f9c85b3ef9b7b2e157980"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c6cf1d92edf936ceedc7afa61b07e9d78a27b15244aa46bbcd534c7458ee1b"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce44217ad99ffad8027d2fde0269ae368c86db66ea0571c62a000798d69401fb"}, + {file = "yarl-1.15.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47a6000a7e833ebfe5886b56a31cb2ff12120b1efd4578a6fcc38df16cc77bd"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e52f77a0cd246086afde8815039f3e16f8d2be51786c0a39b57104c563c5cbb0"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:f9ca0e6ce7774dc7830dc0cc4bb6b3eec769db667f230e7c770a628c1aa5681b"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:136f9db0f53c0206db38b8cd0c985c78ded5fd596c9a86ce5c0b92afb91c3a19"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:173866d9f7409c0fb514cf6e78952e65816600cb888c68b37b41147349fe0057"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:6e840553c9c494a35e449a987ca2c4f8372668ee954a03a9a9685075228e5036"}, + {file = "yarl-1.15.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:458c0c65802d816a6b955cf3603186de79e8fdb46d4f19abaec4ef0a906f50a7"}, + {file = "yarl-1.15.2-cp39-cp39-win32.whl", hash = "sha256:5b48388ded01f6f2429a8c55012bdbd1c2a0c3735b3e73e221649e524c34a58d"}, + {file = "yarl-1.15.2-cp39-cp39-win_amd64.whl", hash = "sha256:81dadafb3aa124f86dc267a2168f71bbd2bfb163663661ab0038f6e4b8edb810"}, + {file = "yarl-1.15.2-py3-none-any.whl", hash = "sha256:0d3105efab7c5c091609abacad33afff33bdff0035bece164c98bcf5a85ef90a"}, + {file = "yarl-1.15.2.tar.gz", hash = "sha256:a39c36f4218a5bb668b4f06874d676d35a035ee668e6e7e3538835c703634b84"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [[package]] name = "zipp" -version = "3.20.1" +version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"}, - {file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"}, + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] From 4f41dfe77866d0fcf25e1d274d631a6772968b0e Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:39:57 +0100 Subject: [PATCH 072/104] snapshot --- tests/snapshot_tests/test_snapshots.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 0516429c6b..b184b9a247 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2417,3 +2417,8 @@ def on_mount(self) -> None: self.mount(Label("HELLO")) assert snap_compare(PSApp()) + + +def test_split_segments_infinite_loop(snap_compare): + """Regression test for https://github.com/Textualize/textual/issues/5151""" + assert snap_compare(SNAPSHOT_APPS_DIR / "split_segments.py") From 9b8cbf6173c62e1f3a261a0cf261c0500783dfca Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:40:14 +0100 Subject: [PATCH 073/104] snapshot --- .../snapshot_tests/snapshot_apps/split_segments.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/snapshot_tests/snapshot_apps/split_segments.py diff --git a/tests/snapshot_tests/snapshot_apps/split_segments.py b/tests/snapshot_tests/snapshot_apps/split_segments.py new file mode 100644 index 0000000000..cb80b63e84 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/split_segments.py @@ -0,0 +1,14 @@ +from textual.app import App, ComposeResult +from textual.widgets import TextArea + +FAIL_TEXT = "x " + + +class CodeApp(App): + def compose(self) -> ComposeResult: + yield TextArea(FAIL_TEXT, soft_wrap=False, show_line_numbers=True) + + +if __name__ == "__main__": + app = CodeApp() + app.run() From 4923061d070b787693736ae3cf161ea637c5bf7e Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 22 Oct 2024 13:50:37 +0100 Subject: [PATCH 074/104] snapshot tests --- .../test_split_segments_infinite_loop.svg | 154 ++++++++++++++++++ .../snapshot_apps/split_segments.py | 2 +- tests/snapshot_tests/test_snapshots.py | 6 +- 3 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg new file mode 100644 index 0000000000..fb874be20f --- /dev/null +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_split_segments_infinite_loop.svg @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CodeApp + + + + + + + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +1  x + + + + + + + + + + + + + + + + + + + + + +▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎ + + + diff --git a/tests/snapshot_tests/snapshot_apps/split_segments.py b/tests/snapshot_tests/snapshot_apps/split_segments.py index cb80b63e84..9acb6fe28b 100644 --- a/tests/snapshot_tests/snapshot_apps/split_segments.py +++ b/tests/snapshot_tests/snapshot_apps/split_segments.py @@ -1,7 +1,7 @@ from textual.app import App, ComposeResult from textual.widgets import TextArea -FAIL_TEXT = "x " +FAIL_TEXT = "x" class CodeApp(App): diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index b184b9a247..019f49f505 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -2420,5 +2420,9 @@ def on_mount(self) -> None: def test_split_segments_infinite_loop(snap_compare): - """Regression test for https://github.com/Textualize/textual/issues/5151""" + """Regression test for https://github.com/Textualize/textual/issues/5151 + + Should be a bare-bones text editor containing "x" + + """ assert snap_compare(SNAPSHOT_APPS_DIR / "split_segments.py") From 8e755109cfba8dc615bc2d9839932ec2fe0eb596 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 23 Oct 2024 11:56:34 +0100 Subject: [PATCH 075/104] remove -screen-suspended class --- src/textual/screen.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/textual/screen.py b/src/textual/screen.py index ac452ba03f..8fbf68d5db 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -1210,7 +1210,6 @@ def _screen_resized(self, size: Size): def _on_screen_resume(self) -> None: """Screen has resumed.""" - self.remove_class("-screen-suspended") self.stack_updates += 1 self.app._refresh_notifications() size = self.app.size @@ -1230,7 +1229,6 @@ def _on_screen_resume(self) -> None: def _on_screen_suspend(self) -> None: """Screen has suspended.""" - self.add_class("-screen-suspended") self.app._set_mouse_over(None) self._clear_tooltip() self.stack_updates += 1 From faae1c6a857ad8969b754ec7d7b7bed559237b29 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 23 Oct 2024 15:57:48 +0100 Subject: [PATCH 076/104] snapshots --- CHANGELOG.md | 21 +-- src/textual/app.py | 9 +- src/textual/renderables/digits.py | 14 +- .../test_collapsible_expanded.svg | 131 +++++++++--------- .../test_snapshots/test_digits.svg | 112 +++++++-------- tests/snapshot_tests/snapshot_apps/digits.py | 6 +- .../snapshot_apps/dock_scroll_off_by_one.py | 2 + .../snapshot_tests/snapshot_apps/scroll_to.py | 2 + 8 files changed, 162 insertions(+), 135 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e8d38f745..049de85098 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,23 +5,24 @@ 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.84.0] - 2024-10-22 +## Unreleased ### Changed -- Grid will now size children to the maximum height of a row -- Markdown links will be opened with `App.open_url` automatically -- The universal selector (`*`) will now not match widgets with the class `-textual-system` (scrollbars, notifications etc) +- Grid will now size children to the maximum height of a row https://github.com/Textualize/textual/pull/5113 +- Markdown links will be opened with `App.open_url` automatically https://github.com/Textualize/textual/pull/5113 +- The universal selector (`*`) will now not match widgets with the class `-textual-system` (scrollbars, notifications etc) https://github.com/Textualize/textual/pull/5113 ### Added -- Added Link widget -- Added `open_links` to `Markdown` and `MarkdownViewer` widgets -- Added `background-tint` CSS rule https://github.com/Textualize/textual/pull/5117 -- Added `App.DEFAULT_MODE` -- Added `Containers.HorizontalGroup` and `Containers.VerticalGroup` -- Added `$`, `£`, `€` symbols to Digits +- Added Link widget https://github.com/Textualize/textual/pull/5113 +- Added `open_links` to `Markdown` and `MarkdownViewer` widgets https://github.com/Textualize/textual/pull/5113 +- Added `App.DEFAULT_MODE` https://github.com/Textualize/textual/pull/5113 +- Added `Containers.HorizontalGroup` and `Containers.VerticalGroup` https://github.com/Textualize/textual/pull/5113 +- Added `$`, `£`, `€`, `(`, `)` symbols to Digits https://github.com/Textualize/textual/pull/5113 + +## [0.84.0] - 2024-10-22 ### Fixed diff --git a/src/textual/app.py b/src/textual/app.py index 382d476d3d..438abb1b15 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -493,6 +493,9 @@ class MyApp(App[None]): SUSPENDED_SCREEN_CLASS: ClassVar[str] = "" """Class to apply to suspended screens, or empty string for no class.""" + HOVER_EFFECTS_SCROLL_PAUSE: ClassVar[float] = 0.2 + """Seconds to pause hover effects for when scrolling.""" + _PSEUDO_CLASSES: ClassVar[dict[str, Callable[[App], bool]]] = { "focus": lambda app: app.app_focus, "blur": lambda app: not app.app_focus, @@ -2703,10 +2706,12 @@ def set_focus(self, widget: Widget | None, scroll_visible: bool = True) -> None: def _pause_hover_effects(self): """Pause any hover effects based on Enter and Leave events for 200ms.""" + if not self.HOVER_EFFECTS_SCROLL_PAUSE: + return self._paused_hover_effects = True if self._hover_effects_timer is None: self._hover_effects_timer = self.set_interval( - 0.2, self._resume_hover_effects + self.HOVER_EFFECTS_SCROLL_PAUSE, self._resume_hover_effects ) else: self._hover_effects_timer.reset() @@ -2714,6 +2719,8 @@ def _pause_hover_effects(self): def _resume_hover_effects(self): """Resume sending Enter and Leave for hover effects.""" + if not self.HOVER_EFFECTS_SCROLL_PAUSE: + return if self._paused_hover_effects: self._paused_hover_effects = False if self._hover_effects_timer is not None: diff --git a/src/textual/renderables/digits.py b/src/textual/renderables/digits.py index f751387cf9..c6d982f5dc 100644 --- a/src/textual/renderables/digits.py +++ b/src/textual/renderables/digits.py @@ -5,7 +5,7 @@ from rich.segment import Segment from rich.style import Style, StyleType -DIGITS = " 0123456789+-^x:ABCDEF$£€" +DIGITS = " 0123456789+-^x:ABCDEF$£€()" DIGITS3X3_BOLD = """\ @@ -82,6 +82,12 @@ ╭─╮ ╪═ ╰─╯ +╭╴ +│ +╰╴ + ╶╮ + │ + ╶╯ """.splitlines() @@ -161,6 +167,12 @@ ╭─╮ ╪═ ╰─╯ +╭╴ +│ +╰╴ + ╶╮ + │ + ╶╯ """.splitlines() diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_collapsible_expanded.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_collapsible_expanded.svg index f734738d0b..7051555a72 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_collapsible_expanded.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_collapsible_expanded.svg @@ -19,142 +19,141 @@ font-weight: 700; } - .terminal-938527833-matrix { + .terminal-3191182536-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-938527833-title { + .terminal-3191182536-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-938527833-r1 { fill: #121212 } -.terminal-938527833-r2 { fill: #1e1e1e } -.terminal-938527833-r3 { fill: #c5c8c6 } -.terminal-938527833-r4 { fill: #ddedf9 } -.terminal-938527833-r5 { fill: #e2e2e2 } -.terminal-938527833-r6 { fill: #4ebf71;font-weight: bold } -.terminal-938527833-r7 { fill: #14191f } -.terminal-938527833-r8 { fill: #e1e1e1 } -.terminal-938527833-r9 { fill: #fea62b;font-weight: bold } -.terminal-938527833-r10 { fill: #a7a9ab } -.terminal-938527833-r11 { fill: #e2e3e3 } -.terminal-938527833-r12 { fill: #4c5055 } + .terminal-3191182536-r1 { fill: #c5c8c6 } +.terminal-3191182536-r2 { fill: #e1e1e1 } +.terminal-3191182536-r3 { fill: #121212 } +.terminal-3191182536-r4 { fill: #e2e2e2 } +.terminal-3191182536-r5 { fill: #23568b } +.terminal-3191182536-r6 { fill: #1e1e1e } +.terminal-3191182536-r7 { fill: #4ebf71;font-weight: bold } +.terminal-3191182536-r8 { fill: #fea62b;font-weight: bold } +.terminal-3191182536-r9 { fill: #a7a9ab } +.terminal-3191182536-r10 { fill: #e2e3e3 } +.terminal-3191182536-r11 { fill: #4c5055 } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - CollapsibleApp + CollapsibleApp - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -▼ Leto - -# Duke Leto I Atreides - -Head of House Atreides.                                                    - -▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -▼ Jessica - - - -Lady Jessica - -  Bene Gesserit and concubine of Leto, and mother of Paul and Alia. - - - -▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -▼ Paul▆▆ - - - - c Collapse All  e Expand All ^p palette + + + + +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +▼ Jessica + +▂▂ + +Lady Jessica + +  Bene Gesserit and concubine of Leto, and mother of Paul and Alia. + + + +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +▼ Paul + + + +Paul Atreides + +  Son of Leto and Jessica. + + + + c Collapse All  e Expand All ^p palette diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_digits.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_digits.svg index 238accbed3..25cf2879e4 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_digits.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_digits.svg @@ -19,133 +19,133 @@ font-weight: 700; } - .terminal-2016553667-matrix { + .terminal-876286072-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2016553667-title { + .terminal-876286072-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2016553667-r1 { fill: #e1e1e1 } -.terminal-2016553667-r2 { fill: #c5c8c6 } -.terminal-2016553667-r3 { fill: #e1e1e1;font-weight: bold } + .terminal-876286072-r1 { fill: #e1e1e1 } +.terminal-876286072-r2 { fill: #c5c8c6 } +.terminal-876286072-r3 { fill: #e1e1e1;font-weight: bold } - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - DigitApp + DigitApp - + - - ╶─╮ ╶╮ ╷ ╷╶─╮╶─┐                                                                 - ─┤  │ ╰─┤┌─┘  │                                                                 -╶─╯.╶┴╴  ╵╰─╴  ╵                                                                 -             ╭─╮╶╮ ╶─╮╶─╮╷ ╷╭─╴╭─╴╶─┐╭─╮╭─╮        ╭─╮┌─╮╭─╮┌─╮╭─╴╭─╴            -             │ │ │ ┌─┘ ─┤╰─┤╰─╮├─╮  │├─┤╰─┤╶┼╴╶─╴  ├─┤├─┤│  │ │├─ ├─             -             ╰─╯╶┴╴╰─╴╶─╯  ╵╶─╯╰─╯  ╵╰─╯╶─╯      .,╵ ╵└─╯╰─╯└─╯╰─╴╵              -             ┏━┓ ┓ ╺━┓╺━┓╻ ╻┏━╸┏━╸╺━┓┏━┓┏━┓        ╭─╮┌─╮╭─╮┌─╮╭─╴╭─╴            -             ┃ ┃ ┃ ┏━┛ ━┫┗━┫┗━┓┣━┓  ┃┣━┫┗━┫╺╋╸╺━╸  ├─┤├─┤│  │ │├─ ├─             -             ┗━┛╺┻╸┗━╸╺━┛  ╹╺━┛┗━┛  ╹┗━┛╺━┛      .,╵ ╵└─╯╰─╯└─╯╰─╴╵              -                                                              ╶─╮   ╶╮ ╭─╮ ^ ╷ ╷ -                                                               ─┤ ×  │ │ │   ╰─┤ -                                                              ╶─╯   ╶┴╴╰─╯     ╵ - - - - - - - - - - - - + + ╶─╮ ╶╮ ╷ ╷╶╮ ╭─╴╭─╮╶─╮╭─╴╭─╴╶─╮╭─╴╭─╮                                            + ─┤  │ ╰─┤ │ ╰─╮╰─┤┌─┘├─╮╰─╮ ─┤╰─╮╰─┤                                            +╶─╯•╶┴╴  ╵╶┴╴╶─╯╶─╯╰─╴╰─╯╶─╯╶─╯╶─╯╶─╯                                            +             ╭─╮╶╮ ╶─╮╶─╮╷ ╷╭─╴╭─╴╶─┐╭─╮╭─╮        ╭─╮┌─╮╭─╮┌─╮╭─╴╭─╴            +             │ │ │ ┌─┘ ─┤╰─┤╰─╮├─╮  │├─┤╰─┤╶┼╴╶─╴  ├─┤├─┤│  │ │├─ ├─             +             ╰─╯╶┴╴╰─╴╶─╯  ╵╶─╯╰─╯  ╵╰─╯╶─╯      •,╵ ╵└─╯╰─╯└─╯╰─╴╵              +             ┏━┓ ┓ ╺━┓╺━┓╻ ╻┏━╸┏━╸╺━┓┏━┓┏━┓        ╭─╮┌─╮╭─╮┌─╮╭─╴╭─╴            +             ┃ ┃ ┃ ┏━┛ ━┫┗━┫┗━┓┣━┓  ┃┣━┫┗━┫╺╋╸╺━╸  ├─┤├─┤│  │ │├─ ├─             +             ┗━┛╺┻╸┗━╸╺━┛  ╹╺━┛┗━┛  ╹┗━┛╺━┛      •,╵ ╵└─╯╰─╯└─╯╰─╴╵              +                                                              ╶─╮   ╶╮ ╭─╮ ^ ╷ ╷ +                                                               ─┤ ×  │ │ │   ╰─┤ +                                                              ╶─╯   ╶┴╴╰─╯     ╵ +                                                              ╶─╮   ╶╮ ╭─╮ ^ ╷ ╷ +                                                               ─┤ ×  │ │ │   ╰─┤ +                                                              ╶─╯   ╶┴╴╰─╯     ╵ +╭╴ ╭╫╮╶╮ ╶─╮╶─╮ ╷ ╷╭─╴ ╶╮                                                        +│  ╰╫╮ │ ┌─┘ ─┤ ╰─┤╰─╮  │                                                        +╰╴ ╰╫╯╶┴╴╰─╴╶─╯•  ╵╶─╯ ╶╯                                                        +╭─╮╶╮ ╶─╮╶─╮ ╷ ╷╭─╴                                                              +╪═  │ ┌─┘ ─┤ ╰─┤╰─╮                                                              +┴─╴╶┴╴╰─╴╶─╯•  ╵╶─╯                                                              +╭─╮╶╮ ╶─╮╶─╮ ╷ ╷╭─╴                                                              +╪═  │ ┌─┘ ─┤ ╰─┤╰─╮                                                              +╰─╯╶┴╴╰─╴╶─╯•  ╵╶─╯                                                              diff --git a/tests/snapshot_tests/snapshot_apps/digits.py b/tests/snapshot_tests/snapshot_apps/digits.py index 93d7c5df32..0eb16e9da2 100644 --- a/tests/snapshot_tests/snapshot_apps/digits.py +++ b/tests/snapshot_tests/snapshot_apps/digits.py @@ -19,10 +19,14 @@ class DigitApp(App): """ def compose(self) -> ComposeResult: - yield Digits("3.1427", classes="left") + yield Digits("3.14159265359", classes="left") yield Digits(" 0123456789+-.,ABCDEF", classes="center") yield Digits(" 0123456789+-.,ABCDEF", classes="center bold") yield Digits("3x10^4", classes="right") + yield Digits("3x10^4", classes="right") + yield Digits("($123.45)") + yield Digits("£123.45") + yield Digits("€123.45") if __name__ == "__main__": diff --git a/tests/snapshot_tests/snapshot_apps/dock_scroll_off_by_one.py b/tests/snapshot_tests/snapshot_apps/dock_scroll_off_by_one.py index f9a5a00fd0..3a3f531768 100644 --- a/tests/snapshot_tests/snapshot_apps/dock_scroll_off_by_one.py +++ b/tests/snapshot_tests/snapshot_apps/dock_scroll_off_by_one.py @@ -3,6 +3,8 @@ class ScrollOffByOne(App): + HOVER_EFFECTS_SCROLL_PAUSE = 0.0 + def compose(self) -> ComposeResult: for number in range(1, 100): yield Checkbox(str(number)) diff --git a/tests/snapshot_tests/snapshot_apps/scroll_to.py b/tests/snapshot_tests/snapshot_apps/scroll_to.py index 9dd21a3fc4..b3673eb269 100644 --- a/tests/snapshot_tests/snapshot_apps/scroll_to.py +++ b/tests/snapshot_tests/snapshot_apps/scroll_to.py @@ -5,6 +5,8 @@ class ScrollOffByOne(App): """Scroll to item 50.""" + HOVER_EFFECTS_SCROLL_PAUSE = 0 + def compose(self) -> ComposeResult: for number in range(1, 100): yield Checkbox(str(number), id=f"number-{number}") From b1bf8a88a5e7ea6953fff2b88c3d422f04a31da4 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 23 Oct 2024 15:59:38 +0100 Subject: [PATCH 077/104] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 049de85098..ef410b0749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `App.DEFAULT_MODE` https://github.com/Textualize/textual/pull/5113 - Added `Containers.HorizontalGroup` and `Containers.VerticalGroup` https://github.com/Textualize/textual/pull/5113 - Added `$`, `£`, `€`, `(`, `)` symbols to Digits https://github.com/Textualize/textual/pull/5113 +- Added `Button.action` parameter to invoke action when clicked https://github.com/Textualize/textual/pull/5113 ## [0.84.0] - 2024-10-22 From d005b543717413d8d0199cacbbe24af1597406cb Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 23 Oct 2024 19:09:00 +0100 Subject: [PATCH 078/104] Fix list view flicker --- src/textual/_loop.py | 43 ++++++++++++++- src/textual/widget.py | 89 +++++++++++++++++++++++++------ src/textual/widgets/_list_view.py | 72 ++++++++++++------------- 3 files changed, 150 insertions(+), 54 deletions(-) diff --git a/src/textual/_loop.py b/src/textual/_loop.py index 7a057fc214..0f50f864b3 100644 --- a/src/textual/_loop.py +++ b/src/textual/_loop.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable, TypeVar +from typing import Iterable, Literal, Sequence, TypeVar T = TypeVar("T") @@ -43,3 +43,44 @@ def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]: first = False previous_value = value yield first, True, previous_value + + +def loop_from_index( + values: Sequence[T], index: int, direction: Literal[+1, -1] = +1 +) -> Iterable[tuple[int, T]]: + """Iterate over values in a sequence from a given starting index, wrapping the index + if it would go out of bounds. + + Args: + values: A sequence of values. + index: Starting index. + direction: Direction to move index (+1 for forward, -1 for backward). + + Yields: + A tuple of index and value from the sequence. + """ + count = len(values) + for _ in range(count): + index = (index + direction) % count + yield (index, values[index]) + + +def loop_from_index_no_wrap( + values: Sequence[T], index: int, direction: Literal[+1, -1] = +1 +) -> Iterable[tuple[int, T]]: + """Iterate over values in a sequence from a given starting index, without wrapping the index. + + Args: + values: A sequence of values. + index: Starting index. + direction: Direction to move index (+1 for forward, -1 for backward). + + Yields: + A tuple of index and value from the sequence. + """ + count = len(values) + maxima = (-1, count) + for _ in range(count): + if (index := index + direction) in maxima: + break + yield (index, values[index]) diff --git a/src/textual/widget.py b/src/textual/widget.py index cf072cba6e..917efd9d8b 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -2349,6 +2349,7 @@ def scroll_to( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll to a given (absolute) coordinate, optionally animating. @@ -2362,22 +2363,37 @@ def scroll_to( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. Note: The call to scroll is made after the next refresh. """ - self.call_after_refresh( - self._scroll_to, - x, - y, - animate=animate, - speed=speed, - duration=duration, - easing=easing, - force=force, - on_complete=on_complete, - level=level, - ) + if immediate: + self._scroll_to( + x, + y, + animate=animate, + speed=speed, + duration=duration, + easing=easing, + force=force, + on_complete=on_complete, + level=level, + ) + else: + self.call_after_refresh( + self._scroll_to, + x, + y, + animate=animate, + speed=speed, + duration=duration, + easing=easing, + force=force, + on_complete=on_complete, + level=level, + ) def scroll_relative( self, @@ -2391,6 +2407,7 @@ def scroll_relative( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll relative to current position. @@ -2404,6 +2421,8 @@ def scroll_relative( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ self.scroll_to( None if x is None else (self.scroll_x + x), @@ -2415,6 +2434,7 @@ def scroll_relative( force=force, on_complete=on_complete, level=level, + immediate=immediate, ) def scroll_home( @@ -2427,6 +2447,7 @@ def scroll_home( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll to home position. @@ -2438,6 +2459,8 @@ def scroll_home( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ if speed is None and duration is None: duration = 1.0 @@ -2451,6 +2474,7 @@ def scroll_home( force=force, on_complete=on_complete, level=level, + immediate=immediate, ) def scroll_end( @@ -2463,6 +2487,7 @@ def scroll_end( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll to the end of the container. @@ -2474,6 +2499,8 @@ def scroll_end( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ if speed is None and duration is None: duration = 1.0 @@ -2510,6 +2537,7 @@ def scroll_left( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll one cell left. @@ -2521,6 +2549,8 @@ def scroll_left( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ self.scroll_to( x=self.scroll_target_x - 1, @@ -2531,6 +2561,7 @@ def scroll_left( force=force, on_complete=on_complete, level=level, + immediate=immediate, ) def _scroll_left_for_pointer( @@ -2583,6 +2614,7 @@ def scroll_right( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll one cell right. @@ -2594,6 +2626,8 @@ def scroll_right( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ self.scroll_to( x=self.scroll_target_x + 1, @@ -2604,6 +2638,7 @@ def scroll_right( force=force, on_complete=on_complete, level=level, + immediate=immediate, ) def _scroll_right_for_pointer( @@ -2656,6 +2691,7 @@ def scroll_down( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll one line down. @@ -2667,6 +2703,8 @@ def scroll_down( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ self.scroll_to( y=self.scroll_target_y + 1, @@ -2677,6 +2715,7 @@ def scroll_down( force=force, on_complete=on_complete, level=level, + immediate=immediate, ) def _scroll_down_for_pointer( @@ -2729,6 +2768,7 @@ def scroll_up( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll one line up. @@ -2740,6 +2780,8 @@ def scroll_up( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ self.scroll_to( y=self.scroll_target_y - 1, @@ -2942,6 +2984,7 @@ def scroll_to_widget( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> bool: """Scroll scrolling to bring a widget in to view. @@ -2956,6 +2999,8 @@ def scroll_to_widget( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. Returns: `True` if any scrolling has occurred in any descendant, otherwise `False`. @@ -2982,6 +3027,7 @@ def scroll_to_widget( force=force, on_complete=on_complete, level=level, + immediate=immediate, ) if scroll_offset: scrolled = True @@ -3021,6 +3067,7 @@ def scroll_to_region( level: AnimationLevel = "basic", x_axis: bool = True, y_axis: bool = True, + immediate: bool = False, ) -> Offset: """Scrolls a given region in to view, if required. @@ -3041,6 +3088,8 @@ def scroll_to_region( level: Minimum level required for the animation to take place (inclusive). x_axis: Allow scrolling on X axis? y_axis: Allow scrolling on Y axis? + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. Returns: The distance that was scrolled. @@ -3101,6 +3150,7 @@ def clamp_delta(delta: Offset) -> Offset: force=force, on_complete=on_complete, level=level, + immediate=immediate, ) return delta @@ -3115,6 +3165,7 @@ def scroll_visible( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll the container to make this widget visible. @@ -3127,11 +3178,12 @@ def scroll_visible( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ parent = self.parent if isinstance(parent, Widget): - self.call_after_refresh( - self.screen.scroll_to_widget, + self.screen.scroll_to_widget( self, animate=animate, speed=speed, @@ -3141,6 +3193,7 @@ def scroll_visible( force=force, on_complete=on_complete, level=level, + immediate=immediate, ) def scroll_to_center( @@ -3155,6 +3208,7 @@ def scroll_to_center( origin_visible: bool = True, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll this widget to the center of self. @@ -3170,9 +3224,11 @@ def scroll_to_center( origin_visible: Ensure that the top left corner of the widget remains visible after the scroll. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ - self.call_after_refresh( - self.scroll_to_widget, + + self.scroll_to_widget( widget=widget, animate=animate, speed=speed, @@ -3183,6 +3239,7 @@ def scroll_to_center( origin_visible=origin_visible, on_complete=on_complete, level=level, + immediate=immediate, ) def can_view(self, widget: Widget) -> bool: diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index a60ddf8468..e8e47f8172 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -4,7 +4,7 @@ from typing_extensions import TypeGuard -from textual import _widget_navigation +from textual._loop import loop_from_index_no_wrap from textual.await_remove import AwaitRemove from textual.binding import Binding, BindingType from textual.containers import VerticalScroll @@ -38,7 +38,7 @@ class ListView(VerticalScroll, can_focus=True, can_focus_children=False): | down | Move the cursor down. | """ - index = reactive[Optional[int]](0, always_update=True, init=False) + index = reactive[Optional[int]](None, init=False) """The index of the currently highlighted item.""" class Highlighted(Message): @@ -117,17 +117,12 @@ def __init__( super().__init__( *children, name=name, id=id, classes=classes, disabled=disabled ) - # Set the index to the given initial index, or the first available index after. - self._index = _widget_navigation.find_next_enabled( - children, - anchor=initial_index if initial_index is not None else None, - direction=1, - with_anchor=True, - ) + self._initial_index = initial_index def _on_mount(self, _: Mount) -> None: """Ensure the ListView is fully-settled after mounting.""" - self.index = self._index + with self.prevent(self.Highlighted): + self.index = self._initial_index @property def highlighted_child(self) -> ListItem | None: @@ -165,16 +160,25 @@ def _is_valid_index(self, index: int | None) -> TypeGuard[int]: def watch_index(self, old_index: int | None, new_index: int | None) -> None: """Updates the highlighting when the index changes.""" + + if new_index is not None: + self.scroll_to_widget( + self.children[new_index], animate=False, immediate=True + ) + if self._is_valid_index(old_index): old_child = self._nodes[old_index] assert isinstance(old_child, ListItem) old_child.highlighted = False - if self._is_valid_index(new_index) and not self._nodes[new_index].disabled: + if ( + new_index is not None + and self._is_valid_index(new_index) + and not self._nodes[new_index].disabled + ): new_child = self._nodes[new_index] assert isinstance(new_child, ListItem) new_child.highlighted = True - self._scroll_highlighted_region() self.post_message(self.Highlighted(self, new_child)) else: self.post_message(self.Highlighted(self, None)) @@ -271,27 +275,28 @@ def action_select_cursor(self) -> None: def action_cursor_down(self) -> None: """Highlight the next item in the list.""" - candidate = _widget_navigation.find_next_enabled( - self._nodes, - anchor=self.index, - direction=1, - ) - if self.index is not None and candidate is not None and candidate < self.index: - return # Avoid wrapping around. - - self.index = candidate + if self.index is None: + if self._nodes: + self.index = 0 + else: + index = self.index + for index, item in loop_from_index_no_wrap(self._nodes, self.index): + if not item.disabled: + self.index = index + break def action_cursor_up(self) -> None: """Highlight the previous item in the list.""" - candidate = _widget_navigation.find_next_enabled( - self._nodes, - anchor=self.index, - direction=-1, - ) - if self.index is not None and candidate is not None and candidate > self.index: - return # Avoid wrapping around. - - self.index = candidate + if self.index is None: + if self._nodes: + self.index = len(self._nodes) - 1 + else: + for index, item in loop_from_index_no_wrap( + self._nodes, self.index, direction=-1 + ): + if not item.disabled: + self.index = index + break def _on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None: event.stop() @@ -299,13 +304,6 @@ def _on_list_item__child_clicked(self, event: ListItem._ChildClicked) -> None: self.index = self._nodes.index(event.item) self.post_message(self.Selected(self, event.item)) - def _scroll_highlighted_region(self) -> None: - """Used to keep the highlighted index within vision""" - if self.highlighted_child is not None: - self.call_after_refresh( - self.scroll_to_widget, self.highlighted_child, animate=False - ) - def __len__(self) -> int: """Compute the length (in number of items) of the list view.""" return len(self._nodes) From ac85f286fa9b325a8f0362c89d8733272c4e0179 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Wed, 23 Oct 2024 19:16:01 +0100 Subject: [PATCH 079/104] loop --- CHANGELOG.md | 7 ++++++ src/textual/_loop.py | 37 ++++++++++--------------------- src/textual/widgets/_list_view.py | 8 +++---- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a7487fe28..e7dd34ab05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ 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/). +## Unreleased + +### Added + +- Added `immediate` parameter to scroll methods https://github.com/Textualize/textual/pull/5164 +- Added `textual._loop.loop_from_index` https://github.com/Textualize/textual/pull/5164 + ## [0.84.0] - 2024-10-22 ### Fixed diff --git a/src/textual/_loop.py b/src/textual/_loop.py index 0f50f864b3..7711a99d57 100644 --- a/src/textual/_loop.py +++ b/src/textual/_loop.py @@ -46,7 +46,7 @@ def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]: def loop_from_index( - values: Sequence[T], index: int, direction: Literal[+1, -1] = +1 + values: Sequence[T], index: int, direction: Literal[+1, -1] = +1, wrap: bool = True ) -> Iterable[tuple[int, T]]: """Iterate over values in a sequence from a given starting index, wrapping the index if it would go out of bounds. @@ -55,32 +55,19 @@ def loop_from_index( values: A sequence of values. index: Starting index. direction: Direction to move index (+1 for forward, -1 for backward). + bool: Should the index wrap when out of bounds? Yields: A tuple of index and value from the sequence. """ count = len(values) - for _ in range(count): - index = (index + direction) % count - yield (index, values[index]) - - -def loop_from_index_no_wrap( - values: Sequence[T], index: int, direction: Literal[+1, -1] = +1 -) -> Iterable[tuple[int, T]]: - """Iterate over values in a sequence from a given starting index, without wrapping the index. - - Args: - values: A sequence of values. - index: Starting index. - direction: Direction to move index (+1 for forward, -1 for backward). - - Yields: - A tuple of index and value from the sequence. - """ - count = len(values) - maxima = (-1, count) - for _ in range(count): - if (index := index + direction) in maxima: - break - yield (index, values[index]) + if wrap: + maxima = (-1, count) + for _ in range(count): + if (index := index + direction) in maxima: + break + yield (index, values[index]) + else: + for _ in range(count): + index = (index + direction) % count + yield (index, values[index]) diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index e8e47f8172..4ec1ff711b 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -4,7 +4,7 @@ from typing_extensions import TypeGuard -from textual._loop import loop_from_index_no_wrap +from textual._loop import loop_from_index from textual.await_remove import AwaitRemove from textual.binding import Binding, BindingType from textual.containers import VerticalScroll @@ -280,7 +280,7 @@ def action_cursor_down(self) -> None: self.index = 0 else: index = self.index - for index, item in loop_from_index_no_wrap(self._nodes, self.index): + for index, item in loop_from_index(self._nodes, self.index, wrap=False): if not item.disabled: self.index = index break @@ -291,8 +291,8 @@ def action_cursor_up(self) -> None: if self._nodes: self.index = len(self._nodes) - 1 else: - for index, item in loop_from_index_no_wrap( - self._nodes, self.index, direction=-1 + for index, item in loop_from_index( + self._nodes, self.index, direction=-1, wrap=False ): if not item.disabled: self.index = index From a1831d94639be10fde53a324ba4a2258d5711fe1 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 10:52:35 +0100 Subject: [PATCH 080/104] select first node --- src/textual/_loop.py | 10 +++++----- src/textual/scroll_view.py | 3 +++ src/textual/widgets/_list_view.py | 16 +++++++++++----- tests/listview/test_listview_initial_index.py | 2 +- .../snapshot_apps/listview_index.py | 3 ++- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/textual/_loop.py b/src/textual/_loop.py index 7711a99d57..e0ef2b76d9 100644 --- a/src/textual/_loop.py +++ b/src/textual/_loop.py @@ -48,7 +48,7 @@ def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]: def loop_from_index( values: Sequence[T], index: int, direction: Literal[+1, -1] = +1, wrap: bool = True ) -> Iterable[tuple[int, T]]: - """Iterate over values in a sequence from a given starting index, wrapping the index + """Iterate over values in a sequence from a given starting index, potentially wrapping the index if it would go out of bounds. Args: @@ -62,12 +62,12 @@ def loop_from_index( """ count = len(values) if wrap: - maxima = (-1, count) for _ in range(count): - if (index := index + direction) in maxima: - break + index = (index + direction) % count yield (index, values[index]) else: + maxima = (-1, count) for _ in range(count): - index = (index + direction) % count + if (index := index + direction) in maxima: + break yield (index, values[index]) diff --git a/src/textual/scroll_view.py b/src/textual/scroll_view.py index 56b947172f..42c89ebb23 100644 --- a/src/textual/scroll_view.py +++ b/src/textual/scroll_view.py @@ -123,6 +123,7 @@ def scroll_to( force: bool = False, on_complete: CallbackType | None = None, level: AnimationLevel = "basic", + immediate: bool = False, ) -> None: """Scroll to a given (absolute) coordinate, optionally animating. @@ -136,6 +137,8 @@ def scroll_to( force: Force scrolling even when prohibited by overflow styling. on_complete: A callable to invoke when the animation is finished. level: Minimum level required for the animation to take place (inclusive). + immediate: If `False` the scroll will be deferred until after a screen refresh, + set to `True` to scroll immediately. """ self._scroll_to( diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index 4ec1ff711b..9757899957 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -121,8 +121,16 @@ def __init__( def _on_mount(self, _: Mount) -> None: """Ensure the ListView is fully-settled after mounting.""" - with self.prevent(self.Highlighted): - self.index = self._initial_index + + if self._initial_index is not None and self.children: + index = self._initial_index + if index >= len(self.children): + index = 0 + if self._nodes[index].disabled: + for index, node in loop_from_index(self._nodes, index, wrap=True): + if not node.disabled: + break + self.index = index @property def highlighted_child(self) -> ListItem | None: @@ -162,9 +170,7 @@ def watch_index(self, old_index: int | None, new_index: int | None) -> None: """Updates the highlighting when the index changes.""" if new_index is not None: - self.scroll_to_widget( - self.children[new_index], animate=False, immediate=True - ) + self.scroll_to_widget(self._nodes[new_index], animate=False, immediate=True) if self._is_valid_index(old_index): old_child = self._nodes[old_index] diff --git a/tests/listview/test_listview_initial_index.py b/tests/listview/test_listview_initial_index.py index 515de4f044..ab2eed573e 100644 --- a/tests/listview/test_listview_initial_index.py +++ b/tests/listview/test_listview_initial_index.py @@ -39,4 +39,4 @@ def compose(self) -> ComposeResult: app = ListViewDisabledItemsApp() async with app.run_test() as pilot: list_view = pilot.app.query_one(ListView) - assert list_view._index == expected_index + assert list_view.index == expected_index diff --git a/tests/snapshot_tests/snapshot_apps/listview_index.py b/tests/snapshot_tests/snapshot_apps/listview_index.py index 1aa88aec3b..7ea856bb67 100644 --- a/tests/snapshot_tests/snapshot_apps/listview_index.py +++ b/tests/snapshot_tests/snapshot_apps/listview_index.py @@ -22,7 +22,8 @@ def compose(self) -> ComposeResult: async def watch_data(self, data: "list[int]") -> None: await self._menu.remove_children() await self._menu.extend((ListItem(Label(str(value))) for value in data)) - self._menu.index = len(self._menu) - 1 + new_index = len(self._menu) - 1 + self._menu.index = new_index async def on_ready(self): self.data = list(range(0, 30, 2)) From ab02cf75deb1956a33ef5cdcd4361ad0260e14a3 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 11:19:31 +0100 Subject: [PATCH 081/104] list view fix --- src/textual/widgets/_list_view.py | 11 ++++++++--- tests/snapshot_tests/snapshot_apps/listview_index.py | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index 9757899957..b92f1bf773 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -170,7 +170,14 @@ def watch_index(self, old_index: int | None, new_index: int | None) -> None: """Updates the highlighting when the index changes.""" if new_index is not None: - self.scroll_to_widget(self._nodes[new_index], animate=False, immediate=True) + selected_widget = self._nodes[new_index] + if selected_widget.region: + self.scroll_to_widget(self._nodes[new_index], animate=False) + else: + # Call after refresh to permit a refresh operation + self.call_after_refresh( + self.scroll_to_widget, selected_widget, animate=False + ) if self._is_valid_index(old_index): old_child = self._nodes[old_index] @@ -200,8 +207,6 @@ def extend(self, items: Iterable[ListItem]) -> AwaitMount: until the DOM has been updated with the new child items. """ await_mount = self.mount(*items) - if len(self) == 1: - self.index = 0 return await_mount def append(self, item: ListItem) -> AwaitMount: diff --git a/tests/snapshot_tests/snapshot_apps/listview_index.py b/tests/snapshot_tests/snapshot_apps/listview_index.py index 7ea856bb67..9027e2a3e0 100644 --- a/tests/snapshot_tests/snapshot_apps/listview_index.py +++ b/tests/snapshot_tests/snapshot_apps/listview_index.py @@ -22,6 +22,7 @@ def compose(self) -> ComposeResult: async def watch_data(self, data: "list[int]") -> None: await self._menu.remove_children() await self._menu.extend((ListItem(Label(str(value))) for value in data)) + new_index = len(self._menu) - 1 self._menu.index = new_index From 58116282f69dad432f5fe93b61c0ac90e0ae825a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 11:32:21 +0100 Subject: [PATCH 082/104] test for loop_from_index --- src/textual/_loop.py | 10 +++++++++- tests/test_loop.py | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/textual/_loop.py b/src/textual/_loop.py index e0ef2b76d9..ad4be901f6 100644 --- a/src/textual/_loop.py +++ b/src/textual/_loop.py @@ -46,11 +46,17 @@ def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]: def loop_from_index( - values: Sequence[T], index: int, direction: Literal[+1, -1] = +1, wrap: bool = True + values: Sequence[T], + index: int, + direction: Literal[-1, +1] = +1, + wrap: bool = True, ) -> Iterable[tuple[int, T]]: """Iterate over values in a sequence from a given starting index, potentially wrapping the index if it would go out of bounds. + Note that the first value to be yielded is a step from `index`, and `index` will be yielded *last*. + + Args: values: A sequence of values. index: Starting index. @@ -60,6 +66,8 @@ def loop_from_index( Yields: A tuple of index and value from the sequence. """ + # Sanity check for devs who miss the typing errors + assert direction in (-1, +1), "direction must be -1 or +1" count = len(values) if wrap: for _ in range(count): diff --git a/tests/test_loop.py b/tests/test_loop.py index 87ef97da76..65b0aa0097 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -1,4 +1,4 @@ -from textual._loop import loop_first, loop_first_last, loop_last +from textual._loop import loop_first, loop_first_last, loop_from_index, loop_last def test_loop_first(): @@ -26,3 +26,40 @@ def test_loop_first_last(): assert next(iterable) == (False, False, "oranges") assert next(iterable) == (False, False, "pears") assert next(iterable) == (False, True, "lemons") + + +def test_loop_from_index(): + assert list(loop_from_index("abcdefghij", 3)) == [ + (4, "e"), + (5, "f"), + (6, "g"), + (7, "h"), + (8, "i"), + (9, "j"), + (0, "a"), + (1, "b"), + (2, "c"), + (3, "d"), + ] + + assert list(loop_from_index("abcdefghij", 3, direction=-1)) == [ + (2, "c"), + (1, "b"), + (0, "a"), + (9, "j"), + (8, "i"), + (7, "h"), + (6, "g"), + (5, "f"), + (4, "e"), + (3, "d"), + ] + + assert list(loop_from_index("abcdefghij", 3, wrap=False)) == [ + (4, "e"), + (5, "f"), + (6, "g"), + (7, "h"), + (8, "i"), + (9, "j"), + ] From 2974c1f64b3aacb6cd6ebcb53f6f4163f36309cf Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 11:34:34 +0100 Subject: [PATCH 083/104] todo comment --- src/textual/_widget_navigation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/textual/_widget_navigation.py b/src/textual/_widget_navigation.py index c547d01be0..d80ae36b37 100644 --- a/src/textual/_widget_navigation.py +++ b/src/textual/_widget_navigation.py @@ -101,6 +101,8 @@ def find_last_enabled(candidates: Sequence[Disableable]) -> int | None: ) +# TODO: This method is quiet inefficient if an "anchor" is supplied +# I think we could optimize this, but its not clear if we need it at all def find_next_enabled( candidates: Sequence[Disableable], anchor: int | None, From 0dbeb743417d097ade60b7ca77f47cdc949a7731 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 11:35:34 +0100 Subject: [PATCH 084/104] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7dd34ab05..01cb767e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `immediate` parameter to scroll methods https://github.com/Textualize/textual/pull/5164 - Added `textual._loop.loop_from_index` https://github.com/Textualize/textual/pull/5164 +### Fixed + +- Fixed glitchy ListView https://github.com/Textualize/textual/issues/5163 + ## [0.84.0] - 2024-10-22 ### Fixed From 0bb81368e9af3e5bcee3271c08a5da59f942405d Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 11:38:51 +0100 Subject: [PATCH 085/104] optimization --- src/textual/_loop.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/textual/_loop.py b/src/textual/_loop.py index ad4be901f6..67546999c0 100644 --- a/src/textual/_loop.py +++ b/src/textual/_loop.py @@ -74,8 +74,13 @@ def loop_from_index( index = (index + direction) % count yield (index, values[index]) else: - maxima = (-1, count) - for _ in range(count): - if (index := index + direction) in maxima: - break - yield (index, values[index]) + if direction == +1: + for _ in range(count): + if (index := index + 1) >= count: + break + yield (index, values[index]) + else: + for _ in range(count): + if (index := index - 1) < 0: + break + yield (index, values[index]) From 2b43743cac46838645033981bd7df9624c1c89f1 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 11:45:57 +0100 Subject: [PATCH 086/104] no hover pause in tests --- src/textual/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index 438abb1b15..36fbe50f15 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -2706,7 +2706,7 @@ def set_focus(self, widget: Widget | None, scroll_visible: bool = True) -> None: def _pause_hover_effects(self): """Pause any hover effects based on Enter and Leave events for 200ms.""" - if not self.HOVER_EFFECTS_SCROLL_PAUSE: + if not self.HOVER_EFFECTS_SCROLL_PAUSE or self.is_headless: return self._paused_hover_effects = True if self._hover_effects_timer is None: @@ -2719,7 +2719,7 @@ def _pause_hover_effects(self): def _resume_hover_effects(self): """Resume sending Enter and Leave for hover effects.""" - if not self.HOVER_EFFECTS_SCROLL_PAUSE: + if not self.HOVER_EFFECTS_SCROLL_PAUSE or self.is_headless: return if self._paused_hover_effects: self._paused_hover_effects = False From 92d391eebd7f1cd68eb29126456155d8b392ec06 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 12:11:13 +0100 Subject: [PATCH 087/104] fix for stars --- src/textual/demo2/projects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/textual/demo2/projects.py b/src/textual/demo2/projects.py index 045eb24016..e5fa217491 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo2/projects.py @@ -141,13 +141,13 @@ class Project(Vertical, can_focus=True, can_focus_children=False): background: $primary 40%; opacity: 1.0; } - #title { text-style: bold; } + #title { text-style: bold; width: 1fr; } #author { text-style: italic; } .stars { color: $secondary; text-align: right; text-style: bold; - width: 1fr; + width: auto; } .header { height: 1; } .link { From 952ee22edc6d4c31560a529289790dbae1b390bd Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 24 Oct 2024 14:21:32 +0100 Subject: [PATCH 088/104] Simplify find_next_enabled --- src/textual/_profile.py | 2 +- src/textual/_widget_navigation.py | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/textual/_profile.py b/src/textual/_profile.py index 279a873ec0..3e880cf15c 100644 --- a/src/textual/_profile.py +++ b/src/textual/_profile.py @@ -16,4 +16,4 @@ def timer(subject: str = "time") -> Generator[None, None, None]: yield elapsed = perf_counter() - start elapsed_ms = elapsed * 1000 - log(f"{subject} elapsed {elapsed_ms:.2f}ms") + log(f"{subject} elapsed {elapsed_ms:.4f}ms") diff --git a/src/textual/_widget_navigation.py b/src/textual/_widget_navigation.py index d80ae36b37..1d010a17b4 100644 --- a/src/textual/_widget_navigation.py +++ b/src/textual/_widget_navigation.py @@ -8,12 +8,13 @@ from __future__ import annotations -from functools import partial from itertools import count from typing import Literal, Protocol, Sequence from typing_extensions import TypeAlias +from textual._loop import loop_from_index + class Disableable(Protocol): """Non-widgets that have an enabled/disabled status.""" @@ -136,17 +137,10 @@ def find_next_enabled( ) return None - start = anchor + direction if not with_anchor else anchor - key_function = partial( - get_directed_distance, - start=start, - direction=direction, - wrap_at=len(candidates), - ) - enabled_candidates = [ - index for index, candidate in enumerate(candidates) if not candidate.disabled - ] - return min(enabled_candidates, key=key_function, default=anchor) + for index, candidate in loop_from_index(candidates, anchor, direction, wrap=True): + if not candidate.disabled: + return index + return None def find_next_enabled_no_wrap( From 5ffa224041884583e130a55c82465c69390e2e3e Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 24 Oct 2024 14:25:31 +0100 Subject: [PATCH 089/104] Optimise optionlist/radio_set scrolling method --- src/textual/_widget_navigation.py | 2 +- tests/test_widget_navigation.py | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/textual/_widget_navigation.py b/src/textual/_widget_navigation.py index 1d010a17b4..a5e8299000 100644 --- a/src/textual/_widget_navigation.py +++ b/src/textual/_widget_navigation.py @@ -140,7 +140,7 @@ def find_next_enabled( for index, candidate in loop_from_index(candidates, anchor, direction, wrap=True): if not candidate.disabled: return index - return None + return anchor def find_next_enabled_no_wrap( diff --git a/tests/test_widget_navigation.py b/tests/test_widget_navigation.py index a322f3846f..44f8b17159 100644 --- a/tests/test_widget_navigation.py +++ b/tests/test_widget_navigation.py @@ -142,16 +142,10 @@ def test_find_next_enabled_no_wrap(candidates, anchor, direction, result): @pytest.mark.parametrize( ["function", "start", "direction"], [ - (find_next_enabled, 0, 1), - (find_next_enabled, 0, -1), (find_next_enabled_no_wrap, 0, 1), (find_next_enabled_no_wrap, 0, -1), - (find_next_enabled, 1, 1), - (find_next_enabled, 1, -1), (find_next_enabled_no_wrap, 1, 1), (find_next_enabled_no_wrap, 1, -1), - (find_next_enabled, 2, 1), - (find_next_enabled, 2, -1), (find_next_enabled_no_wrap, 2, 1), (find_next_enabled_no_wrap, 2, -1), ], From 441a6d2a23612f7a6c9892ffb7845e91b25ca58e Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 24 Oct 2024 14:26:05 +0100 Subject: [PATCH 090/104] Remove TODO --- src/textual/_widget_navigation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/textual/_widget_navigation.py b/src/textual/_widget_navigation.py index a5e8299000..023e94fb02 100644 --- a/src/textual/_widget_navigation.py +++ b/src/textual/_widget_navigation.py @@ -102,8 +102,6 @@ def find_last_enabled(candidates: Sequence[Disableable]) -> int | None: ) -# TODO: This method is quiet inefficient if an "anchor" is supplied -# I think we could optimize this, but its not clear if we need it at all def find_next_enabled( candidates: Sequence[Disableable], anchor: int | None, From 4afe6792cb8fff9450cfb649720a3de01854f5d7 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 24 Oct 2024 14:30:18 +0100 Subject: [PATCH 091/104] Remove unused param --- src/textual/_widget_navigation.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/textual/_widget_navigation.py b/src/textual/_widget_navigation.py index 023e94fb02..302a332090 100644 --- a/src/textual/_widget_navigation.py +++ b/src/textual/_widget_navigation.py @@ -106,7 +106,6 @@ def find_next_enabled( candidates: Sequence[Disableable], anchor: int | None, direction: Direction, - with_anchor: bool = False, ) -> int | None: """Find the next enabled object if we're currently at the given anchor. @@ -119,8 +118,6 @@ def find_next_enabled( enabled object. direction: The direction in which to traverse the candidates when looking for the next enabled candidate. - with_anchor: Consider the anchor position as the first valid position instead of - the last one. Returns: The next enabled object. If none are available, return the anchor. From f9d9f17fdef075643a093a491a2523950555fb67 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 15:52:17 +0100 Subject: [PATCH 092/104] lazy scrolling fixes --- src/textual/widget.py | 44 +++++++++++++------ src/textual/widgets/_collapsible.py | 2 +- .../snapshot_apps/dock_scroll_off_by_one.py | 1 + .../snapshot_tests/snapshot_apps/scroll_to.py | 1 + tests/snapshot_tests/test_snapshots.py | 5 +-- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/textual/widget.py b/src/textual/widget.py index d2350db478..71e578b5b7 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -2557,7 +2557,10 @@ def _lazily_scroll_end() -> None: level=level, ) - self.call_after_refresh(_lazily_scroll_end) + if immediate: + _lazily_scroll_end() + else: + self.call_after_refresh(_lazily_scroll_end) def scroll_left( self, @@ -3215,18 +3218,33 @@ def scroll_visible( """ parent = self.parent if isinstance(parent, Widget): - self.screen.scroll_to_widget( - self, - animate=animate, - speed=speed, - duration=duration, - top=top, - easing=easing, - force=force, - on_complete=on_complete, - level=level, - immediate=immediate, - ) + if self.region: + self.screen.scroll_to_widget( + self, + animate=animate, + speed=speed, + duration=duration, + top=top, + easing=easing, + force=force, + on_complete=on_complete, + level=level, + immediate=immediate, + ) + else: + self.call_after_refresh( + self.screen.scroll_to_widget, + self, + animate=animate, + speed=speed, + duration=duration, + top=top, + easing=easing, + force=force, + on_complete=on_complete, + level=level, + immediate=immediate, + ) def scroll_to_center( self, diff --git a/src/textual/widgets/_collapsible.py b/src/textual/widgets/_collapsible.py index 0cb791a7a7..55b181e528 100644 --- a/src/textual/widgets/_collapsible.py +++ b/src/textual/widgets/_collapsible.py @@ -203,7 +203,7 @@ def _watch_collapsed(self, collapsed: bool) -> None: self.post_message(self.Collapsed(self)) else: self.post_message(self.Expanded(self)) - self.call_later(self.scroll_visible) + self.call_after_refresh(self.scroll_visible) def _update_collapsed(self, collapsed: bool) -> None: """Update children to match collapsed state.""" diff --git a/tests/snapshot_tests/snapshot_apps/dock_scroll_off_by_one.py b/tests/snapshot_tests/snapshot_apps/dock_scroll_off_by_one.py index 3a3f531768..563cd0aba9 100644 --- a/tests/snapshot_tests/snapshot_apps/dock_scroll_off_by_one.py +++ b/tests/snapshot_tests/snapshot_apps/dock_scroll_off_by_one.py @@ -3,6 +3,7 @@ class ScrollOffByOne(App): + AUTO_FOCUS = None HOVER_EFFECTS_SCROLL_PAUSE = 0.0 def compose(self) -> ComposeResult: diff --git a/tests/snapshot_tests/snapshot_apps/scroll_to.py b/tests/snapshot_tests/snapshot_apps/scroll_to.py index b3673eb269..bded6e5ff4 100644 --- a/tests/snapshot_tests/snapshot_apps/scroll_to.py +++ b/tests/snapshot_tests/snapshot_apps/scroll_to.py @@ -5,6 +5,7 @@ class ScrollOffByOne(App): """Scroll to item 50.""" + AUTO_FOCUS = None HOVER_EFFECTS_SCROLL_PAUSE = 0 def compose(self) -> ComposeResult: diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 31d45ad122..f1a9de43ff 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -937,7 +937,6 @@ def test_dock_scroll_off_by_one(snap_compare): assert snap_compare( SNAPSHOT_APPS_DIR / "dock_scroll_off_by_one.py", terminal_size=(80, 25), - press=["_"], ) @@ -962,9 +961,7 @@ def compose(self) -> ComposeResult: def test_scroll_to(snap_compare): # https://github.com/Textualize/textual/issues/2525 - assert snap_compare( - SNAPSHOT_APPS_DIR / "scroll_to.py", terminal_size=(80, 25), press=["_"] - ) + assert snap_compare(SNAPSHOT_APPS_DIR / "scroll_to.py", terminal_size=(80, 25)) def test_auto_fr(snap_compare): From 6276d329614ec3cf27072a023720fc0d9181ad3a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 15:56:52 +0100 Subject: [PATCH 093/104] comment --- src/textual/widget.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/textual/widget.py b/src/textual/widget.py index 71e578b5b7..e6b124f191 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -3232,6 +3232,8 @@ def scroll_visible( immediate=immediate, ) else: + # self.region is falsey which may indicate the widget hasn't been through a layout operation + # We can potentially make it do the right thing by postponing the scroll to after a refresh self.call_after_refresh( self.screen.scroll_to_widget, self, From f1fce84136e3377f9c07a89c439af6390a18c87f Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Thu, 24 Oct 2024 19:24:55 +0100 Subject: [PATCH 094/104] tests(markdown): prevent tests from opening links --- tests/test_markdown.py | 2 +- tests/test_markdownviewer.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_markdown.py b/tests/test_markdown.py index ed99b2feef..ef48564cca 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -205,7 +205,7 @@ async def test_markdown_quoting(): class MyApp(App): def compose(self) -> ComposeResult: - self.md = Markdown(markdown="[tété](tété)") + self.md = Markdown(markdown="[tété](tété)", open_links=False) yield self.md def on_markdown_link_clicked(self, message: Markdown.LinkClicked): diff --git a/tests/test_markdownviewer.py b/tests/test_markdownviewer.py index 8d94d4b946..a1646f5a2e 100644 --- a/tests/test_markdownviewer.py +++ b/tests/test_markdownviewer.py @@ -29,7 +29,7 @@ def __init__(self, markdown_file: Path) -> None: markdown_file.write_text(TEST_MARKDOWN.replace("{{file}}", markdown_file.name)) def compose(self) -> ComposeResult: - yield MarkdownViewer() + yield MarkdownViewer(open_links=False) async def on_mount(self) -> None: self.query_one(MarkdownViewer).show_table_of_contents = False @@ -52,7 +52,7 @@ def __init__(self, markdown_string: str) -> None: super().__init__() def compose(self) -> ComposeResult: - yield MarkdownViewer(self.markdown_string) + yield MarkdownViewer(self.markdown_string, open_links=False) async def on_mount(self) -> None: self.query_one(MarkdownViewer).show_table_of_contents = False From 8ecb5296836f32bd246f79de0df20c5ccab97102 Mon Sep 17 00:00:00 2001 From: TomJGooding <101601846+TomJGooding@users.noreply.github.com> Date: Thu, 24 Oct 2024 19:38:42 +0100 Subject: [PATCH 095/104] tests(markdown): prevent tests from opening links --- tests/test_markdown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_markdown.py b/tests/test_markdown.py index ef48564cca..01ee498a5c 100644 --- a/tests/test_markdown.py +++ b/tests/test_markdown.py @@ -184,7 +184,7 @@ class MarkdownTableApp(App): messages = [] def compose(self) -> ComposeResult: - yield Markdown(markdown_table) + yield Markdown(markdown_table, open_links=False) @on(Markdown.LinkClicked) def log_markdown_link_clicked( From 2fc9832d754249d047e21ecaf5de3fb23cb92937 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 24 Oct 2024 20:09:37 +0100 Subject: [PATCH 096/104] logs demo --- src/textual/demo2/widgets.py | 118 ++++++++++++++++++++++++++++++- src/textual/widget.py | 23 ++++-- src/textual/widgets/_log.py | 20 ++++-- src/textual/widgets/_rich_log.py | 11 ++- 4 files changed, 157 insertions(+), 15 deletions(-) diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index 6a1eded388..a18c89d40e 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -1,7 +1,15 @@ +import csv +import io + +from rich.syntax import Syntax +from rich.table import Table +from rich.traceback import Traceback + from textual import containers from textual.app import ComposeResult from textual.demo2.data import COUNTRIES from textual.demo2.page import PageScreen +from textual.reactive import var from textual.suggester import SuggestFromList from textual.widgets import ( Button, @@ -13,11 +21,14 @@ Label, ListItem, ListView, + Log, Markdown, MaskedInput, OptionList, RadioButton, RadioSet, + RichLog, + TabbedContent, ) WIDGETS_MD = """\ @@ -219,7 +230,7 @@ class ListViews(containers.VerticalGroup): """ - DEFAULT_CSS = """\ + DEFAULT_CSS = """ ListViews { ListView { width: 1fr; @@ -244,6 +255,110 @@ def compose(self) -> ComposeResult: yield OptionList(*COUNTRIES) +class Logs(containers.VerticalGroup): + DEFAULT_CLASSES = "column" + LOGS_MD = """\ +## Logs and Rich Logs + +A Log widget to efficiently display a scrolling view of text. +And a RichLog widget to display Rich renderables. + +""" + DEFAULT_CSS = """ + Logs { + Log, RichLog { + width: 1fr; + height: 20; + border: blank; + padding: 0; + overflow-x: auto; + &:focus { + border: heavy $accent; + } + } + TabPane { padding: 0; } + } + """ + + 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.""".splitlines() + + CSV = """lane,swimmer,country,time +4,Joseph Schooling,Singapore,50.39 +2,Michael Phelps,United States,51.14 +5,Chad le Clos,South Africa,51.14 +6,László Cseh,Hungary,51.14 +3,Li Zhuhao,China,51.26 +8,Mehdy Metella,France,51.58 +7,Tom Shields,United States,51.73 +1,Aleksandr Sadovnikov,Russia,51.84""" + CSV_ROWS = list(csv.reader(io.StringIO(CSV))) + + CODE = '''\ +def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]: + """Iterate and generate a tuple with a flag for first and last value.""" + iter_values = iter(values) + try: + previous_value = next(iter_values) + except StopIteration: + return + first = True + for value in iter_values: + yield first, False, previous_value + first = False + previous_value = value + yield first, True, previous_value\ +''' + log_count = var(0) + rich_log_count = var(0) + + def compose(self) -> ComposeResult: + yield Markdown(self.LOGS_MD) + with TabbedContent("Log", "RichLog"): + yield Log(max_lines=10_000) + yield RichLog(max_lines=10_000) + + def on_mount(self) -> None: + log = self.query_one(Log) + rich_log = self.query_one(RichLog) + log.write("I am a Log Widget") + rich_log.write("I am a [b]Rich Log Widget") + self.set_interval(0.25, self.update_log) + self.set_interval(1, self.update_rich_log) + + def update_log(self) -> None: + if not self.screen.can_view(self): + return + self.log_count += 1 + self.query_one(Log).write_line(self.TEXT[self.log_count % len(self.TEXT)]) + + def update_rich_log(self) -> None: + rich_log = self.query_one(RichLog) + self.rich_log_count += 1 + log_option = self.rich_log_count % 3 + if log_option == 0: + rich_log.write("Syntax highlighted code") + rich_log.write(Syntax(self.CODE, lexer="python"), animate=True) + elif log_option == 1: + rich_log.write("A Rich Table") + table = Table(*self.CSV_ROWS[0]) + for row in self.CSV_ROWS[1:]: + table.add_row(*row) + rich_log.write(table, animate=True) + elif log_option == 2: + rich_log.write("A Rich Traceback") + try: + 1 / 0 + except Exception: + traceback = Traceback() + rich_log.write(traceback, animate=True) + + class WidgetsScreen(PageScreen): CSS = """ WidgetsScreen { @@ -266,6 +381,7 @@ def compose(self) -> ComposeResult: yield Datatables() yield Inputs() yield ListViews() + yield Logs() yield Footer() def action_unfocus(self) -> None: diff --git a/src/textual/widget.py b/src/textual/widget.py index e6b124f191..1fe04aedf8 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -2480,6 +2480,8 @@ def scroll_home( on_complete: CallbackType | None = None, level: AnimationLevel = "basic", immediate: bool = False, + x_axis: bool = True, + y_axis: bool = True, ) -> None: """Scroll to home position. @@ -2493,12 +2495,14 @@ def scroll_home( level: Minimum level required for the animation to take place (inclusive). immediate: If `False` the scroll will be deferred until after a screen refresh, set to `True` to scroll immediately. + x_axis: Allow scrolling on X axis? + y_axis: Allow scrolling on Y axis? """ if speed is None and duration is None: duration = 1.0 self.scroll_to( - 0, - 0, + 0 if x_axis else None, + 0 if y_axis else None, animate=animate, speed=speed, duration=duration, @@ -2520,6 +2524,8 @@ def scroll_end( on_complete: CallbackType | None = None, level: AnimationLevel = "basic", immediate: bool = False, + x_axis: bool = True, + y_axis: bool = True, ) -> None: """Scroll to the end of the container. @@ -2533,6 +2539,9 @@ def scroll_end( level: Minimum level required for the animation to take place (inclusive). immediate: If `False` the scroll will be deferred until after a screen refresh, set to `True` to scroll immediately. + x_axis: Allow scrolling on X axis? + y_axis: Allow scrolling on Y axis? + """ if speed is None and duration is None: duration = 1.0 @@ -2546,8 +2555,8 @@ def scroll_end( def _lazily_scroll_end() -> None: """Scroll to the end of the widget.""" self._scroll_to( - 0, - self.max_scroll_y, + 0 if x_axis else None, + self.max_scroll_y if y_axis else None, animate=animate, speed=speed, duration=duration, @@ -3313,7 +3322,7 @@ def can_view(self, widget: Widget) -> bool: node: Widget = widget while isinstance(node.parent, Widget) and node is not self: - if region not in node.parent.scrollable_content_region: + if not region.overlaps(node.parent.scrollable_content_region): return False node = node.parent return True @@ -4210,13 +4219,13 @@ def action_scroll_home(self) -> None: if not self._allow_scroll: raise SkipAction() self._clear_anchor() - self.scroll_home() + self.scroll_home(x_axis=self.scroll_y == 0) def action_scroll_end(self) -> None: if not self._allow_scroll: raise SkipAction() self._clear_anchor() - self.scroll_end() + self.scroll_end(x_axis=self.scroll_y == self.is_vertical_scroll_end) def action_scroll_left(self) -> None: if not self.allow_horizontal_scroll: diff --git a/src/textual/widgets/_log.py b/src/textual/widgets/_log.py index ee50fd87ed..ad6bf616c2 100644 --- a/src/textual/widgets/_log.py +++ b/src/textual/widgets/_log.py @@ -163,7 +163,8 @@ def write( Returns: The `Log` instance. """ - + is_vertical_scroll_end = self.is_vertical_scroll_end + print(self.scroll_offset.y, self.max_scroll_y) if data: if not self._lines: self._lines.append("") @@ -181,8 +182,12 @@ def write( 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) + if ( + auto_scroll + and not self.is_vertical_scrollbar_grabbed + and self.is_vertical_scroll_end + ): + self.scroll_end(animate=False, immediate=True, x_axis=False) return self def write_line(self, line: str) -> Self: @@ -211,6 +216,7 @@ def write_lines( Returns: The `Log` instance. """ + is_vertical_scroll_end = self.is_vertical_scroll_end auto_scroll = self.auto_scroll if scroll_end is None else scroll_end new_lines = [] for line in lines: @@ -222,8 +228,12 @@ def write_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) + if ( + auto_scroll + and not self.is_vertical_scrollbar_grabbed + and is_vertical_scroll_end + ): + self.scroll_end(animate=False, immediate=True, x_axis=False) else: self.refresh() return self diff --git a/src/textual/widgets/_rich_log.py b/src/textual/widgets/_rich_log.py index d7aa4bd3e4..a974503058 100644 --- a/src/textual/widgets/_rich_log.py +++ b/src/textual/widgets/_rich_log.py @@ -169,6 +169,7 @@ def write( expand: bool = False, shrink: bool = True, scroll_end: bool | None = None, + animate: bool = False, ) -> Self: """Write a string or a Rich renderable to the bottom of the log. @@ -186,6 +187,7 @@ def write( shrink: Permit shrinking of content to fit within the content region of the RichLog. If `width` is specified, then `shrink` will be ignored. scroll_end: Enable automatic scroll to end, or `None` to use `self.auto_scroll`. + animate: Enable animation if the log will scroll. Returns: The `RichLog` instance. @@ -200,6 +202,7 @@ def write( ) return self + is_vertical_scroll_end = self.is_vertical_scroll_end renderable = self._make_renderable(content) auto_scroll = self.auto_scroll if scroll_end is None else scroll_end @@ -266,8 +269,12 @@ def write( # the new line(s), and the height will definitely have changed. self.virtual_size = Size(self._widest_line_width, len(self.lines)) - if auto_scroll: - self.scroll_end(animate=False) + if ( + auto_scroll + and not self.is_vertical_scrollbar_grabbed + and is_vertical_scroll_end + ): + self.scroll_end(animate=animate, immediate=True, x_axis=False) return self From 17fad115fee367974880019c8f2b2f6f61867c6f Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 25 Oct 2024 13:01:23 +0100 Subject: [PATCH 097/104] sparklines --- CHANGELOG.md | 1 + src/textual/demo2/widgets.py | 69 ++++++++++++++++++++++++++++--- src/textual/widgets/_log.py | 3 +- src/textual/widgets/_sparkline.py | 25 ++++++++--- 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3ae4b5d98..0532cd9139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `Button.action` parameter to invoke action when clicked https://github.com/Textualize/textual/pull/5113 - Added `immediate` parameter to scroll methods https://github.com/Textualize/textual/pull/5164 - Added `textual._loop.loop_from_index` https://github.com/Textualize/textual/pull/5164 +- Added `min_color` and `max_color` to Sparklines constructor, which take precedence over CSS ### Fixed diff --git a/src/textual/demo2/widgets.py b/src/textual/demo2/widgets.py index a18c89d40e..597d862059 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo2/widgets.py @@ -1,5 +1,6 @@ import csv import io +from math import sin from rich.syntax import Syntax from rich.table import Table @@ -9,7 +10,7 @@ from textual.app import ComposeResult from textual.demo2.data import COUNTRIES from textual.demo2.page import PageScreen -from textual.reactive import var +from textual.reactive import reactive, var from textual.suggester import SuggestFromList from textual.widgets import ( Button, @@ -28,6 +29,7 @@ RadioButton, RadioSet, RichLog, + Sparkline, TabbedContent, ) @@ -260,7 +262,7 @@ class Logs(containers.VerticalGroup): LOGS_MD = """\ ## Logs and Rich Logs -A Log widget to efficiently display a scrolling view of text. +A Log widget to efficiently display a scrolling view of text, with optional highlighted. And a RichLog widget to display Rich renderables. """ @@ -320,7 +322,7 @@ def loop_first_last(values: Iterable[T]) -> Iterable[tuple[bool, bool, T]]: def compose(self) -> ComposeResult: yield Markdown(self.LOGS_MD) with TabbedContent("Log", "RichLog"): - yield Log(max_lines=10_000) + yield Log(max_lines=10_000, highlight=True) yield RichLog(max_lines=10_000) def on_mount(self) -> None: @@ -332,12 +334,17 @@ def on_mount(self) -> None: self.set_interval(1, self.update_rich_log) def update_log(self) -> None: - if not self.screen.can_view(self): + if not self.screen.can_view(self) or not self.screen.is_active: return self.log_count += 1 - self.query_one(Log).write_line(self.TEXT[self.log_count % len(self.TEXT)]) + log = self.query_one(Log) + line_no = self.log_count % len(self.TEXT) + line = self.TEXT[self.log_count % len(self.TEXT)] + log.write_line(f"fear[{line_no}] = {line!r}") def update_rich_log(self) -> None: + if not self.screen.can_view(self) or not self.screen.is_active: + return rich_log = self.query_one(RichLog) self.rich_log_count += 1 log_option = self.rich_log_count % 3 @@ -359,6 +366,57 @@ def update_rich_log(self) -> None: rich_log.write(traceback, animate=True) +class Sparklines(containers.VerticalGroup): + DEFAULT_CLASSES = "column" + LOGS_MD = """\ +## Sparklines + +A low-res summary of time-series data. + +For detailed graphs, see [textual-plotext](https://github.com/Textualize/textual-plotext). +""" + DEFAULT_CSS = """ + Sparklines { + Sparkline { + width: 1fr; + margin: 1; + & #first > .sparkline--min-color { color: $success; } + & #first > .sparkline--max-color { color: $warning; } + & #second > .sparkline--min-color { color: $warning; } + & #second > .sparkline--max-color { color: $error; } + & #third > .sparkline--min-color { color: $primary; } + & #third > .sparkline--max-color { color: $accent; } + } + } + + """ + + count = var(0) + data: reactive[list[float]] = reactive(list) + + def compose(self) -> ComposeResult: + yield Markdown(self.LOGS_MD) + yield Sparkline([], summary_function=max, id="first").data_bind( + Sparklines.data, + ) + yield Sparkline([], summary_function=max, id="second").data_bind( + Sparklines.data + ) + yield Sparkline([], summary_function=max, id="third").data_bind( + Sparklines.data, + ) + + def on_mount(self) -> None: + self.set_interval(0.1, self.update_sparks) + + def update_sparks(self) -> None: + if not self.screen.can_view(self) or not self.screen.is_active: + return + self.count += 1 + offset = self.count * 40 + self.data = [abs(sin(x / 3.14)) for x in range(offset, offset + 360 * 6, 20)] + + class WidgetsScreen(PageScreen): CSS = """ WidgetsScreen { @@ -382,6 +440,7 @@ def compose(self) -> ComposeResult: yield Inputs() yield ListViews() yield Logs() + yield Sparklines() yield Footer() def action_unfocus(self) -> None: diff --git a/src/textual/widgets/_log.py b/src/textual/widgets/_log.py index ad6bf616c2..0441a2c74e 100644 --- a/src/textual/widgets/_log.py +++ b/src/textual/widgets/_log.py @@ -164,7 +164,6 @@ def write( The `Log` instance. """ is_vertical_scroll_end = self.is_vertical_scroll_end - print(self.scroll_offset.y, self.max_scroll_y) if data: if not self._lines: self._lines.append("") @@ -185,7 +184,7 @@ def write( if ( auto_scroll and not self.is_vertical_scrollbar_grabbed - and self.is_vertical_scroll_end + and is_vertical_scroll_end ): self.scroll_end(animate=False, immediate=True, x_axis=False) return self diff --git a/src/textual/widgets/_sparkline.py b/src/textual/widgets/_sparkline.py index 13e302a361..9fac8f0776 100644 --- a/src/textual/widgets/_sparkline.py +++ b/src/textual/widgets/_sparkline.py @@ -3,6 +3,7 @@ from typing import Callable, ClassVar, Optional, Sequence from textual.app import RenderResult +from textual.color import Color from textual.reactive import reactive from textual.renderables.sparkline import Sparkline as SparklineRenderable from textual.widget import Widget @@ -56,6 +57,8 @@ def __init__( self, data: Sequence[float] | None = None, *, + min_color: Color | str | None = None, + max_color: Color | str | None = None, summary_function: Callable[[Sequence[float]], float] | None = None, name: str | None = None, id: str | None = None, @@ -66,6 +69,8 @@ def __init__( Args: data: The initial data to populate the sparkline with. + min_color: The color of the minimum value, or `None` to take from CSS. + max_color: the color of the maximum value, or `None` to take from CSS. summary_function: Summarizes bar values into a single value used to represent each bar. name: The name of the widget. @@ -74,6 +79,8 @@ def __init__( disabled: Whether the widget is disabled or not. """ super().__init__(name=name, id=id, classes=classes, disabled=disabled) + self.min_color = None if min_color is None else Color.parse(min_color) + self.max_color = None if max_color is None else Color.parse(max_color) self.data = data if summary_function is not None: self.summary_function = summary_function @@ -83,14 +90,20 @@ def render(self) -> RenderResult: if not self.data: return "" _, base = self.background_colors + min_color = ( + self.get_component_styles("sparkline--min-color").color + if self.min_color is None + else self.min_color + ) + max_color = ( + self.get_component_styles("sparkline--max-color").color + if self.min_color is None + else self.max_color + ) return SparklineRenderable( self.data, width=self.size.width, - min_color=( - base + self.get_component_styles("sparkline--min-color").color - ).rich_color, - max_color=( - base + self.get_component_styles("sparkline--max-color").color - ).rich_color, + min_color=min_color.rich_color, + max_color=max_color.rich_color, summary_function=self.summary_function, ) From 2a4a38bd71647cf6a742e4505b97353670c4a193 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 25 Oct 2024 13:24:30 +0100 Subject: [PATCH 098/104] replace demo --- src/textual/__main__.py | 2 +- src/textual/demo.py | 400 ------------------------ src/textual/demo.tcss | 271 ---------------- src/textual/{demo2 => demo}/__main__.py | 0 src/textual/{demo2 => demo}/data.py | 0 src/textual/{demo2 => demo}/demo_app.py | 12 +- src/textual/{demo2 => demo}/home.py | 12 +- src/textual/{demo2 => demo}/page.py | 0 src/textual/{demo2 => demo}/projects.py | 2 +- src/textual/{demo2 => demo}/widgets.py | 35 ++- 10 files changed, 46 insertions(+), 688 deletions(-) delete mode 100644 src/textual/demo.py delete mode 100644 src/textual/demo.tcss rename src/textual/{demo2 => demo}/__main__.py (100%) rename src/textual/{demo2 => demo}/data.py (100%) rename src/textual/{demo2 => demo}/demo_app.py (74%) rename src/textual/{demo2 => demo}/home.py (96%) rename src/textual/{demo2 => demo}/page.py (100%) rename src/textual/{demo2 => demo}/projects.py (99%) rename src/textual/{demo2 => demo}/widgets.py (93%) diff --git a/src/textual/__main__.py b/src/textual/__main__.py index 3bf8ea235c..9da832d968 100644 --- a/src/textual/__main__.py +++ b/src/textual/__main__.py @@ -1,4 +1,4 @@ -from textual.demo import DemoApp +from textual.demo.demo_app import DemoApp if __name__ == "__main__": app = DemoApp() diff --git a/src/textual/demo.py b/src/textual/demo.py deleted file mode 100644 index 9a5acf176d..0000000000 --- a/src/textual/demo.py +++ /dev/null @@ -1,400 +0,0 @@ -from __future__ import annotations - -from importlib.metadata import version -from pathlib import Path -from typing import cast - -from rich import box -from rich.console import RenderableType -from rich.json import JSON -from rich.markdown import Markdown -from rich.markup import escape -from rich.pretty import Pretty -from rich.syntax import Syntax -from rich.table import Table -from rich.text import Text - -from textual.app import App, ComposeResult -from textual.binding import Binding -from textual.containers import Container, Horizontal, ScrollableContainer -from textual.reactive import reactive -from textual.widgets import ( - Button, - DataTable, - Footer, - Header, - Input, - RichLog, - Static, - Switch, -) - -from_markup = Text.from_markup - -example_table = Table( - show_edge=False, - show_header=True, - expand=True, - row_styles=["none", "dim"], - box=box.SIMPLE, -) -example_table.add_column(from_markup("[green]Date"), style="green", no_wrap=True) -example_table.add_column(from_markup("[blue]Title"), style="blue") - -example_table.add_column( - from_markup("[magenta]Box Office"), - style="magenta", - justify="right", - no_wrap=True, -) -example_table.add_row( - "Dec 20, 2019", - "Star Wars: The Rise of Skywalker", - "$375,126,118", -) -example_table.add_row( - "May 25, 2018", - from_markup("[b]Solo[/]: A Star Wars Story"), - "$393,151,347", -) -example_table.add_row( - "Dec 15, 2017", - "Star Wars Ep. VIII: The Last Jedi", - from_markup("[bold]$1,332,539,889[/bold]"), -) -example_table.add_row( - "May 19, 1999", - from_markup("Star Wars Ep. [b]I[/b]: [i]The phantom Menace"), - "$1,027,044,677", -) - - -WELCOME_MD = """ - -## Textual Demo - -**Welcome**! Textual is a framework for creating sophisticated applications with the terminal. -""" - - -RICH_MD = """ - -Textual is built on **Rich**, the popular Python library for advanced terminal output. - -Add content to your Textual App with Rich *renderables* (this text is written in Markdown and formatted with Rich's Markdown class). - -Here are some examples: -""" - -CSS_MD = """ - -Textual uses Cascading Stylesheets (CSS) to create Rich interactive User Interfaces. - -- **Easy to learn** - much simpler than browser CSS -- **Live editing** - see your changes without restarting the app! - -Here's an example of some CSS used in this app: -""" - -DATA = { - "foo": [ - 3.1427, - ( - "Paul Atreides", - "Vladimir Harkonnen", - "Thufir Hawat", - "Gurney Halleck", - "Duncan Idaho", - ), - ], -} - -WIDGETS_MD = """ - -Textual widgets are powerful interactive components. - -Build your own or use the builtin widgets. - -- **Input** Text / Password input. -- **Button** Clickable button with a number of styles. -- **Switch** A switch to toggle between states. -- **DataTable** A spreadsheet-like widget for navigating data. Cells may contain text or Rich renderables. -- **Tree** An generic tree with expandable nodes. -- **DirectoryTree** A tree of file and folders. -- *... many more planned ...* -""" - - -MESSAGE = """ -We hope you enjoy using Textual. - -Here are some links. You can click these! - -[@click="app.open_link('https://textual.textualize.io')"]Textual Docs[/] - -[@click="app.open_link('https://github.com/Textualize/textual')"]Textual GitHub Repository[/] - -[@click="app.open_link('https://github.com/Textualize/rich')"]Rich GitHub Repository[/] - - -Built with ♥ by [@click="app.open_link('https://www.textualize.io')"]Textualize.io[/] -""" - - -JSON_EXAMPLE = """{ - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": { - "GlossEntry": { - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Markup Language", - "Acronym": "SGML", - "Abbrev": "ISO 8879:1986", - "GlossDef": { - "para": "A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": ["GML", "XML"] - }, - "GlossSee": "markup" - } - } - } - } -} -""" - - -class Body(ScrollableContainer): - pass - - -class Title(Static): - pass - - -class DarkSwitch(Horizontal): - def compose(self) -> ComposeResult: - yield Switch(value=self.app.dark) - yield Static("Dark mode toggle", classes="label") - - def on_mount(self) -> None: - self.watch(self.app, "dark", self.on_dark_change, init=False) - - def on_dark_change(self) -> None: - self.query_one(Switch).value = self.app.dark - - def on_switch_changed(self, event: Switch.Changed) -> None: - self.app.dark = event.value - - -class Welcome(Container): - ALLOW_MAXIMIZE = True - - def compose(self) -> ComposeResult: - yield Static(Markdown(WELCOME_MD)) - yield Button("Start", variant="success") - - def on_button_pressed(self, event: Button.Pressed) -> None: - app = cast(DemoApp, self.app) - app.add_note("[b magenta]Start!") - app.query_one(".location-first").scroll_visible(duration=0.5, top=True) - - -class OptionGroup(Container): - pass - - -class SectionTitle(Static): - pass - - -class Message(Static): - pass - - -class Version(Static): - def render(self) -> RenderableType: - return f"[b]v{version('textual')}" - - -class Sidebar(Container): - def compose(self) -> ComposeResult: - yield Title("Textual Demo") - yield OptionGroup(Message(MESSAGE), Version()) - yield DarkSwitch() - - -class AboveFold(Container): - pass - - -class Section(Container): - pass - - -class Column(Container): - pass - - -class TextContent(Static): - pass - - -class QuickAccess(Container): - pass - - -class LocationLink(Static): - def __init__(self, label: str, reveal: str) -> None: - super().__init__(label) - self.reveal = reveal - - def on_click(self) -> None: - app = cast(DemoApp, self.app) - app.query_one(self.reveal).scroll_visible(top=True, duration=0.5) - app.add_note(f"Scrolling to [b]{self.reveal}[/b]") - - -class LoginForm(Container): - ALLOW_MAXIMIZE = True - - def compose(self) -> ComposeResult: - yield Static("Username", classes="label") - yield Input(placeholder="Username") - yield Static("Password", classes="label") - yield Input(placeholder="Password", password=True) - yield Static() - yield Button("Login", variant="primary") - - -class Window(Container): - pass - - -class SubTitle(Static): - pass - - -class DemoApp(App[None]): - CSS_PATH = "demo.tcss" - TITLE = "Textual Demo" - BINDINGS = [ - ("ctrl+b", "toggle_sidebar", "Sidebar"), - ("ctrl+t", "app.toggle_dark", "Toggle Dark mode"), - ("ctrl+s", "app.screenshot()", "Screenshot"), - ("f1", "app.toggle_class('RichLog', '-hidden')", "Notes"), - Binding("ctrl+q", "app.quit", "Quit", show=True), - ] - - show_sidebar = reactive(False) - - def add_note(self, renderable: RenderableType) -> None: - 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), - RichLog(classes="-hidden", wrap=False, highlight=True, markup=True), - Body( - QuickAccess( - LocationLink("TOP", ".location-top"), - LocationLink("Widgets", ".location-widgets"), - LocationLink("Rich content", ".location-rich"), - LocationLink("CSS", ".location-css"), - ), - AboveFold(Welcome(), classes="location-top"), - Column( - Section( - SectionTitle("Widgets"), - TextContent(Markdown(WIDGETS_MD)), - LoginForm(), - DataTable(), - ), - classes="location-widgets location-first", - ), - Column( - Section( - SectionTitle("Rich"), - TextContent(Markdown(RICH_MD)), - SubTitle("Pretty Printed data (try resizing the terminal)"), - Static(Pretty(DATA, indent_guides=True), classes="pretty pad"), - SubTitle("JSON"), - Window(Static(JSON(JSON_EXAMPLE), expand=True), classes="pad"), - SubTitle("Tables"), - Static(example_table, classes="table pad"), - ), - classes="location-rich", - ), - Column( - Section( - SectionTitle("CSS"), - TextContent(Markdown(CSS_MD)), - Window( - Static( - Syntax( - example_css, - "css", - theme="material", - line_numbers=True, - ), - expand=True, - ) - ), - ), - classes="location-css", - ), - ), - ) - yield Footer() - - def action_open_link(self, link: str) -> None: - self.app.bell() - import webbrowser - - webbrowser.open(link) - - def action_toggle_sidebar(self) -> None: - sidebar = self.query_one(Sidebar) - self.set_focus(None) - if sidebar.has_class("-hidden"): - sidebar.remove_class("-hidden") - else: - if sidebar.query("*:focus"): - self.screen.set_focus(None) - sidebar.add_class("-hidden") - - def on_mount(self) -> None: - self.add_note("Textual Demo app is running") - table = self.query_one(DataTable) - table.add_column("Foo", width=20) - table.add_column("Bar", width=20) - table.add_column("Baz", width=20) - table.add_column("Foo", width=20) - table.add_column("Bar", width=20) - table.add_column("Baz", width=20) - table.zebra_stripes = True - for n in range(20): - table.add_row(*[f"Cell ([b]{n}[/b], {col})" for col in range(6)]) - self.query_one("Welcome Button", Button).focus() - - def action_screenshot(self, filename: str | None = None, path: str = "./") -> None: - """Save an SVG "screenshot". This action will save an SVG file containing the current contents of the screen. - - Args: - filename: Filename of screenshot, or None to auto-generate. - path: Path to directory. - """ - self.bell() - path = self.save_screenshot(filename, path) - message = f"Screenshot saved to [bold green]'{escape(str(path))}'[/]" - self.add_note(Text.from_markup(message)) - self.notify(message) - - -app = DemoApp() -if __name__ == "__main__": - app.run() diff --git a/src/textual/demo.tcss b/src/textual/demo.tcss deleted file mode 100644 index 4febd734be..0000000000 --- a/src/textual/demo.tcss +++ /dev/null @@ -1,271 +0,0 @@ -* { - transition: background 500ms in_out_cubic, color 500ms in_out_cubic; -} - -Screen { - layers: base overlay notes notifications; - overflow: hidden; - &:inline { - height: 50vh; - } - &.-maximized-view { - overflow: auto; - } -} - - -Notification { - dock: bottom; - layer: notification; - width: auto; - margin: 2 4; - padding: 1 2; - background: $background; - color: $text; - height: auto; - -} - -Sidebar { - width: 40; - background: $panel; - transition: offset 500ms in_out_cubic; - layer: overlay; - -} - -Sidebar:focus-within { - offset: 0 0 !important; -} - -Sidebar.-hidden { - offset-x: -100%; -} - -Sidebar Title { - background: $boost; - color: $secondary; - padding: 2 4; - border-right: vkey $background; - dock: top; - text-align: center; - text-style: bold; -} - - -OptionGroup { - background: $boost; - color: $text; - height: 1fr; - border-right: vkey $background; -} - -Option { - margin: 1 0 0 1; - height: 3; - padding: 1 2; - background: $boost; - border: tall $panel; - text-align: center; -} - -Option:hover { - background: $primary 20%; - color: $text; -} - -Body { - height: 100%; - overflow-y: scroll; - width: 100%; - background: $surface; - -} - -AboveFold { - width: 100%; - height: 100%; - align: center middle; -} - -Welcome { - background: $boost; - height: auto; - max-width: 100; - min-width: 40; - border: wide $primary; - padding: 1 2; - margin: 1 2; - box-sizing: border-box; -} - -Welcome Button { - width: 100%; - margin-top: 1; -} - -Column { - height: auto; - min-height: 100vh; - align: center top; - overflow: hidden; -} - - -DarkSwitch { - background: $panel; - padding: 1; - dock: bottom; - height: auto; - border-right: vkey $background; -} - -DarkSwitch .label { - width: 1fr; - padding: 1 2; - color: $text-muted; -} - -DarkSwitch Switch { - background: $boost; - dock: left; -} - - -Screen>Container { - height: 100%; - overflow: hidden; -} - -RichLog { - background: $surface; - color: $text; - height: 50vh; - dock: bottom; - layer: notes; - border-top: hkey $primary; - offset-y: 0; - transition: offset 400ms in_out_cubic; - padding: 0 1 1 1; -} - - -RichLog:focus { - offset: 0 0 !important; -} - -RichLog.-hidden { - offset-y: 100%; -} - - - -Section { - height: auto; - min-width: 40; - margin: 1 2 4 2; - -} - -SectionTitle { - padding: 1 2; - background: $boost; - text-align: center; - text-style: bold; -} - -SubTitle { - padding-top: 1; - border-bottom: heavy $panel; - color: $text; - text-style: bold; -} - -TextContent { - margin: 1 0; -} - -QuickAccess { - width: 30; - dock: left; - -} - -LocationLink { - margin: 1 0 0 1; - height: 1; - padding: 1 2; - background: $boost; - color: $text; - box-sizing: content-box; - content-align: center middle; -} - -LocationLink:hover { - background: $accent; - color: $text; - text-style: bold; -} - - -.pad { - margin: 1 0; -} - -DataTable { - height: 16; - max-height: 16; -} - - -LoginForm { - height: auto; - margin: 1 0; - padding: 1 2; - layout: grid; - grid-size: 2; - grid-rows: 4; - grid-columns: 12 1fr; - background: $boost; - border: wide $background; -} - -LoginForm Button { - margin: 0 1; - width: 100%; -} - -LoginForm .label { - padding: 1 2; - text-align: right; -} - -Message { - margin: 0 1; - -} - - -Tree { - margin: 1 0; -} - - -Window { - background: $boost; - overflow: auto; - height: auto; - max-height: 16; -} - -Window>Static { - width: auto; -} - - -Version { - color: $text-disabled; - dock: bottom; - text-align: center; - padding: 1; -} diff --git a/src/textual/demo2/__main__.py b/src/textual/demo/__main__.py similarity index 100% rename from src/textual/demo2/__main__.py rename to src/textual/demo/__main__.py diff --git a/src/textual/demo2/data.py b/src/textual/demo/data.py similarity index 100% rename from src/textual/demo2/data.py rename to src/textual/demo/data.py diff --git a/src/textual/demo2/demo_app.py b/src/textual/demo/demo_app.py similarity index 74% rename from src/textual/demo2/demo_app.py rename to src/textual/demo/demo_app.py index 28307d30b1..d3be0a33c2 100644 --- a/src/textual/demo2/demo_app.py +++ b/src/textual/demo/demo_app.py @@ -1,8 +1,8 @@ from textual.app import App from textual.binding import Binding -from textual.demo2.home import HomeScreen -from textual.demo2.projects import ProjectsScreen -from textual.demo2.widgets import WidgetsScreen +from textual.demo.home import HomeScreen +from textual.demo.projects import ProjectsScreen +from textual.demo.widgets import WidgetsScreen class DemoApp(App): @@ -40,4 +40,10 @@ class DemoApp(App): "widgets", tooltip="Test the builtin widgets", ), + Binding( + "ctrl+s", + "app.screenshot", + "Screenshot", + tooltip="Save an SVG 'screenshot' of the current screen", + ), ] diff --git a/src/textual/demo2/home.py b/src/textual/demo/home.py similarity index 96% rename from src/textual/demo2/home.py rename to src/textual/demo/home.py index 53611c7aaf..2ce64ddd95 100644 --- a/src/textual/demo2/home.py +++ b/src/textual/demo/home.py @@ -6,7 +6,7 @@ from textual import work from textual.app import ComposeResult from textual.containers import Horizontal, Vertical, VerticalScroll -from textual.demo2.page import PageScreen +from textual.demo.page import PageScreen from textual.reactive import reactive from textual.widgets import Collapsible, Digits, Footer, Label, Markdown @@ -41,14 +41,14 @@ ## Textual interfaces are *snappy* Even the most modern of web apps can leave the user waiting hundreds of milliseconds or more for a response. -Given their low graphical requirements, Textual interfaces can be far more responsive—no waiting required. +Given their low graphical requirements, Textual interfaces can be far more responsive — no waiting required. ## Reward repeated use Use the mouse to explore, but Textual apps are keyboard-centric and reward repeated use. An experience user can operate a Textual app far faster than their web / GUI counterparts. ## Command palette -A builtin command palette with fuzzy searching, puts powerful commands at your fingertips. +A builtin command palette with fuzzy searching puts powerful commands at your fingertips. **Try it:** Press **ctrl+p** now. @@ -57,6 +57,12 @@ API_MD = """\ A modern Python API from the developer of [Rich](https://github.com/Textualize/rich). +```python +# Start building! +import textual + +``` + Well documented, typed, and intuitive. Textual's API is accessible to Python developers of all skill levels. diff --git a/src/textual/demo2/page.py b/src/textual/demo/page.py similarity index 100% rename from src/textual/demo2/page.py rename to src/textual/demo/page.py diff --git a/src/textual/demo2/projects.py b/src/textual/demo/projects.py similarity index 99% rename from src/textual/demo2/projects.py rename to src/textual/demo/projects.py index e5fa217491..d352fbcbd3 100644 --- a/src/textual/demo2/projects.py +++ b/src/textual/demo/projects.py @@ -4,7 +4,7 @@ from textual.app import ComposeResult from textual.binding import Binding from textual.containers import Center, Horizontal, ItemGrid, Vertical, VerticalScroll -from textual.demo2.page import PageScreen +from textual.demo.page import PageScreen from textual.widgets import Footer, Label, Link, Markdown, Static diff --git a/src/textual/demo2/widgets.py b/src/textual/demo/widgets.py similarity index 93% rename from src/textual/demo2/widgets.py rename to src/textual/demo/widgets.py index 597d862059..bad0b6ad71 100644 --- a/src/textual/demo2/widgets.py +++ b/src/textual/demo/widgets.py @@ -8,8 +8,8 @@ from textual import containers from textual.app import ComposeResult -from textual.demo2.data import COUNTRIES -from textual.demo2.page import PageScreen +from textual.demo.data import COUNTRIES +from textual.demo.page import PageScreen from textual.reactive import reactive, var from textual.suggester import SuggestFromList from textual.widgets import ( @@ -96,6 +96,8 @@ def compose(self) -> ComposeResult: class Checkboxes(containers.VerticalGroup): + """Demonstrates Checkboxes.""" + DEFAULT_CLASSES = "column" DEFAULT_CSS = """ Checkboxes { @@ -137,6 +139,8 @@ def compose(self) -> ComposeResult: class Datatables(containers.VerticalGroup): + """Demonstrates DataTables.""" + DEFAULT_CLASSES = "column" DATATABLES_MD = """\ ## Datatables @@ -170,6 +174,8 @@ def on_mount(self) -> None: class Inputs(containers.VerticalGroup): + """Demonstrates Inputs.""" + DEFAULT_CLASSES = "column" INPUTS_MD = """\ ## Inputs and MaskedInputs @@ -223,6 +229,8 @@ def compose(self) -> ComposeResult: class ListViews(containers.VerticalGroup): + """Demonstrates List Views and Option Lists.""" + DEFAULT_CLASSES = "column" LISTS_MD = """\ ## List Views and Option Lists @@ -258,6 +266,8 @@ def compose(self) -> ComposeResult: class Logs(containers.VerticalGroup): + """Demonstrates Logs.""" + DEFAULT_CLASSES = "column" LOGS_MD = """\ ## Logs and Rich Logs @@ -334,6 +344,7 @@ def on_mount(self) -> None: self.set_interval(1, self.update_rich_log) def update_log(self) -> None: + """Update the Log with new content.""" if not self.screen.can_view(self) or not self.screen.is_active: return self.log_count += 1 @@ -343,6 +354,7 @@ def update_log(self) -> None: log.write_line(f"fear[{line_no}] = {line!r}") def update_rich_log(self) -> None: + """Update the Rich Log with content.""" if not self.screen.can_view(self) or not self.screen.is_active: return rich_log = self.query_one(RichLog) @@ -367,6 +379,8 @@ def update_rich_log(self) -> None: class Sparklines(containers.VerticalGroup): + """Demonstrates sparklines.""" + DEFAULT_CLASSES = "column" LOGS_MD = """\ ## Sparklines @@ -380,12 +394,12 @@ class Sparklines(containers.VerticalGroup): Sparkline { width: 1fr; margin: 1; - & #first > .sparkline--min-color { color: $success; } - & #first > .sparkline--max-color { color: $warning; } - & #second > .sparkline--min-color { color: $warning; } - & #second > .sparkline--max-color { color: $error; } - & #third > .sparkline--min-color { color: $primary; } - & #third > .sparkline--max-color { color: $accent; } + &#first > .sparkline--min-color { color: $success; } + &#first > .sparkline--max-color { color: $warning; } + &#second > .sparkline--min-color { color: $warning; } + &#second > .sparkline--max-color { color: $error; } + &#third > .sparkline--min-color { color: $primary; } + &#third > .sparkline--max-color { color: $accent; } } } @@ -407,9 +421,10 @@ def compose(self) -> ComposeResult: ) def on_mount(self) -> None: - self.set_interval(0.1, self.update_sparks) + self.set_interval(0.2, self.update_sparks) def update_sparks(self) -> None: + """Update the sparks data.""" if not self.screen.can_view(self) or not self.screen.is_active: return self.count += 1 @@ -418,6 +433,8 @@ def update_sparks(self) -> None: class WidgetsScreen(PageScreen): + """The Widgets screen""" + CSS = """ WidgetsScreen { align-horizontal: center; From d12c207ebebb0a73450720feafadc92bf7a8536c Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 25 Oct 2024 13:29:20 +0100 Subject: [PATCH 099/104] changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0532cd9139..db4fb32bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `Button.action` parameter to invoke action when clicked https://github.com/Textualize/textual/pull/5113 - Added `immediate` parameter to scroll methods https://github.com/Textualize/textual/pull/5164 - Added `textual._loop.loop_from_index` https://github.com/Textualize/textual/pull/5164 -- Added `min_color` and `max_color` to Sparklines constructor, which take precedence over CSS +- Added `min_color` and `max_color` to Sparklines constructor, which take precedence over CSS https://github.com/Textualize/textual/pull/5174 +- Added new demo `python -m textual`, not *quite* finished but better than the old one https://github.com/Textualize/textual/pull/5174 ### Fixed From c10ebca840eb88245120b992f636a1710ada77a7 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 25 Oct 2024 13:36:49 +0100 Subject: [PATCH 100/104] ws --- src/textual/demo/home.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/textual/demo/home.py b/src/textual/demo/home.py index 2ce64ddd95..06a76b6aa5 100644 --- a/src/textual/demo/home.py +++ b/src/textual/demo/home.py @@ -60,7 +60,6 @@ ```python # Start building! import textual - ``` Well documented, typed, and intuitive. From f5d6c81a018c89fa0c865ca4d0ed0044370d8a73 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 25 Oct 2024 13:45:03 +0100 Subject: [PATCH 101/104] sparkline fix --- src/textual/widgets/_sparkline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/textual/widgets/_sparkline.py b/src/textual/widgets/_sparkline.py index 9fac8f0776..5eb284b591 100644 --- a/src/textual/widgets/_sparkline.py +++ b/src/textual/widgets/_sparkline.py @@ -90,14 +90,14 @@ def render(self) -> RenderResult: if not self.data: return "" _, base = self.background_colors - min_color = ( + min_color = base + ( self.get_component_styles("sparkline--min-color").color if self.min_color is None else self.min_color ) - max_color = ( + max_color = base + ( self.get_component_styles("sparkline--max-color").color - if self.min_color is None + if self.max_color is None else self.max_color ) return SparklineRenderable( From 369a83290f79057a787d4279d40bbc3c4f41e749 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 25 Oct 2024 14:31:56 +0100 Subject: [PATCH 102/104] test fixes --- src/textual/demo/widgets.py | 8 ++++---- src/textual/widget.py | 4 ++-- src/textual/widgets/_rich_log.py | 2 +- tests/snapshot_tests/test_snapshots.py | 8 -------- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/textual/demo/widgets.py b/src/textual/demo/widgets.py index bad0b6ad71..edb3e1e6ca 100644 --- a/src/textual/demo/widgets.py +++ b/src/textual/demo/widgets.py @@ -361,16 +361,16 @@ def update_rich_log(self) -> None: self.rich_log_count += 1 log_option = self.rich_log_count % 3 if log_option == 0: - rich_log.write("Syntax highlighted code") + rich_log.write("Syntax highlighted code", animate=True) rich_log.write(Syntax(self.CODE, lexer="python"), animate=True) elif log_option == 1: - rich_log.write("A Rich Table") + rich_log.write("A Rich Table", animate=True) table = Table(*self.CSV_ROWS[0]) for row in self.CSV_ROWS[1:]: table.add_row(*row) rich_log.write(table, animate=True) elif log_option == 2: - rich_log.write("A Rich Traceback") + rich_log.write("A Rich Traceback", animate=True) try: 1 / 0 except Exception: @@ -414,7 +414,7 @@ def compose(self) -> ComposeResult: Sparklines.data, ) yield Sparkline([], summary_function=max, id="second").data_bind( - Sparklines.data + Sparklines.data, ) yield Sparkline([], summary_function=max, id="third").data_bind( Sparklines.data, diff --git a/src/textual/widget.py b/src/textual/widget.py index 1fe04aedf8..53392a69bc 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1647,12 +1647,12 @@ def max_scroll_y(self) -> int: @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 + return self.scroll_offset.y == self.max_scroll_y or not self.size @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 + return self.scroll_offset.x == self.max_scroll_x or not self.size @property def is_vertical_scrollbar_grabbed(self) -> bool: diff --git a/src/textual/widgets/_rich_log.py b/src/textual/widgets/_rich_log.py index a974503058..f0585c9dfe 100644 --- a/src/textual/widgets/_rich_log.py +++ b/src/textual/widgets/_rich_log.py @@ -274,7 +274,7 @@ def write( and not self.is_vertical_scrollbar_grabbed and is_vertical_scroll_end ): - self.scroll_end(animate=animate, immediate=True, x_axis=False) + self.scroll_end(animate=animate, immediate=False, x_axis=False) return self diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index f1a9de43ff..0fa1678090 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -599,14 +599,6 @@ def test_key_display(snap_compare): assert snap_compare(SNAPSHOT_APPS_DIR / "key_display.py") -def test_demo(snap_compare): - """Test the demo app (python -m textual)""" - assert snap_compare( - Path("../../src/textual/demo.py"), - terminal_size=(100, 30), - ) - - def test_label_widths(snap_compare): """Test renderable widths are calculate correctly.""" assert snap_compare(SNAPSHOT_APPS_DIR / "label_widths.py") From e76e42921f1f265b506f52431bb926f35083ea27 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 25 Oct 2024 15:07:34 +0100 Subject: [PATCH 103/104] can view fixes --- CHANGELOG.md | 2 ++ src/textual/demo/widgets.py | 12 ++++++------ src/textual/screen.py | 32 ++++++++++++++++++++++++++------ src/textual/widget.py | 33 ++++++++++++++++++++++++++++++--- 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db4fb32bcc..f1583c243b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Grid will now size children to the maximum height of a row https://github.com/Textualize/textual/pull/5113 - Markdown links will be opened with `App.open_url` automatically https://github.com/Textualize/textual/pull/5113 - The universal selector (`*`) will now not match widgets with the class `-textual-system` (scrollbars, notifications etc) https://github.com/Textualize/textual/pull/5113 +- Renamed `Screen.can_view` and `Widget.can_view` to `Screen.can_view_entire` and `Widget.can_view_entire` https://github.com/Textualize/textual/pull/5174 ### Added @@ -26,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added `textual._loop.loop_from_index` https://github.com/Textualize/textual/pull/5164 - Added `min_color` and `max_color` to Sparklines constructor, which take precedence over CSS https://github.com/Textualize/textual/pull/5174 - Added new demo `python -m textual`, not *quite* finished but better than the old one https://github.com/Textualize/textual/pull/5174 +- Added `Screen.can_view_partial` and `Widget.can_view_partial` https://github.com/Textualize/textual/pull/5174 ### Fixed diff --git a/src/textual/demo/widgets.py b/src/textual/demo/widgets.py index edb3e1e6ca..7432dbdde8 100644 --- a/src/textual/demo/widgets.py +++ b/src/textual/demo/widgets.py @@ -339,25 +339,25 @@ def on_mount(self) -> None: log = self.query_one(Log) rich_log = self.query_one(RichLog) log.write("I am a Log Widget") - rich_log.write("I am a [b]Rich Log Widget") + rich_log.write("I am a Rich Log Widget") self.set_interval(0.25, self.update_log) self.set_interval(1, self.update_rich_log) def update_log(self) -> None: """Update the Log with new content.""" - if not self.screen.can_view(self) or not self.screen.is_active: + log = self.query_one(Log) + if not self.screen.can_view_partial(log) or not self.screen.is_active: return self.log_count += 1 - log = self.query_one(Log) line_no = self.log_count % len(self.TEXT) line = self.TEXT[self.log_count % len(self.TEXT)] log.write_line(f"fear[{line_no}] = {line!r}") def update_rich_log(self) -> None: """Update the Rich Log with content.""" - if not self.screen.can_view(self) or not self.screen.is_active: - return rich_log = self.query_one(RichLog) + if not self.screen.can_view_partial(rich_log) or not self.screen.is_active: + return self.rich_log_count += 1 log_option = self.rich_log_count % 3 if log_option == 0: @@ -425,7 +425,7 @@ def on_mount(self) -> None: def update_sparks(self) -> None: """Update the sparks data.""" - if not self.screen.can_view(self) or not self.screen.is_active: + if not self.screen.can_view_partial(self) or not self.screen.is_active: return self.count += 1 offset = self.count * 40 diff --git a/src/textual/screen.py b/src/textual/screen.py index f60cad4aa9..ab9b187efa 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -896,7 +896,7 @@ def set_focus(self, widget: Widget | None, scroll_visible: bool = True) -> None: def scroll_to_center(widget: Widget) -> None: """Scroll to center (after a refresh).""" - if self.focused is widget and not self.can_view(widget): + if self.focused is widget and not self.can_view_entire(widget): self.scroll_to_center(widget, origin_visible=True) self.call_later(scroll_to_center, widget) @@ -1480,24 +1480,44 @@ async def action_dismiss(self, result: ScreenResultType | None = None) -> None: await self._flush_next_callbacks() self.dismiss(result) - def can_view(self, widget: Widget) -> bool: - """Check if a given widget is in the current view (scrollable area). + def can_view_entire(self, widget: Widget) -> bool: + """Check if a given widget is fully within the current screen. Note: This doesn't necessarily equate to a widget being visible. There are other reasons why a widget may not be visible. Args: - widget: A widget that is a descendant of self. + widget: A widget. Returns: - True if the entire widget is in view, False if it is partially visible or not in view. + `True` if the entire widget is in view, `False` if it is partially visible or not in view. """ + if widget not in self._compositor.visible_widgets: + return False + # If the widget is one that overlays the screen... + if widget.styles.overlay == "screen": + # ...simply check if it's within the screen's region. + return widget.region in self.region + # Failing that fall back to normal checking. + return super().can_view_entire(widget) + + def can_view_partial(self, widget: Widget) -> bool: + """Check if a given widget is at least partially within the current view. + + Args: + widget: A widget. + + Returns: + `True` if the any part of the widget is in view, `False` if it is completely outside of the screen. + """ + if widget not in self._compositor.visible_widgets: + return False # If the widget is one that overlays the screen... if widget.styles.overlay == "screen": # ...simply check if it's within the screen's region. return widget.region in self.region # Failing that fall back to normal checking. - return super().can_view(widget) + return super().can_view_partial(widget) def validate_title(self, title: Any) -> str | None: """Ensure the title is a string or `None`.""" diff --git a/src/textual/widget.py b/src/textual/widget.py index 53392a69bc..7c71bb885c 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -3303,8 +3303,8 @@ def scroll_to_center( immediate=immediate, ) - def can_view(self, widget: Widget) -> bool: - """Check if a given widget is in the current view (scrollable area). + def can_view_entire(self, widget: Widget) -> bool: + """Check if a given widget is *fully* within the current view (scrollable area). Note: This doesn't necessarily equate to a widget being visible. There are other reasons why a widget may not be visible. @@ -3313,11 +3313,38 @@ def can_view(self, widget: Widget) -> bool: widget: A widget that is a descendant of self. Returns: - True if the entire widget is in view, False if it is partially visible or not in view. + `True` if the entire widget is in view, `False` if it is partially visible or not in view. """ if widget is self: return True + if widget not in self.screen._compositor.visible_widgets: + return False + + region = widget.region + node: Widget = widget + + while isinstance(node.parent, Widget) and node is not self: + if region not in node.parent.scrollable_content_region: + return False + node = node.parent + return True + + def can_view_partial(self, widget: Widget) -> bool: + """Check if a given widget at least partially visible within the current view (scrollable area). + + Args: + widget: A widget that is a descendant of self. + + Returns: + `True` if any part of the widget is visible, `False` if it is outside of the viewable area. + """ + if widget is self: + return True + + if widget not in self.screen._compositor.visible_widgets or not widget.display: + return False + region = widget.region node: Widget = widget From ff336342bc98259ff2512fa2c3754281e7259515 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 25 Oct 2024 15:22:13 +0100 Subject: [PATCH 104/104] removed old snapshot --- .../test_snapshots/test_demo.svg | 189 ------------------ 1 file changed, 189 deletions(-) delete mode 100644 tests/snapshot_tests/__snapshots__/test_snapshots/test_demo.svg diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_demo.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_demo.svg deleted file mode 100644 index 44fe56e48f..0000000000 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_demo.svg +++ /dev/null @@ -1,189 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Textual Demo - - - - - - - - - - Textual Demo - - -TOP - -▆▆ - -Widgets -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - - -Rich contentTextual Demo - -Welcome! Textual is a framework for creating sophisticated -applications with the terminal.                            -CSS -▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - Start  -▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ - -▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - - - - - - - - - ^b Sidebar  ^t Toggle Dark mode  ^s Screenshot  f1 Notes  ^q Quit ^p palette - - -