From a0f812fa302eca82b2cdd7df72195312f1318e2e Mon Sep 17 00:00:00 2001 From: Fabio Zadrozny Date: Mon, 30 Sep 2024 16:09:15 -0300 Subject: [PATCH] When configuring the input, a default input with valid values is generated and the input file may have multiple inputs defined. Also added support for using pydev to run python (and not just microsoft.python). --- .../src/sema4ai_ls_core/basic.py | 20 +- .../src/sema4ai_ls_core/jsonrpc/monitor.py | 4 +- .../src/sema4ai_ls_core/protocols.py | 18 +- .../unittest_tools/fixtures.py | 2 +- sema4ai/poetry.lock | 317 +++++++++--------- sema4ai/pyproject.toml | 2 +- sema4ai/src/sema4ai_code/compute_launch.py | 2 +- .../sema4ai_code/robo/actions_form_data.py | 275 +++++++++++++++ .../src/sema4ai_code/robo/collect_actions.py | 170 ++++++++++ .../sema4ai_code/robo/collect_actions_ast.py | 4 +- sema4ai/src/sema4ai_code/robo/lint_action.py | 3 +- .../sema4ai_code/robocorp_language_server.py | 60 +++- .../_resources/action_package/package.yaml | 37 +- .../robo/test_list_actions.py | 125 +++++++ .../test_list_actions_full_just_enum_.yml | 26 ++ .../test_list_actions_full_just_oauth2_.yml | 23 ++ ...test_list_actions_full_multiple_types_.yml | 100 ++++++ sema4ai/vscode-client/src/debugger.ts | 23 +- .../vscode-client/src/pythonExtIntegration.ts | 16 + .../vscode-client/src/robo/actionInputs.ts | 92 +++++ .../vscode-client/src/robo/actionPackage.ts | 72 +++- .../vscode-client/src/robo/oauth2InInput.ts | 11 +- sema4ai/vscode-client/src/views.ts | 25 +- 23 files changed, 1199 insertions(+), 228 deletions(-) create mode 100644 sema4ai/src/sema4ai_code/robo/actions_form_data.py create mode 100644 sema4ai/src/sema4ai_code/robo/collect_actions.py create mode 100644 sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_just_enum_.yml create mode 100644 sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_just_oauth2_.yml create mode 100644 sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_multiple_types_.yml create mode 100644 sema4ai/vscode-client/src/robo/actionInputs.ts diff --git a/sema4ai-python-ls-core/src/sema4ai_ls_core/basic.py b/sema4ai-python-ls-core/src/sema4ai_ls_core/basic.py index 9963787bf..10367acfe 100644 --- a/sema4ai-python-ls-core/src/sema4ai_ls_core/basic.py +++ b/sema4ai-python-ls-core/src/sema4ai_ls_core/basic.py @@ -18,16 +18,17 @@ import os import sys import threading +from collections.abc import Callable from concurrent.futures import Future from contextlib import contextmanager from dataclasses import dataclass from functools import lru_cache -from typing import Any, Tuple, TypeVar -from collections.abc import Callable +from typing import Any, Optional, TypeVar from sema4ai_ls_core.core_log import get_logger from sema4ai_ls_core.jsonrpc.exceptions import JsonRpcRequestCancelled from sema4ai_ls_core.options import DEFAULT_TIMEOUT +from sema4ai_ls_core.protocols import IMonitor PARENT_PROCESS_WATCH_INTERVAL = 3 # 3 s @@ -395,7 +396,12 @@ class ProcessRunResult: def launch_and_return_future( - cmd, environ, cwd, timeout=100, stdin: bytes = b"\n" + cmd, + environ, + cwd, + timeout=100, + stdin: bytes = b"\n", + monitor: Optional[IMonitor] = None, ) -> "Future[ProcessRunResult]": import subprocess @@ -469,6 +475,14 @@ def report_output(): except BaseException as e: future.set_exception(e) + if monitor is not None: + + def on_monitor_cancelled(): + if process.poll() is None: # i.e.: still running. + process.kill() + + monitor.add_listener(on_monitor_cancelled) + threading.Thread(target=report_output, daemon=True).start() return future diff --git a/sema4ai-python-ls-core/src/sema4ai_ls_core/jsonrpc/monitor.py b/sema4ai-python-ls-core/src/sema4ai_ls_core/jsonrpc/monitor.py index 14f0788bb..e1bbe11be 100644 --- a/sema4ai-python-ls-core/src/sema4ai_ls_core/jsonrpc/monitor.py +++ b/sema4ai-python-ls-core/src/sema4ai_ls_core/jsonrpc/monitor.py @@ -1,5 +1,5 @@ -from sema4ai_ls_core.protocols import IMonitor, IMonitorListener from sema4ai_ls_core.core_log import get_logger +from sema4ai_ls_core.protocols import IMonitor, IMonitorListener log = get_logger(__name__) @@ -10,7 +10,7 @@ def __init__(self, title: str = ""): self._cancelled: bool = False self._listeners: tuple[IMonitorListener, ...] = () - def add_listener(self, listener): + def add_listener(self, listener: IMonitorListener): if self._cancelled: listener() else: diff --git a/sema4ai-python-ls-core/src/sema4ai_ls_core/protocols.py b/sema4ai-python-ls-core/src/sema4ai_ls_core/protocols.py index 1e9bb707f..74ecf9cbe 100644 --- a/sema4ai-python-ls-core/src/sema4ai_ls_core/protocols.py +++ b/sema4ai-python-ls-core/src/sema4ai_ls_core/protocols.py @@ -1,19 +1,9 @@ import sys import threading import typing -from enum import Enum -from typing import ( - Any, - Dict, - Generic, - List, - Optional, - Tuple, - Type, - TypeVar, - Union, -) from collections.abc import Callable, Iterable, Mapping +from enum import Enum +from typing import Any, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union if typing.TYPE_CHECKING: # This would lead to a circular import, so, do it only when type-checking. @@ -909,7 +899,9 @@ def check_cancelled(self) -> None: """ def add_listener(self, listener: IMonitorListener): - pass + """ + Adds a listener that'll be called when the monitor is cancelled. + """ class ActionResultDict(TypedDict): diff --git a/sema4ai-python-ls-core/src/sema4ai_ls_core/unittest_tools/fixtures.py b/sema4ai-python-ls-core/src/sema4ai_ls_core/unittest_tools/fixtures.py index 846ff7ea2..85a3d3fcb 100644 --- a/sema4ai-python-ls-core/src/sema4ai_ls_core/unittest_tools/fixtures.py +++ b/sema4ai-python-ls-core/src/sema4ai_ls_core/unittest_tools/fixtures.py @@ -25,7 +25,7 @@ def wait_for_test_condition(condition, msg=None, timeout=TIMEOUT, sleep=1 / 20.0 @pytest.fixture -def ws_root_path(tmpdir): +def ws_root_path(tmpdir) -> str: return str(tmpdir.join("root")) diff --git a/sema4ai/poetry.lock b/sema4ai/poetry.lock index c01e3c11f..ebef99636 100644 --- a/sema4ai/poetry.lock +++ b/sema4ai/poetry.lock @@ -1,4 +1,4 @@ -# 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 = "annotated-types" @@ -114,43 +114,38 @@ files = [ [[package]] name = "cryptography" -version = "42.0.8" +version = "43.0.1" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, - {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, - {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, - {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, - {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, - {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, - {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, - {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, - {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, - {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, - {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, - {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, - {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, - {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, + {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"}, + {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"}, + {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"}, + {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"}, + {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"}, + {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"}, + {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"}, ] [package.dependencies] @@ -163,7 +158,7 @@ nox = ["nox"] pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -710,18 +705,18 @@ files = [ [[package]] name = "pydantic" -version = "2.9.1" +version = "2.9.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, - {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, + {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, + {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.3" +pydantic-core = "2.23.4" typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} [package.extras] @@ -730,100 +725,100 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.3" +version = "2.23.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, - {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, - {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, - {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, - {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, - {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, - {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, - {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, - {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d063c6b9fed7d992bcbebfc9133f4c24b7a7f215d6b102f3e082b1117cddb72c"}, - {file = "pydantic_core-2.23.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6cb968da9a0746a0cf521b2b5ef25fc5a0bee9b9a1a8214e0a1cfaea5be7e8a4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edbefe079a520c5984e30e1f1f29325054b59534729c25b874a16a5048028d16"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbaaf2ef20d282659093913da9d402108203f7cb5955020bd8d1ae5a2325d1c4"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb539d7e5dc4aac345846f290cf504d2fd3c1be26ac4e8b5e4c2b688069ff4cf"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e6f33503c5495059148cc486867e1d24ca35df5fc064686e631e314d959ad5b"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04b07490bc2f6f2717b10c3969e1b830f5720b632f8ae2f3b8b1542394c47a8e"}, - {file = "pydantic_core-2.23.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03795b9e8a5d7fda05f3873efc3f59105e2dcff14231680296b87b80bb327295"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c483dab0f14b8d3f0df0c6c18d70b21b086f74c87ab03c59250dbf6d3c89baba"}, - {file = "pydantic_core-2.23.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b2682038e255e94baf2c473dca914a7460069171ff5cdd4080be18ab8a7fd6e"}, - {file = "pydantic_core-2.23.3-cp38-none-win32.whl", hash = "sha256:f4a57db8966b3a1d1a350012839c6a0099f0898c56512dfade8a1fe5fb278710"}, - {file = "pydantic_core-2.23.3-cp38-none-win_amd64.whl", hash = "sha256:13dd45ba2561603681a2676ca56006d6dee94493f03d5cadc055d2055615c3ea"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, - {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, - {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, - {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, + {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, + {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, + {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, + {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, + {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, + {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, + {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, + {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, + {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, + {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, + {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, + {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, + {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, + {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, + {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, + {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, + {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, + {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, + {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, + {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, + {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, + {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, + {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, + {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, + {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, + {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, + {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, + {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, + {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, + {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, + {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, + {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, + {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, ] [package.dependencies] @@ -1034,28 +1029,28 @@ files = [ [[package]] name = "robocorp-actions" -version = "0.2.1" +version = "0.2.2" description = "Robocorp Actions" optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "robocorp_actions-0.2.1-py3-none-any.whl", hash = "sha256:601271c541f5e6d88a6a597248959de6eeb4d40eb338c68002c31ec05bd439f3"}, - {file = "robocorp_actions-0.2.1.tar.gz", hash = "sha256:c8cde74a3cf02590c43881baf7367bcd8a5a766af1fc8238fc286d33b0b6a3df"}, + {file = "robocorp_actions-0.2.2-py3-none-any.whl", hash = "sha256:70f0311723aba03b6b2d8ab2d309a68fcdfc5c3a636676bf46451a3da535c2f0"}, + {file = "robocorp_actions-0.2.2.tar.gz", hash = "sha256:574ec61ca91858ef173c2ef8ed847be8177adca1b9f2459dcc4e09d9525ed8eb"}, ] [package.dependencies] -cryptography = ">=42.0.5,<43.0.0" +cryptography = ">=43.0.1,<44.0.0" robocorp-tasks = "3.1.1" [[package]] name = "robocorp-log" -version = "2.9.2" +version = "2.9.3" description = "Automatic trace logging for Python" optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "robocorp_log-2.9.2-py3-none-any.whl", hash = "sha256:e4efdf9a7fc8f949be3fafc24016943a7449e113fc30ca40807ab9b0e54dba68"}, - {file = "robocorp_log-2.9.2.tar.gz", hash = "sha256:8917057b2eb5fc6713ff4aa255b43cce809bdc6a59ab637630b8444fe4bba0a6"}, + {file = "robocorp_log-2.9.3-py3-none-any.whl", hash = "sha256:ef365b5c67c392835cd1af10f898e4ed37b7058e075dc7957bf0cd44653d7100"}, + {file = "robocorp_log-2.9.3.tar.gz", hash = "sha256:ecde32a50489af16a9f4941452d729298d809fcf212772559c93965854cdef17"}, ] [package.dependencies] @@ -1184,46 +1179,46 @@ files = [ [[package]] name = "ruff" -version = "0.6.5" +version = "0.6.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.5-py3-none-linux_armv6l.whl", hash = "sha256:7e4e308f16e07c95fc7753fc1aaac690a323b2bb9f4ec5e844a97bb7fbebd748"}, - {file = "ruff-0.6.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:932cd69eefe4daf8c7d92bd6689f7e8182571cb934ea720af218929da7bd7d69"}, - {file = "ruff-0.6.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3a8d42d11fff8d3143ff4da41742a98f8f233bf8890e9fe23077826818f8d680"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a50af6e828ee692fb10ff2dfe53f05caecf077f4210fae9677e06a808275754f"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:794ada3400a0d0b89e3015f1a7e01f4c97320ac665b7bc3ade24b50b54cb2972"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:381413ec47f71ce1d1c614f7779d88886f406f1fd53d289c77e4e533dc6ea200"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:52e75a82bbc9b42e63c08d22ad0ac525117e72aee9729a069d7c4f235fc4d276"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09c72a833fd3551135ceddcba5ebdb68ff89225d30758027280968c9acdc7810"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:800c50371bdcb99b3c1551d5691e14d16d6f07063a518770254227f7f6e8c178"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e25ddd9cd63ba1f3bd51c1f09903904a6adf8429df34f17d728a8fa11174253"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7291e64d7129f24d1b0c947ec3ec4c0076e958d1475c61202497c6aced35dd19"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9ad7dfbd138d09d9a7e6931e6a7e797651ce29becd688be8a0d4d5f8177b4b0c"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:005256d977021790cc52aa23d78f06bb5090dc0bfbd42de46d49c201533982ae"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:482c1e6bfeb615eafc5899127b805d28e387bd87db38b2c0c41d271f5e58d8cc"}, - {file = "ruff-0.6.5-py3-none-win32.whl", hash = "sha256:cf4d3fa53644137f6a4a27a2b397381d16454a1566ae5335855c187fbf67e4f5"}, - {file = "ruff-0.6.5-py3-none-win_amd64.whl", hash = "sha256:3e42a57b58e3612051a636bc1ac4e6b838679530235520e8f095f7c44f706ff9"}, - {file = "ruff-0.6.5-py3-none-win_arm64.whl", hash = "sha256:51935067740773afdf97493ba9b8231279e9beef0f2a8079188c4776c25688e0"}, - {file = "ruff-0.6.5.tar.gz", hash = "sha256:4d32d87fab433c0cf285c3683dd4dae63be05fd7a1d65b3f5bf7cdd05a6b96fb"}, + {file = "ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2"}, + {file = "ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c"}, + {file = "ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44"}, + {file = "ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a"}, + {file = "ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263"}, + {file = "ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc"}, + {file = "ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18"}, ] [[package]] name = "sema4ai-actions" -version = "0.10.0" +version = "1.0.1" description = "Sema4AI Actions" optional = false python-versions = "<4.0,>=3.10" files = [ - {file = "sema4ai_actions-0.10.0-py3-none-any.whl", hash = "sha256:3f16b3304b25831f50ffee78cedcdc5e0d74086f624f932ccc8c3514b2726ab1"}, - {file = "sema4ai_actions-0.10.0.tar.gz", hash = "sha256:d08cb22887f099f8a7e47cf1072c731ccac72afd389181b44c5e13091b188c40"}, + {file = "sema4ai_actions-1.0.1-py3-none-any.whl", hash = "sha256:5f7eef626b1120796d6062cecefeb0f673b66dc42a87b332357960b75b751c78"}, + {file = "sema4ai_actions-1.0.1.tar.gz", hash = "sha256:c3c51116400a0ba757f8b5af8c0a5146de343b027d44c03eaab57f9bf5d2f776"}, ] [package.dependencies] -cryptography = ">=42.0.5,<43.0.0" +cryptography = ">=43.0.1,<44.0.0" docstring_parser_fork = ">=0.0.5,<0.0.6" -psutil = ">=5.0,<6.0" +psutil = ">=5.0,<7.0" pydantic = ">=2.0,<3.0" robocorp-log = ">=2.4,<3" robocorp-truststore = ">=0.8.0" @@ -1454,4 +1449,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.11" -content-hash = "cb40e884baeb9bc20c2eeacd785af6c4106aa6b3f7a9386a919ceef4bc943b4f" +content-hash = "300c19070670edef4e67dc75a606918a2b2385f8ee69f98befc9b93c6463bc7c" diff --git a/sema4ai/pyproject.toml b/sema4ai/pyproject.toml index ab74de257..7911612e7 100644 --- a/sema4ai/pyproject.toml +++ b/sema4ai/pyproject.toml @@ -51,7 +51,7 @@ sema4ai-python-ls-core = { path = "../sema4ai-python-ls-core/", develop = true } fire = "*" robocorp-actions = "^0.2.1" # Just needed for testing. -sema4ai-actions = "^0.10.0" # Just needed for testing. +sema4ai-actions = "^1.0.1" # Just needed for testing. numpy = "<2" ruff = "^0.6.5" diff --git a/sema4ai/src/sema4ai_code/compute_launch.py b/sema4ai/src/sema4ai_code/compute_launch.py index 47c2ec7fd..129b5a0a5 100644 --- a/sema4ai/src/sema4ai_code/compute_launch.py +++ b/sema4ai/src/sema4ai_code/compute_launch.py @@ -1,5 +1,5 @@ import os -from typing import Any, Dict, List, Optional +from typing import Any from sema4ai_ls_core import uris from sema4ai_ls_core.core_log import get_logger diff --git a/sema4ai/src/sema4ai_code/robo/actions_form_data.py b/sema4ai/src/sema4ai_code/robo/actions_form_data.py new file mode 100644 index 000000000..8ce1e9864 --- /dev/null +++ b/sema4ai/src/sema4ai_code/robo/actions_form_data.py @@ -0,0 +1,275 @@ +from enum import Enum +from typing import Any, Dict, List, Union + + +class InputPropertyType(Enum): + STRING = "string" + INTEGER = "integer" + NUMBER = "number" + BOOLEAN = "boolean" + ARRAY = "array" + OBJECT = "object" + ENUM = "enum" + + +class InputProperty: + def __init__( + self, + title: str, + description: str, + type: InputPropertyType, + enum: List[str] | None = None, + ): + self.title = title + self.description = description + self.type = type + self.enum = enum + + +PropertyFormDataType = Union[str, int, float, bool, List["PropertyFormData"]] + + +class PropertyFormData: + def __init__( + self, + name: str, + prop: InputProperty, + required: bool, + value: PropertyFormDataType, + title: str | None = None, + options: List[str] | None = None, + ): + self.name = name + self.prop = prop + self.required = required + self.value = value + self.title = title + self.options = options + + def value_as_str(self) -> str: + if self.prop.enum: + if self.prop.description: + return f"{self.prop.enum} ({self.prop.description})" + return f"{self.prop.enum}" + + if self.prop.description: + return f"{self.prop.type.value}: {self.prop.description}" + + return f"{self.prop.type.value}" + + def __str__(self): + return f"{self.name} [{self.value_as_str()}]" + + __repr__ = __str__ + + +def get_default_value( + property_type: str, enum: List[str] | None = None +) -> PropertyFormDataType: + if property_type == "number": + return 0.0 + elif property_type == "boolean": + return False + elif property_type == "integer": + return 0 + elif property_type == "array": + return "[]" + elif property_type == "object": + return "{}" + else: + if enum: + return enum[0] + return "" + + +def set_array_item_title(item: PropertyFormData) -> None: + new_title = item.prop.title + if new_title.endswith("*"): + new_title = new_title[:-1] + if not new_title.endswith(" (item)"): + new_title += " (item)" + item.prop.title = new_title + + +def properties_to_form_data( + schema: Dict[str, Any], parents: List[str] | None = None +) -> List[PropertyFormData]: + if parents is None: + parents = [] + + if "properties" not in schema: + return [] + + entries = [] + for name, prop in schema["properties"].items(): + property_name = parents + [name] + + if "$ref" in prop: + continue + + if "properties" in prop: + entries.extend(properties_to_form_data(prop, property_name)) + continue + + if "allOf" in prop: + first_child = prop["allOf"][0] + if "enum" in first_child: + entry = PropertyFormData( + name=".".join(property_name), + prop=InputProperty( + title=prop.get("title", property_name[-1]), + description=prop.get("description", ""), + type=InputPropertyType.ENUM, + ), + required=name in schema.get("required", []), + value=prop.get( + "default", first_child["enum"][0] if first_child["enum"] else "" + ), + options=first_child["enum"], + ) + entries.append(entry) + else: + for item in prop["allOf"]: + entries.extend(properties_to_form_data(item, property_name)) + continue + + if isinstance(prop.get("type"), list) or prop.get("type") == "array": + row_entry = PropertyFormData( + name=".".join(property_name), + title=prop.get("title", property_name[-1]), + prop=InputProperty( + title=prop.get("title", property_name[-1]), + description=prop.get("description", ""), + type=InputPropertyType.ARRAY, + ), + required=name in schema.get("required", []), + value=get_default_value(prop.get("type", "string")), + ) + + if "items" in prop: + if "properties" in prop["items"]: + row_properties = properties_to_form_data( + prop["items"], property_name + ["0"] + ) + row_entry.value = row_properties + entries.append(row_entry) + entries.extend(row_properties) + else: + enum = prop["items"].get("enum") + row_property = PropertyFormData( + name=f"{'.'.join(property_name)}.0", + prop=InputProperty( + title=prop.get("title", property_name[-1]), + description=prop.get("description", ""), + type=InputPropertyType(prop["items"].get("type", "string")), + enum=enum, + ), + required=name in schema.get("required", []), + value=get_default_value( + prop["items"].get("type", "string"), enum + ), + ) + row_entry.value = [row_property] + set_array_item_title(row_property) + entries.extend([row_entry, row_property]) + else: + entries.append(row_entry) + continue + + enum = prop.get("enum") + entry = PropertyFormData( + name=".".join(property_name), + prop=InputProperty( + title=prop.get("title", property_name[-1]), + description=prop.get("description", ""), + type=InputPropertyType(prop.get("type", "string")), + enum=enum, + ), + required=name in schema.get("required", []), + value=get_default_value(prop.get("type", "string"), enum), + ) + + if schema.get("title") and entries: + entry.title = schema["title"] + + entries.append(entry) + + return entries + + +Payload = Dict[str, Any] + + +def form_data_to_payload(data: List[PropertyFormData]) -> Payload: + result: Payload = {} + + for item in data: + levels = item.name.split(".") + property_name = levels[-1] + + current_level = result + for level in levels[:-1]: + if level not in current_level: + current_level[level] = {} + current_level = current_level[level] + + if item.prop.type == InputPropertyType.OBJECT: + current_level[property_name] = eval(str(item.value)) + elif item.prop.type == InputPropertyType.ARRAY: + if property_name not in current_level: + current_level[property_name] = [] + elif isinstance(current_level, list): + current_level.append(item.value) + else: + current_level[property_name] = item.value + + return result + + +def payload_to_form_data( + payload: Payload, form_data: List[PropertyFormData], path: str = "" +) -> List[PropertyFormData]: + result: List[PropertyFormData] = [] + + for key, val in payload.items(): + full_path = f"{path}.{key}" if path else key + if isinstance(val, dict): + result.extend(payload_to_form_data(val, form_data, full_path)) + elif isinstance(val, list): + found_data = next( + (elem for elem in form_data if elem.name == full_path), None + ) + if found_data: + result.append(found_data) + for index, elem_value in enumerate(val): + found_elem = next( + (elem for elem in form_data if elem.name == f"{full_path}.{index}"), + None, + ) + if found_elem: + result.append( + PropertyFormData(**{**found_elem.__dict__, "value": elem_value}) + ) + else: + prev = next( + (elem for elem in form_data if elem.name == f"{full_path}.0"), + None, + ) + if prev: + result.append( + PropertyFormData( + **{ + **prev.__dict__, + "value": elem_value, + "name": f"{full_path}.{index}", + } + ) + ) + else: + found_data = next( + (elem for elem in form_data if elem.name == full_path), None + ) + if found_data: + result.append(PropertyFormData(**{**found_data.__dict__, "value": val})) + + return result diff --git a/sema4ai/src/sema4ai_code/robo/collect_actions.py b/sema4ai/src/sema4ai_code/robo/collect_actions.py new file mode 100644 index 000000000..e7fff043e --- /dev/null +++ b/sema4ai/src/sema4ai_code/robo/collect_actions.py @@ -0,0 +1,170 @@ +import json +from typing import TypedDict + +from sema4ai_ls_core.core_log import get_logger +from sema4ai_ls_core.ep_resolve_interpreter import ( + EPResolveInterpreter, + IInterpreterInfo, +) +from sema4ai_ls_core.pluginmanager import PluginManager +from sema4ai_ls_core.protocols import ActionResult, IMonitor + +log = get_logger(__name__) + + +class ExtractedActionInfo(TypedDict): + # Default values that can be used to bootstrap the action input. + default_values: dict + # Informal representation of the schema. + informal_schema_representation: list[str] + # JSON Schema of the action input. + json_schema: dict + # Schema related to the managed params (secrets/oauth2). + managed_params_json_schema: dict + action_name: str + action_relative_path: str + + +def extract_info( + action_info_found: list, action_package_yaml_directory: str +) -> dict[str, ExtractedActionInfo]: + """ + Args: + action_info_found: The result from calling 'collect_actions_full_and_slow'. + """ + from pathlib import Path + + from sema4ai_code.robo.actions_form_data import ( + form_data_to_payload, + properties_to_form_data, + ) + + action_name_to_extracted_info: dict[str, ExtractedActionInfo] = {} + for item in action_info_found: + action_name = item.get("name", "") + target_file = item.get("file", "") + relative_path = "" + # Now, make relative to the action_package_yaml_directory + if target_file: + relative_path = ( + Path(target_file).relative_to(action_package_yaml_directory).as_posix() + ) + + input_schema = item.get("input_schema", {}) + managed_params_schema = item.get("managed_params_schema", {}) + as_form_data = properties_to_form_data(input_schema) + payload = form_data_to_payload(as_form_data) + + if managed_params_schema: + for ( + managed_param_name, + managed_param_schema, + ) in managed_params_schema.items(): + if isinstance(managed_param_schema, dict): + oauth2_requests: dict = {} + if managed_param_schema.get("type") == "Secret": + payload[managed_param_name] = "" + elif managed_param_schema.get("type") == "OAuth2Secret": + oauth2_requests[managed_param_name] = { + "type": managed_param_schema.get("type"), + "provider": managed_param_schema.get("provider"), + "scopes": managed_param_schema.get("scopes"), + "access_token": "", + } + + if oauth2_requests: + payload["vscode:request:oauth2"] = oauth2_requests + + informal_schema_representation = [] + for d in as_form_data: + informal_schema_representation.append(f"{d.name}: {d.value_as_str()}") + + full: ExtractedActionInfo = { + "default_values": payload, + "informal_schema_representation": informal_schema_representation, + "json_schema": input_schema, + "managed_params_json_schema": managed_params_schema, + "action_name": action_name, + "action_relative_path": relative_path, + } + + action_name_to_extracted_info[item.get("name")] = full + return action_name_to_extracted_info + + +def collect_actions_full_and_slow( + pm: PluginManager, uri: str, monitor: IMonitor +) -> ActionResult[list[dict]]: + """ + Note: the way this works is that we'll launch a separate script using the user + environment to collect the actions information. + + The major reason this is done (vs just loading the actions in the current + environment is that if we used the current environment, if the user uses a new + version of python we could potentially have a syntax error or load libraries not + available to the VSCode extension (because for listing the actions we need to + actually load the user code to resolve things such as complex models). + """ + from pathlib import Path + + from sema4ai_ls_core import uris + from sema4ai_ls_core.basic import launch_and_return_future + + if not uri: + return ActionResult.make_failure("No uri given") + + path = Path(uris.to_fs_path(uri)) + if not path.exists(): + return ActionResult.make_failure( + f"Uri ({uri}) mapped to path: {path} does not exist" + ) + + file_name = path.name + args = [ + "-m", + "sema4ai.actions", + "list", + "--skip-lint", + ] + + if not path.is_dir(): + # If a file is given, we'll use the glob to list the actions just in that file. + args.append("--glob") + args.append(file_name) + + error_msg = "Unable to collect @actions" + try: + for ep in pm.get_implementations(EPResolveInterpreter): + interpreter_info: IInterpreterInfo = ep.get_interpreter_info_for_doc_uri( + uri + ) + if interpreter_info is not None: + environ = interpreter_info.get_environ() + python_exe = interpreter_info.get_python_exe() + future = launch_and_return_future( + [python_exe] + args, + environ=environ, + cwd=str(path.parent), + timeout=30, + monitor=monitor, + ) + result = future.result(30) + if result.returncode == 0: + if result.stdout: + try: + return ActionResult.make_success(json.loads(result.stdout)) + except Exception: + msg = f"Unable to parse as json: {result.stdout}" + log.exception(msg) + return ActionResult.make_failure(msg) + if result.stderr: + error_msg = ( + f"Found errors while collecting actions errors: {result.stderr}" + ) + log.info(error_msg) + break + except BaseException as e: + log.exception("Error collection @action") + return ActionResult.make_failure(f"Unable to collect @actions: {e}") + + return ActionResult.make_failure(error_msg) diff --git a/sema4ai/src/sema4ai_code/robo/collect_actions_ast.py b/sema4ai/src/sema4ai_code/robo/collect_actions_ast.py index 0db9ba9d9..d0fd7e6b8 100644 --- a/sema4ai/src/sema4ai_code/robo/collect_actions_ast.py +++ b/sema4ai/src/sema4ai_code/robo/collect_actions_ast.py @@ -1,7 +1,7 @@ import ast as ast_module -from pathlib import Path -from typing import Any, List, Optional, Tuple, TypedDict from collections.abc import Iterator +from pathlib import Path +from typing import Any, TypedDict from sema4ai_ls_core import uris from sema4ai_ls_core.core_log import get_logger diff --git a/sema4ai/src/sema4ai_code/robo/lint_action.py b/sema4ai/src/sema4ai_code/robo/lint_action.py index 958756d65..622e36bf2 100644 --- a/sema4ai/src/sema4ai_code/robo/lint_action.py +++ b/sema4ai/src/sema4ai_code/robo/lint_action.py @@ -1,12 +1,12 @@ import json +from sema4ai_ls_core.core_log import get_logger from sema4ai_ls_core.ep_resolve_interpreter import ( EPResolveInterpreter, IInterpreterInfo, ) from sema4ai_ls_core.pluginmanager import PluginManager from sema4ai_ls_core.protocols import IDocument -from sema4ai_ls_core.core_log import get_logger from sema4ai_code.robo import lint_in_target_env @@ -52,6 +52,7 @@ def collect_lint_errors(pm: PluginManager, doc: IDocument) -> list: log.info( f"Found stderr while collecting lint errors: {result.stderr}" ) + break except BaseException: log.exception("Error collection @action") return [] diff --git a/sema4ai/src/sema4ai_code/robocorp_language_server.py b/sema4ai/src/sema4ai_code/robocorp_language_server.py index ddc7e5a20..32f4b7cb2 100644 --- a/sema4ai/src/sema4ai_code/robocorp_language_server.py +++ b/sema4ai/src/sema4ai_code/robocorp_language_server.py @@ -6,7 +6,7 @@ from collections.abc import Iterator, Sequence from functools import partial from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple +from typing import Any from sema4ai_ls_core import uris, watchdog_wrapper from sema4ai_ls_core.basic import overrides @@ -674,6 +674,64 @@ def _run_in_rcc_internal(self, params=RunInRccParamsDict) -> ActionResultDict: return dict(success=False, message=str(e), result=None) return ret.as_dict() + def m_list_actions_full_and_slow( + self, action_package_uri: str = "", action_package_yaml_directory: str = "" + ): + if not action_package_uri: + msg = f"Unable to collect actions because the target action_package_uri was not given (or was empty)." + return dict(success=False, message=msg, result=None) + if not action_package_yaml_directory: + msg = f"Unable to collect actions because the target action_package_yaml_directory was not given (or was empty)." + return dict(success=False, message=msg, result=None) + return require_monitor( + partial( + self._list_actions_full_and_slow, + action_package_uri=action_package_uri, + action_package_yaml_directory=action_package_yaml_directory, + ) + ) + + def _list_actions_full_and_slow( + self, + action_package_uri: str, + action_package_yaml_directory: str, + monitor: IMonitor, + ) -> ActionResultDict: + from sema4ai_code.robo.collect_actions import ( + collect_actions_full_and_slow, + extract_info, + ) + + p = Path(uris.to_fs_path(action_package_uri)) + if not p.exists(): + msg = f"Unable to collect actions from: {p} because it does not exist." + return dict(success=False, message=msg, result=None) + + try: + if not self.workspace: + return dict( + success=False, message="No workspace currently open", result=None + ) + + result = collect_actions_full_and_slow( + self._pm, action_package_uri, monitor + ) + if not result.success: + return result.as_dict() + + lst = result.result + assert ( + lst is not None + ), "When collecting actions, the result should not be None in the success case" + actions_info = extract_info(lst, action_package_yaml_directory) + except Exception as e: + log.exception("Error collecting actions.") + return dict( + success=False, message=f"Error collecting actions: {e}", result=None + ) + + return dict(success=True, message=None, result=actions_info) + @command_dispatcher(commands.SEMA4AI_LIST_ACTIONS_INTERNAL) def _local_list_actions_internal(self, params: ListActionsParams | None): # i.e.: should not block. diff --git a/sema4ai/tests/sema4ai_code_tests/_resources/action_package/package.yaml b/sema4ai/tests/sema4ai_code_tests/_resources/action_package/package.yaml index 02fc5ff31..bc5a7085e 100644 --- a/sema4ai/tests/sema4ai_code_tests/_resources/action_package/package.yaml +++ b/sema4ai/tests/sema4ai_code_tests/_resources/action_package/package.yaml @@ -1,8 +1,8 @@ # Required: Defines the name of the action package. -name: My awesome cookie maker +name: My awesome cookie maker # Required: A description of what's in the action package. -description: This does cookies +description: This does cookies # Required: The current version of this action package. version: 0.2.3 @@ -11,26 +11,25 @@ version: 0.2.3 documentation: https://github.com/robocorp/actions-cookbook/blob/master/database-postgres/README.md # Required: -# Defines the Python dependencies which should be used to launch the +# Defines the Python dependencies which should be used to launch the # actions. # The action server will automatically create a new python environment # based on this specification. # Note that at this point the only operator supported is `=`. dependencies: - conda-forge: - # This section is required: at least the python version must be specified. - - python=3.10.12 - - pip=23.2.1 - - robocorp-truststore=0.8.0 + conda-forge: + # This section is required: at least the python version must be specified. + - python=3.10.12 + - uv=0.2.6 - pypi: - # This section is required: at least `robocorp-actions` must - # be specified. - # Note: robocorp-actions is special case because the action server - # has coupling with the library. This means that if the version of - # robocorp-actions is not pinned to a value the action server will - # select a version based on a version that's known to work with the - # current version of the action server. - # If the version is pinned, then the action server will validate - # if the given version can be used with it. - - robocorp-actions + pypi: + # This section is required: at least `robocorp-actions` must + # be specified. + # Note: robocorp-actions is special case because the action server + # has coupling with the library. This means that if the version of + # robocorp-actions is not pinned to a value the action server will + # select a version based on a version that's known to work with the + # current version of the action server. + # If the version is pinned, then the action server will validate + # if the given version can be used with it. + - robocorp-actions diff --git a/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions.py b/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions.py index 57978e4b0..dfa9e7b88 100644 --- a/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions.py +++ b/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions.py @@ -1,7 +1,9 @@ import os +import pytest from sema4ai_code_tests.protocols import IRobocorpLanguageServerClient from sema4ai_ls_core import uris +from sema4ai_ls_core.ep_resolve_interpreter import IInterpreterInfo def test_list_actions( @@ -22,3 +24,126 @@ def test_list_actions( for entry in result: entry["uri"] = os.path.basename(entry["uri"]) data_regression.check(result) + + +class ResolveInterpreterCurrentEnv: + def get_interpreter_info_for_doc_uri(self, doc_uri) -> IInterpreterInfo | None: + """ + Provides a customized interpreter for a given document uri. + """ + import sys + + from sema4ai_ls_core.ep_resolve_interpreter import DefaultInterpreterInfo + + return DefaultInterpreterInfo("interpreter_id", sys.executable, {}, []) + + +def multiple_types() -> str: + return """ +from sema4ai.actions import action, Secret +from typing import List +import pydantic +from enum import Enum + +class SomeEnum(str, Enum): + A = "a" + B = "b" + +class AnotherModel(pydantic.BaseModel): + name: str + age: int + +class UseModel(pydantic.BaseModel): + name: str + lst: List[int] + other: dict[str, int] + another: AnotherModel + some_enum: SomeEnum + +@action +def my_action(a:int,b:float,c:str,d:bool, model: UseModel, secret: Secret) -> str: + return "result" +""" + + +def just_enum() -> str: + return """ +from sema4ai.actions import action, Secret +from typing import List +import pydantic +from enum import Enum + +class SomeEnum(str, Enum): + A = "a" + B = "b" + + +class UseModel(pydantic.BaseModel): + + some_enum: SomeEnum + +@action +def my_action(model: UseModel) -> str: + return "result" +""" + + +def just_oauth2() -> str: + return """ +from sema4ai.actions import action, Secret, OAuth2Secret +from typing import List, Literal +import pydantic + + +@action +def my_action(google_secret: OAuth2Secret[ + Literal["google"], + list[ + Literal[ + "https://www.googleapis.com/auth/spreadsheets.readonly", + "https://www.googleapis.com/auth/drive.readonly", + ] + ], + ]) -> str: + return "result" +""" + + +@pytest.mark.parametrize( + "scenario", + [ + just_oauth2, + multiple_types, + just_enum, + ], +) +def test_list_actions_full(tmpdir, scenario, data_regression) -> None: + from pathlib import Path + + from sema4ai_ls_core import pluginmanager + from sema4ai_ls_core.ep_resolve_interpreter import EPResolveInterpreter + from sema4ai_ls_core.jsonrpc.monitor import Monitor + + from sema4ai_code.robo.collect_actions import ( + collect_actions_full_and_slow, + extract_info, + ) + + root = Path(tmpdir) / "check" + root.mkdir(parents=True, exist_ok=True) + + pm = pluginmanager.PluginManager() + pm.register(EPResolveInterpreter, ResolveInterpreterCurrentEnv) + + action_path = root / "my_action.py" + action_path.write_text(scenario(), "utf-8") + + uri = uris.from_fs_path(str(root)) + + monitor = Monitor() + result = collect_actions_full_and_slow(pm, uri, monitor) + assert result.success, result.message + lst = result.result + assert lst + action_name_to_schema = extract_info(lst, str(root)) + data_regression.check(action_name_to_schema) diff --git a/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_just_enum_.yml b/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_just_enum_.yml new file mode 100644 index 000000000..aa82aba03 --- /dev/null +++ b/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_just_enum_.yml @@ -0,0 +1,26 @@ +my_action: + action_name: my_action + action_relative_path: my_action.py + default_values: + model: + some_enum: a + informal_schema_representation: + - 'model.some_enum: [''a'', ''b'']' + json_schema: + properties: + model: + properties: + some_enum: + enum: + - a + - b + title: SomeEnum + type: string + required: + - some_enum + title: Model + type: object + required: + - model + type: object + managed_params_json_schema: {} diff --git a/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_just_oauth2_.yml b/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_just_oauth2_.yml new file mode 100644 index 000000000..de4359ffa --- /dev/null +++ b/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_just_oauth2_.yml @@ -0,0 +1,23 @@ +my_action: + action_name: my_action + action_relative_path: my_action.py + default_values: + vscode:request:oauth2: + google_secret: + access_token: + provider: google + scopes: + - https://www.googleapis.com/auth/spreadsheets.readonly + - https://www.googleapis.com/auth/drive.readonly + type: OAuth2Secret + informal_schema_representation: [] + json_schema: + properties: {} + type: object + managed_params_json_schema: + google_secret: + provider: google + scopes: + - https://www.googleapis.com/auth/spreadsheets.readonly + - https://www.googleapis.com/auth/drive.readonly + type: OAuth2Secret diff --git a/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_multiple_types_.yml b/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_multiple_types_.yml new file mode 100644 index 000000000..274c2036a --- /dev/null +++ b/sema4ai/tests/sema4ai_code_tests/robo/test_list_actions/test_list_actions_full_multiple_types_.yml @@ -0,0 +1,100 @@ +my_action: + action_name: my_action + action_relative_path: my_action.py + default_values: + a: 0 + b: 0.0 + c: '' + d: false + model: + another: + age: 0 + name: '' + lst: + - 0 + name: '' + other: {} + some_enum: a + secret: + informal_schema_representation: + - 'a: integer' + - 'b: number' + - 'c: string' + - 'd: boolean' + - 'model.name: string' + - 'model.lst: array' + - 'model.lst.0: integer' + - 'model.other: object' + - 'model.another.name: string' + - 'model.another.age: integer' + - 'model.some_enum: [''a'', ''b'']' + json_schema: + properties: + a: + description: '' + title: A + type: integer + b: + description: '' + title: B + type: number + c: + description: '' + title: C + type: string + d: + description: '' + title: D + type: boolean + model: + properties: + another: + properties: + age: + title: Age + type: integer + name: + title: Name + type: string + required: + - name + - age + title: AnotherModel + type: object + lst: + items: + type: integer + title: Lst + type: array + name: + title: Name + type: string + other: + additionalProperties: + type: integer + title: Other + type: object + some_enum: + enum: + - a + - b + title: SomeEnum + type: string + required: + - name + - lst + - other + - another + - some_enum + title: Model + type: object + required: + - a + - b + - c + - d + - model + type: object + managed_params_json_schema: + secret: + type: Secret diff --git a/sema4ai/vscode-client/src/debugger.ts b/sema4ai/vscode-client/src/debugger.ts index a66c6539e..f9a3241c5 100644 --- a/sema4ai/vscode-client/src/debugger.ts +++ b/sema4ai/vscode-client/src/debugger.ts @@ -20,7 +20,10 @@ import { SEMA4AI_GET_CONNECTED_VAULT_WORKSPACE_INTERNAL, } from "./robocorpCommands"; import { globalCachedPythonInfo } from "./extension"; -import { disablePythonTerminalActivateEnvironment } from "./pythonExtIntegration"; +import { + disablePythonTerminalActivateEnvironment, + isPyDevDebuggerPythonExtensionInstalled, +} from "./pythonExtIntegration"; import { InterpreterInfo } from "./protocols"; import { applyOutViewIntegrationEnvVars } from "./output/outViewRunIntegration"; @@ -205,6 +208,24 @@ export class RobocorpCodeDebugConfigurationProvider implements DebugConfiguratio // the launch is already resolved. await extension.activate(); } + } else if (isPyDevDebuggerPythonExtensionInstalled()) { + let extension = extensions.getExtension("fabioz.vscode-pydev-python-debugger"); + if (extension) { + if (!extension.isActive) { + // i.e.: Auto-activate python extension for the launch as the extension + // is only activated for debug on the resolution, whereas in this case + // the launch is already resolved. + await extension.activate(); + } + } + result.type = "pydevd"; + result.pythonExecutable = result.python; + } else { + const msg = + "It's not possible to make a python launch without the ms-python.python or fabioz.vscode-pydev-python-debugger extension installed."; + OUTPUT_CHANNEL.appendLine(msg); + window.showErrorMessage(msg); + return; } } diff --git a/sema4ai/vscode-client/src/pythonExtIntegration.ts b/sema4ai/vscode-client/src/pythonExtIntegration.ts index ee06b1cd0..29b88a0eb 100644 --- a/sema4ai/vscode-client/src/pythonExtIntegration.ts +++ b/sema4ai/vscode-client/src/pythonExtIntegration.ts @@ -164,6 +164,22 @@ export async function installWorkspaceWatcher(context: ExtensionContext) { } } +export async function isMicrosoftPythonExtensionInstalled() { + const extension = extensions.getExtension("ms-python.python"); + if (extension) { + return true; + } + return false; +} + +export async function isPyDevDebuggerPythonExtensionInstalled() { + const extension = extensions.getExtension("fabioz.vscode-pydev-python-debugger"); + if (extension) { + return true; + } + return false; +} + export async function disablePythonTerminalActivateEnvironment() { try { const extension = extensions.getExtension("ms-python.python"); diff --git a/sema4ai/vscode-client/src/robo/actionInputs.ts b/sema4ai/vscode-client/src/robo/actionInputs.ts new file mode 100644 index 000000000..6dbc4f252 --- /dev/null +++ b/sema4ai/vscode-client/src/robo/actionInputs.ts @@ -0,0 +1,92 @@ +import * as vscode from "vscode"; + +import { langServer } from "../extension"; +import { window } from "vscode"; +import { logError } from "../channel"; + +interface ExtractedActionInfo { + default_values: any; + informal_schema_representation: string[]; + json_schema: any; + managed_params_json_schema: any; + action_name: string; + action_relative_path: string; +} + +// Create interface for the input we write. +export interface ActionInputsV2 { + inputs: { inputName: string; inputValue: any }[]; + metadata: { + actionName: string; + actionRelativePath: string; + schemaDescription: string[]; + managedParamsSchemaDescription: any; + inputFileVersion: "v2"; + }; +} + +/** + * Returns an error message if the input is not in v2 format or if the v2 input is not properly followed, otherwise returns undefined. + */ +export const errorMessageValidatingV2Input = (input: any): string | undefined => { + if (input.metadata.inputFileVersion !== "v2") { + return "Input file is not in v2 format."; + } + if (!input.inputs || !Array.isArray(input.inputs) || input.inputs.length === 0) { + return "Input file is in v2 format, but the inputs array is missing or empty."; + } + // Check inputName + if (!input.inputs.every((input) => typeof input.inputName === "string" && input.inputName.length > 0)) { + return "Input file is in v2 format, but one of the `inputName` fields is not a string, is missing or is empty."; + } + // Check the metadata + if (!input.metadata.actionName || typeof input.metadata.actionName !== "string") { + return "Input file is in v2 format, but the action name in metadata is missing or not a string."; + } + if (!input.metadata.actionRelativePath || typeof input.metadata.actionRelativePath !== "string") { + return "Input file is in v2 format, but the action relative path in metadata is missing or not a string."; + } + return undefined; +}; + +export const createActionInputs = async ( + actionFileUri: vscode.Uri, + actionName: string, + targetInput: string, + actionPackageYamlDirectory: string +): Promise => { + try { + const result: any = await langServer.sendRequest("listActionsFullAndSlow", { + action_package_uri: actionFileUri.toString(), + action_package_yaml_directory: actionPackageYamlDirectory, + }); + if (!result.success) { + window.showErrorMessage("Error listing actions: " + result.message); + return false; + } + const actions = result.result; + const action: ExtractedActionInfo = actions[actionName]; + if (!action) { + window.showErrorMessage("Action not found: " + actionName); + return false; + } + const inputUri = vscode.Uri.file(targetInput); + const inputData: ActionInputsV2 = { + inputs: [{ inputName: "input-1", inputValue: action.default_values }], + metadata: { + actionName: action.action_name, + actionRelativePath: action.action_relative_path, + schemaDescription: action.informal_schema_representation, + managedParamsSchemaDescription: action.managed_params_json_schema, + inputFileVersion: "v2", + }, + }; + await vscode.workspace.fs.writeFile(inputUri, Buffer.from(JSON.stringify(inputData, null, 4))); + await vscode.window.showTextDocument(inputUri); + return true; + } catch (error) { + window.showErrorMessage("Error creating action inputs: " + error); + logError("Error creating action inputs.", error, "ERROR_CREATING_ACTION_INPUTS"); + return false; + } +}; diff --git a/sema4ai/vscode-client/src/robo/actionPackage.ts b/sema4ai/vscode-client/src/robo/actionPackage.ts index d96693bc8..ac00d583a 100644 --- a/sema4ai/vscode-client/src/robo/actionPackage.ts +++ b/sema4ai/vscode-client/src/robo/actionPackage.ts @@ -39,7 +39,7 @@ import { ActionPackageTargetInfo, } from "../common"; import { slugify } from "../slugify"; -import { fileExists, makeDirs } from "../files"; +import { fileExists, makeDirs, readFromFile, writeToFile } from "../files"; import { QuickPickItemWithAction, askForWs, showSelectOneQuickPick } from "../ask"; import * as path from "path"; import { OUTPUT_CHANNEL, logError } from "../channel"; @@ -50,6 +50,7 @@ import { findActionPackagePath, } from "../actionServer"; import { loginToAuth2WhereRequired } from "./oauth2InInput"; +import { createActionInputs, errorMessageValidatingV2Input } from "./actionInputs"; export interface QuickPickItemAction extends QuickPickItem { actionPackageUri: vscode.Uri; @@ -60,15 +61,6 @@ export interface QuickPickItemAction extends QuickPickItem { keyInLRU: string; } -export async function createDefaultInputJson(inputUri: vscode.Uri) { - await vscode.workspace.fs.writeFile( - inputUri, - Buffer.from(`{ - "paramName": "paramValue" -}`) - ); -} - export async function askAndRunRobocorpActionFromActionPackage(noDebug: boolean) { let textEditor = window.activeTextEditor; let fileName: string | undefined = undefined; @@ -210,9 +202,11 @@ export async function runActionFromActionPackage( // The input must be asked when running actions in this case and it should be // saved in 'devdata/input_xxx.json' const nameSlugified = slugify(actionName); - const targetInput = await getTargetInputJson(actionName, actionPackageYamlDirectory); + const multiTargetInput = await getTargetInputJson(actionName, actionPackageYamlDirectory); - if (!(await fileExists(targetInput))) { + const contents = await readFromFile(multiTargetInput); + + if (!(await fileExists(multiTargetInput)) || contents.length === 0) { let items: QuickPickItemWithAction[] = new Array(); items.push({ @@ -228,7 +222,7 @@ export async function runActionFromActionPackage( let selectedItem: QuickPickItemWithAction | undefined = await showSelectOneQuickPick( items, - "Input for the action not defined. How to proceed?", + "File input for the action does not exist or is empty. How to proceed?", `Customize input for the ${actionName} action` ); if (!selectedItem) { @@ -238,15 +232,61 @@ export async function runActionFromActionPackage( if (selectedItem.action === "create") { // Create the file and ask the user to fill it and rerun the action // after he finished doing that. - const inputUri = vscode.Uri.file(targetInput); - await createDefaultInputJson(inputUri); - await vscode.window.showTextDocument(inputUri); + await createActionInputs(actionFileUri, actionName, multiTargetInput, actionPackageYamlDirectory); } // In any case, don't proceed if it wasn't previously created // (so that the user can customize it). return; } + // Now, in the new format, we need to check if the input is in the new format and then extract the input from there + let input; + try { + input = JSON.parse(contents); + } catch (error) { + window.showErrorMessage( + `Unable to run action package: error reading file: ${multiTargetInput} as json: ${error.message}` + ); + return; + } + let targetInput: string; + if (input.metadata.inputFileVersion === "v2") { + const errorMessage = errorMessageValidatingV2Input(input); + if (errorMessage) { + window.showErrorMessage("Unable to run action package (input file is not valid v2):\n" + errorMessage); + return; + } + // Ok, now, check if we have just one input or multiple (if we have just one, create a temporary file with the input) + let inputValue; + if (input.inputs.length === 1) { + // Create a temporary file with the input + inputValue = input.inputs[0].inputValue; + } else { + // Ask the user to select one of the inputs + const selectedInput = await window.showQuickPick( + input.inputs.map((input) => input.inputName), + { + "canPickMany": false, + "placeHolder": "Please select the input to use for the action.", + "ignoreFocusOut": true, + } + ); + if (!selectedInput) { + return; + } + inputValue = input.inputs.find((input) => input.inputName === selectedInput).inputValue; + } + const tempInputFile = path.join(os.tmpdir(), `sema4ai_vscode_extension_input_${nameSlugified}.json`); + await writeToFile(tempInputFile, JSON.stringify(inputValue, null, 4)); + targetInput = tempInputFile; + } else { + // No version matched, so, the input is used directly as is (backward compatibility) + OUTPUT_CHANNEL.appendLine( + `Expected v2 input file (with metadata.inputFileVersion === "v2"). As it was not found, considering it's an old file and using the input directly as is for the action.` + ); + targetInput = multiTargetInput; + } + // Ok, input available. Let's create the launch and run it. let debugConfiguration: vscode.DebugConfiguration = { "name": "Config", diff --git a/sema4ai/vscode-client/src/robo/oauth2InInput.ts b/sema4ai/vscode-client/src/robo/oauth2InInput.ts index 7837c991f..81795ba90 100644 --- a/sema4ai/vscode-client/src/robo/oauth2InInput.ts +++ b/sema4ai/vscode-client/src/robo/oauth2InInput.ts @@ -124,7 +124,10 @@ export const loginToAuth2WhereRequired = async (targetInput: string): Promise