diff --git a/openbb_platform/extensions/platform_api/README.md b/openbb_platform/extensions/platform_api/README.md index d4d25b31b4f..52da2347f1e 100644 --- a/openbb_platform/extensions/platform_api/README.md +++ b/openbb_platform/extensions/platform_api/README.md @@ -1,6 +1,6 @@ -# OpenBB Platform API Meta Package +# OpenBB Platform API Launcher -This package is responsible for launching and configuring an OpenBB Platform environment, or FastAPI instance, to use as an OpenBB Workspace custom backend. +This package is responsible for launching and configuring an OpenBB Platform environment, or FastAPI instance, to use as an OpenBB Workspace [custom backend](https://docs.openbb.co/terminal/custom-backend). ## Installation @@ -37,103 +37,20 @@ To run your application as an OpenBB Workspace custom backend, add the path to t openbb-api --app /Users/some_user/path/to/main.py ``` -#### Example Application +#### Arbitrary Instance Name -This script will create a "markdown" widget with the returned text. +Define the FastAPI instance as an arbitrary name with the `--name` argument. -```python -from openbb_platform_api.main import app - -@app.get("/hello") -async def hello() -> str: - """Widget Description Generated By Docstring""" - return "Hello, from OpenBB!" +```sh +openbb-api --app some_file.py --name my_app ``` -Function arguments will populate as widget parameters. +#### Factory Flag -```python -from openbb_platform_api.main import app -from typing import Literal, Optional +If the FastAPI instance is served via a factory function, set the `--factory` flag. -@app.get("/hello") -async def hello(param1: Optional[str] = None, param2: Literal["Choice 1", "Choice 2"] = None, param3: bool = False) -> str: - """Widget Description Generated By Docstring""" - if not param1 and not param2 and not param3: - return "Enter a parameter or make a choice!" - if param3: - return f"Param3 enabled!" - if param2: - return f"You selected: {param2}" - if param1: - return f"You entered: {param1}" - - return "Nothing to return!" -``` - -Create a table widget by returning data shaped as a list of dictionaries (records) - -```python -from openbb_platform_api.main import app - -@app.get("/hello") -async def hello() -> list: - """Widget Description Generated By Docstring""" - return [{"Column 1": "Hello", "Column 2": "from OpenBB!"}] -``` - -#### Widget Config - -Any value from the [`widgets.json`](https://docs.openbb.co/terminal/custom-backend/widgets.json) structure can be passed into the `@app` decorator by including an `openapi_extra` dictionary with the key, `"widget_config"`. - -```python -from openbb_platform_api.main import app - - -@app.get("/hello", - openapi_extra={ - "widget_config": { - "data": { - "table": { - "columnsDefs": [ - {"field": "Column 1", "headerName": "My Column", "headerTooltip": "Tooltip added!", "type": "text"} - ] - } - } - } - } -) -async def hello() -> list: - """Widget Description Generated By Docstring""" - return [{"Column 1": "Hello", "Column 2": "from OpenBB!"}] -``` - -Configurations for `widgets.json` supplied here will override any of the automatically generated content. -If the key does not exist, it will be created. - -#### Auto-Generate ColumnsDefs - -To generate ColumnsDefs for each field in the response data, return a list of Pydantic models with each column being assigned to Field. - -Inherit the BaseModel from the `openbb-core` package by using the imports below. - -Columns, by default, are presented in the order of insertion. - -```python -from openbb_platform_api.main import app -from openbb_core.provider.abstract.data import Data # Inherit this instead of pydantic.BaseModel to represent 1 row of data. -from pydantic import Field - -class MyData(Data): - """MyData Response Model.""" - column_1: str = Field(title="Column 1", description="Description for Column 1") # 'description' will add the column header tooltip to your widget. - column_2: str = Field(title="Column 2", description="Description for Column 2") - - -@app.get("/hello") -async def hello() -> list[MyData]: - """Widget Description Generated By Docstring""" - return [MyData.model_validate({"column_1": "Hello", "column_2": "from OpenBB!"})] +```sh +openbb-api --app some_file.py:main --factory ``` ## Keyword Arguments @@ -143,6 +60,8 @@ The behavior of the script can be configured with the use of arguments and keywo Launcher specific arguments: --app Absolute path to the Python file with the target FastAPI instance. Default is the installed OpenBB Platform API. + --name Name of the FastAPI instance in the app file. Default is 'app'. + --factory Flag to indicate if the app name is a factory function. Default is 'false'. --editable Flag to make widgets.json an editable file that can be modified during runtime. Default is 'false'. --build If the file already exists, changes prompt action to overwrite/append/ignore. Only valid when --editable true. --no-build Do not build the widgets.json file. Use this flag to load an existing widgets.json file without checking for updates. @@ -151,6 +70,7 @@ Launcher specific arguments: --no-filter Do not filter out widgets in widget_settings.json file. --widgets-path Absolute path to the widgets.json file. Default is ~/envs/{env}/assets/widgets.json. Supplying this sets --editable true. --templates-path Absolute path to the workspace_templates.json file. Default is ~/OpenBBUserData/workspace_templates.json. + --copilots-path Absolute path to the copilots.json file. Including this will add the /copilots endpoint to the API. All other arguments will be passed to uvicorn. Here are the most common ones: @@ -199,7 +119,345 @@ Contact the system administrator if you are using a work device and require addi ![This Connection Is Not Private](https://in.norton.com/content/dam/blogs/images/norton/am/this_connection_not_is_private.png) -### Location of `widgets.json` + +## Example Application + +Examples below will assume this code block is at the start of the file. + +```python +from fastapi import FastAPI + +app = FastAPI() +``` + +### Markdown Widget + +This script will create a "markdown" widget with the returned text. + +```python +@app.get("/hello") +async def hello() -> str: + """Widget Description Generated By Docstring""" + return "Hello, from OpenBB!" +``` + +### Table Widget + +Create a table widget by returning data shaped as a list of dictionaries (records) + +```python +@app.get("/hello") +async def hello() -> list: + """Widget Description Generated By Docstring""" + return [{"Column 1": "Hello", "Column 2": "from OpenBB!"}] +``` + +### Metric Widget + +This widget displays a label, value, and optional delta. + +To create a metric widget, import the custom response model below and define it as a return type. + +```python +from openbb_platform_api.response_models import MetricResponseModel + +@app.get("/hello_metric") +async def hello_metric() -> MetricResponseModel: + """Widget description created by docstring.""" + return MetricResponseModel(label="Good Vibes Score", value=100, delta="1%") +``` + +### Query Parameters + +Function arguments will populate as widget parameters. + +```python +from typing import Literal, Optional + +@app.get("/hello") +async def hello(param1: Optional[str] = None, param2: Literal["Choice 1", "Choice 2"] = None, param3: bool = False) -> str: + """Widget Description Generated By Docstring""" + if not param1 and not param2 and not param3: + return "Enter a parameter or make a choice!" + if param3: + return f"Param3 enabled!" + if param2: + return f"You selected: {param2}" + if param1: + return f"You entered: {param1}" + + return "Nothing to return!" +``` + +### Easy Date Picker + +Name the parameter "date", or include "_date" in the name, and type it as a string. + +Additionally, a parameter type of `datetime.date` will work. + +```python +import datetime + +@app.get("/hello_date") +async def hello_date(date: str) -> list: + """Widget description created by docstring.""" + # Workspace returns the date as YYYY-MM-DD + return [{"Hello": "Row 1!"}, {"Hello": "Row 2!"}] + + +@app.get("/hello_date_range") +async def hello_date_range(start: datetime.date, end: datetime.date) -> list: + """Widget description created by docstring.""" + # Workspace returns the date as YYYY-MM-DD + return [{"Hello": "Row 1!"}, {"Hello": "Row 2!"}] +``` + +This demonstrates how to define any of the basic widget parameter types, in a no-frills way. If you just need something that works, it's an easy starting point. + +```python +@app.get("/hello_params") +async def hello_params( + required_param: datetime.date, + param_1: str = "Default", + param_2: int = 0, + param_3: float = None, + param_4: Literal["Choice 1", "Choice 2", "Choice 3"] = "Choice 1", + param_5: bool = True, +) -> list: + """Widget description created by docstring.""" + # Handle the "choices" parameter inside the function to convert the displayed label to the desired one. + choices_dict = {"Choice 1": "do_one", "Choice 2": "do_two", "Choice 3": "do_three"} + choice = choices_dict.get(param_4, None) + + # Do something with the parameters and return the result of work. + return [{"Hello": "Row 1!"}, {"Hello": "Row 2!"}] +``` + +### Annotated Query Params + +Adding helpful placeholder text and tooltips to parameters requires annotating them. This will also help code editors and improve the API documentation. + +Additional settings, compatible with `widgets.json`, are defined in the `json_schema_extra` dictionary, under a key, `x-widget_config` + +```python +from typing import Annotated +from fastapi import Query +``` + +The pattern for annotating a query parameter is: + +```python +my_param: Annotated[str, Query(title="My Title", description="My custom hovertext with detailed information")] = None +``` + +```python +@app.get("/hello_annotated_params") +async def hello_annotated_params( + required_param: Annotated[ + datetime.date, Query(description="The date is required.", title="Required Date") + ], + not_required_param: Annotated[ + Literal["Choice 1", "Choice 2", "Choice 3"], + Query( + description="Choose from a list of possible choices. The default is, 'Choice 1'", + title="Selector", + json_schema_extra={"x-widget_config": {"multiSelect": True}} # This lets you select multiple items from dropdown choices. + ), + ] = "Choice 1", +) -> list: + """Widget description created by docstring.""" + + # Do something with the parameters and return the result of work. + return [{"Hello": "Row 1!"}, {"Hello": "Row 2!"}] +``` + +### Annotated Table Fields + +The procedure for annotating the output is similar to the query parameters, and involves defining a response model. + +A response model is a Data model of Fields. Create one by defining a new class that inherits from "Data", and then define each column as a "Field". + +```python +from openbb_platform_api.response_models import Data +from pydantic import Field +``` + +Optional values should be defined, as `Optional[{type}]`, with a default value of `None`. + +```python +class MyData(Data): + """This is a custom Data model.""" + + # Add fields to the model. + column_1: datetime.date = Field( + description="The date column is a mandatory field.", + title="Some Date", + ) + column_2: Optional[str] = Field( + default=None, + description="This is an optional string column.", + title="Some String", + ) + column_3: int = Field( + default=-1, + description="This is an integer column.", + title="Some Integer", + ) + column_4: float = Field( + default=10.25, + description="This is a float column.", + title="Some Float", + ) + column_5: float = Field( + default=10.25, + description="This is a percent column.", + title="Some Percent", + json_schema_extra={"x-widget_config": {"formatterFn": "percent"}}, + ) + column_6: float = Field( + default=0.1025, + description="This is a normalized percent value adjusted for presentation.", + title="Some Normalized Percent", + json_schema_extra={ + "x-widget_config": { + "formatterFn": "normalizedPercent", + "renderFn": "greenRed", + } + }, + ) + + +@app.get("/hello_data") +async def hello_data() -> list[MyData]: + """Widget description created by docstring.""" + # Do something with the parameters and return the result of work. + return [MyData(column_1=datetime.date.today(), column_2="Hello!")] +``` + + +### PDF Widget + +To create a PDF widget, import the custom response model below and define it as a return type. + +The model handles conversion of the document, from a bytes object, to a base64 encoded string. + + +```python +from openbb_platform_api.response_models import PdfResponseModel + +@app.get("/open_pdf") +async def open_pdf( + url: Annotated[ + str, + Query( + description="URL, or local path, to the PDF document.", + title="URL or Path", + ), + ], + filename: Annotated[ + Optional[str], + Query( + description="Filename to associate with the PDF internally.", + title="Fiilename", + ), + ] = "", + user_agent: Annotated[ + Optional[str], + Query(description="A specific User-Agent string for the request.", title="User-Agent"), + ] = None, +) -> PdfResponseModel: + """Open a PDF document from a URL, or local file path.""" + # pylint: disable=import-outside-toplevel + from pathlib import Path # noqa + from openbb_core.provider.utils.errors import OpenBBError + from openbb_core.provider.utils.helpers import get_requests_session + + if "://" not in url: + file_path = Path(url) + if not file_path.is_file(): + raise OpenBBError(f"The file - {url} - does not exist.") + with open(file_path, "rb") as file: + pdf = file.read() + else: + session = get_requests_session(headers={"User-Agent": user_agent}) + response = session.get(url) + if response.status_code != 200: + raise OpenBBError( + f"Failed to open PDF from URL -> Code: {response.status_code} -> {response.reason}" + ) + + pdf = response.content + + return PdfResponseModel( + filename = filename, + content = pdf, + ) +``` + +### Custom Plotly Chart + +To define a chart widget, update the widget "type" and return the content from the `Figure.to_plotly_json()` method. + + +```python +@app.get( + "/hello_chart", + openapi_extra={"widget_config": {"type": "chart"}}, +) +async def hello_chart() -> dict: + """Widget description created by docstring.""" + from plotly.graph_objs import Bar, Layout, Figure + + fig = Figure( + data=[Bar(x=["A", "B", "C"], y=[1, 2, 3])], + layout=Layout(title="Hello Chart!"), + ) + + return fig.to_plotly_json() +``` + +## Widget Config + +Any value from the [`widgets.json`](https://docs.openbb.co/terminal/custom-backend/widgets-json-reference) structure can be passed into the `@app` decorator by including an `openapi_extra` dictionary with the key, `"widget_config"`. + +Configurations for `widgets.json` supplied here will override any of the automatically generated content. If the key does not exist, it will be created. + +When inserting/updating an entry in a `Params` or `ColumnsDefs` array, the matching identifier is "paramName" and "field", respectively. + +```python +@app.get( + "/hello_data", + openapi_extra={ + "widget_config": { + "data": { + "table": { + "columnsDefs": [ + { + "field": "column_1", + "headerName": "My Column", + "headerTooltip": "This hovertext wins!", + } + ] + } + } + } + }, +) +async def hello_data() -> list[MyData]: + """Widget description created by docstring.""" + # Do something with the parameters and return the result of work. + return [MyData(column_1=datetime.date.today(), column_2="Hello!")] +``` + +## Location of `widgets.json` + +When `--editable` is not flagged, the file remains in memory until the server is stopped. It is regenerated every run. + +The file can be served at any time by visiting the URL (host address will vary): + +```sh +http://127.0.0.1:6900/widgets.json +``` When launched as `openbb-api --editiable`, a file will be stored to disk. By default, that location is: @@ -220,7 +478,7 @@ openbb-api --widgets-path /Users/some_user/path/to/widgets.json ``` -### Location of `templates.json` +### Location of `workspace_templates.json` By default, the location is: @@ -239,3 +497,5 @@ To export the layout, right-click on the dashboard and select, "Export Template" A JSON dictionary will be exported. Insert the contents of the export into "~/OpenBBUserData/workspace_templates.json" by pasting between the JSON list markers, [ ]. If there are more than one, add a comma between each dictionary entry. + +See the page [here](https://docs.openbb.co/terminal/custom-backend/advanced-controls/templates) for details on custom backend templates. diff --git a/openbb_platform/extensions/platform_api/openbb_platform_api/reponse_models.py b/openbb_platform/extensions/platform_api/openbb_platform_api/response_models.py similarity index 69% rename from openbb_platform/extensions/platform_api/openbb_platform_api/reponse_models.py rename to openbb_platform/extensions/platform_api/openbb_platform_api/response_models.py index acd1d206d37..e60ba0f7da6 100644 --- a/openbb_platform/extensions/platform_api/openbb_platform_api/reponse_models.py +++ b/openbb_platform/extensions/platform_api/openbb_platform_api/response_models.py @@ -3,7 +3,7 @@ from typing import Optional, Union from openbb_core.provider.abstract.data import Data -from pydantic import ConfigDict, Field, model_serializer, model_validator +from pydantic import ConfigDict, Field, model_validator class MetricResponseModel(Data): @@ -33,6 +33,8 @@ class MetricResponseModel(Data): "title": "Metric Widget Response Model", "x-widget_config": { "$.type": "metric", + "$.category": "Metric", + "$.searchCategory": "Metric", }, }, ) @@ -81,11 +83,13 @@ class PdfResponseModel(Data): model_config = ConfigDict( extra="ignore", json_schema_extra={ - "title": "PDF Widget Response Model", "x-widget_config": { "$.type": "pdf", "$.refetchInterval": False, - }, + "$.category": "File", + "$.subCategory": "PDF", + "$.searchCategory": "File", + } }, ) @@ -94,7 +98,7 @@ class PdfResponseModel(Data): description="The filename of the PDF content.", json_schema_extra={"x-widget_config": {"exclude": True}}, ) - content: Optional[bytes] = Field( + content: Optional[Union[str, bytes]] = Field( default=None, description="The PDF content to display in the PDF widget.", json_schema_extra={"x-widget_config": {"exclude": True}}, @@ -104,39 +108,42 @@ class PdfResponseModel(Data): description="The URL reference to the PDF content.", json_schema_extra={"x-widget_config": {"exclude": True}}, ) + data_format: Optional[dict] = Field( + default=None, + description="Leave this field empty. This is populated by the model_validator.", + json_schema_extra={"x-widget_config": {"exclude": True}}, + ) - @model_validator(mode="before") + @model_validator(mode="after") @classmethod - def validate_model(cls, values): + def validate_model(cls, values) -> "PdfResponseModel": """Validate the PDF content.""" - - if not values.get("content") and not values.get("url_reference"): - raise ValueError("Either 'content' or 'url_reference' must be provided.") - - if values.get("url_reference") and "://" not in values.get("url_reference"): - raise ValueError("Invalid URL reference provided") - - return values - - @model_serializer - def model_serialize(self) -> dict: - """Serialize the PDF content.""" # pylint: disable=import-outside-toplevel import base64 # noqa from io import BytesIO - file_reference = None - pdf = None + content = getattr(values, "content", None) + file_reference = getattr(values, "url_reference", None) + filename = getattr(values, "filename", "") - if self.content: - pdf = base64.b64encode(BytesIO(self.content).getvalue()).decode("utf-8") - elif self.url_reference: - file_reference = self.url_reference + if not content and not file_reference: + raise ValueError("Either 'content' or 'url_reference' must be provided.") - output = { - "data_format": {"data_type": "pdf", "filename": self.filename}, - "content": pdf, - "url_reference": file_reference, - } + if file_reference and "://" not in file_reference: + raise ValueError("Invalid URL reference provided") - return {k: v for k, v in output.items() if v is not None} + if content: + pdf = ( + base64.b64encode(BytesIO(content).getvalue()).decode("utf-8") + if isinstance(content, bytes) + else content + ) + + values.content = pdf + if file_reference: + values.url_reference = file_reference + elif hasattr(values, "url_reference"): + del values.url_reference + values.data_format = {"data_type": "pdf", "filename": filename} + + return values diff --git a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/api.py b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/api.py index 439f042771d..06b143e86b6 100644 --- a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/api.py +++ b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/api.py @@ -374,6 +374,9 @@ def parse_args(): if _kwargs.get("copilots-path"): _copilots_path = _kwargs.pop("copilots-path", None) + if not str(_copilots_path).endswith("copilots.json"): + _copilots_path = f"{_copilots_path}{'' if _copilots_path.endswith('/') else '/'}copilots.json" + if not str(_copilots_path).startswith("/"): _copilots_path = str(cwd.joinpath(_copilots_path).resolve()) diff --git a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/openapi.py b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/openapi.py index 22a00ff2644..2369e13af7f 100644 --- a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/openapi.py +++ b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/openapi.py @@ -54,6 +54,7 @@ TO_CAPS_STRINGS = [ "Isin", "Figi", "Cusip", + "Pdf", ] @@ -241,7 +242,7 @@ def set_parameter_options(p: dict, p_schema: dict, providers: list[str]) -> dict p["x-widget_config"] = widget_configs else: - p["x-widget_config"] = p["x-widget_config"] = ( + p["x-widget_config"] = ( widget_configs.get(provider, {}) if provider else widget_configs ) @@ -597,8 +598,6 @@ def data_schema_to_columns_defs( # noqa: PLR0912 # pylint: disable=too-many-br else "percent" ) column_def["renderFn"] = "greenRed" - elif cell_data_type == "number": - del column_def["formatterFn"] if k in ["cik", "isin", "figi", "cusip", "sedol", "symbol"]: column_def["cellDataType"] = "text" diff --git a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/widgets.py b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/widgets.py index 0f58f576055..8dfe508f2d4 100644 --- a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/widgets.py +++ b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/widgets.py @@ -381,7 +381,6 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches if key.startswith("$."): var_key[key] = value _ = data_config.pop(key) - break widget_config["data"] = deep_merge_configs( widget_config["data"], diff --git a/openbb_platform/extensions/platform_api/poetry.lock b/openbb_platform/extensions/platform_api/poetry.lock index 9295cb9b24e..60352d79973 100644 --- a/openbb_platform/extensions/platform_api/poetry.lock +++ b/openbb_platform/extensions/platform_api/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -7,7 +7,6 @@ description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiohappyeyeballs-2.4.6-py3-none-any.whl", hash = "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1"}, {file = "aiohappyeyeballs-2.4.6.tar.gz", hash = "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0"}, @@ -20,7 +19,6 @@ description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f"}, {file = "aiohttp-3.11.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854"}, @@ -116,7 +114,7 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] +speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] [[package]] name = "aiosignal" @@ -125,7 +123,6 @@ description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, @@ -141,7 +138,6 @@ description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -154,7 +150,6 @@ description = "High level compatibility layer for multiple asynchronous event lo optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, @@ -168,7 +163,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] trio = ["trio (>=0.26.1)"] [[package]] @@ -191,19 +186,18 @@ description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] [[package]] name = "backoff" @@ -212,7 +206,6 @@ description = "Function decoration for backoff and retry" optional = false python-versions = ">=3.7,<4.0" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, @@ -225,7 +218,6 @@ description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -238,7 +230,6 @@ description = "The Real First Universal Charset Detector. Open, modern and activ optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -341,7 +332,6 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -357,7 +347,7 @@ description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["main"] -markers = "python_version <= \"3.11\" and platform_system == \"Windows\" or python_version >= \"3.12\" and platform_system == \"Windows\"" +markers = "platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -370,7 +360,6 @@ description = "Deep Difference and Search of any Python object/data. Recreate ob optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "deepdiff-8.2.0-py3-none-any.whl", hash = "sha256:5091f2cdfd372b1b9f6bfd8065ba323ae31118dc4e42594371b38c8bea3fd0a4"}, {file = "deepdiff-8.2.0.tar.gz", hash = "sha256:6ec78f65031485735545ffbe7a61e716c3c2d12ca6416886d5e9291fc76c46c3"}, @@ -406,7 +395,6 @@ description = "FastAPI framework, high performance, easy to learn, fast to code, optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf"}, {file = "fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9"}, @@ -428,7 +416,6 @@ description = "A list-like structure which implements collections.abc.MutableSeq optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, @@ -531,7 +518,6 @@ description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -544,7 +530,6 @@ description = "HTML parser based on the WHATWG HTML specification" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, @@ -555,10 +540,10 @@ six = ">=1.9" webencodings = "*" [package.extras] -all = ["chardet (>=2.2)", "genshi", "lxml"] +all = ["chardet (>=2.2)", "genshi", "lxml ; platform_python_implementation == \"CPython\""] chardet = ["chardet (>=2.2)"] genshi = ["genshi"] -lxml = ["lxml"] +lxml = ["lxml ; platform_python_implementation == \"CPython\""] [[package]] name = "idna" @@ -567,7 +552,6 @@ description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -583,7 +567,6 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, @@ -593,12 +576,12 @@ files = [ zipp = ">=3.20" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -608,7 +591,6 @@ description = "An implementation of time.monotonic() for Python 2 & < 3.3" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, @@ -621,7 +603,6 @@ description = "multidict implementation" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, @@ -783,7 +764,7 @@ description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" groups = ["main"] -markers = "python_version == \"3.11\" or python_version >= \"3.12\"" +markers = "python_version >= \"3.11\"" files = [ {file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"}, {file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"}, @@ -849,7 +830,6 @@ description = "OpenBB package with core functionality." optional = false python-versions = "<4.0,>=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "openbb_core-1.4.0-py3-none-any.whl", hash = "sha256:a8f7e63a4b0e642e0d68055ead1ce6ea94867094d4550b6c5de128c33069e3a1"}, {file = "openbb_core-1.4.0.tar.gz", hash = "sha256:50fa9746c461db392c927136bb2ade38cf130a28b1b101db0cb3802f1e03e3bf"}, @@ -879,7 +859,6 @@ description = "Orderly set" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "orderly_set-5.3.0-py3-none-any.whl", hash = "sha256:c2c0bfe604f5d3d9b24e8262a06feb612594f37aa3845650548befd7772945d1"}, {file = "orderly_set-5.3.0.tar.gz", hash = "sha256:80b3d8fdd3d39004d9aad389eaa0eab02c71f0a0511ba3a6d54a935a6c6a0acc"}, @@ -892,7 +871,6 @@ description = "Powerful data structures for data analysis, time series, and stat optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, @@ -980,7 +958,6 @@ description = "Integrate PostHog into any python application." optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "posthog-3.12.1-py2.py3-none-any.whl", hash = "sha256:f749bb4aa4610678c3ea71e0f491cfb2dce570196b9753a04260ff08e52dfb6d"}, {file = "posthog-3.12.1.tar.gz", hash = "sha256:319036f83436981b3bdc750b0ed067b5439c510ed601933f8fc043d0989c73c6"}, @@ -1006,7 +983,6 @@ description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, @@ -1099,7 +1075,6 @@ description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, @@ -1112,7 +1087,7 @@ typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] [[package]] name = "pydantic-core" @@ -1121,7 +1096,6 @@ description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -1235,7 +1209,6 @@ description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, @@ -1254,7 +1227,6 @@ description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -1270,7 +1242,6 @@ description = "Read key-value pairs from a .env file and set them as environment optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -1286,7 +1257,6 @@ description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "python_multipart-0.0.18-py3-none-any.whl", hash = "sha256:efe91480f485f6a361427a541db4796f9e1591afc0fb8e7a4ba06bfbc6708996"}, {file = "python_multipart-0.0.18.tar.gz", hash = "sha256:7a68db60c8bfb82e460637fa4750727b45af1d5e2ed215593f917f64694d34fe"}, @@ -1299,7 +1269,6 @@ description = "World timezone definitions, modern and historical" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, @@ -1312,7 +1281,6 @@ description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1335,7 +1303,6 @@ description = "An extremely fast Python linter and code formatter, written in Ru optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, @@ -1364,7 +1331,6 @@ description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -1377,7 +1343,6 @@ description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -1390,7 +1355,6 @@ description = "The little ASGI library that shines." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d"}, {file = "starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f"}, @@ -1410,7 +1374,6 @@ description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1423,7 +1386,6 @@ description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, @@ -1436,14 +1398,13 @@ description = "HTTP library with thread-safe connection pooling, file post, and optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1455,7 +1416,6 @@ description = "UUID version 7, generating time-sorted UUIDs with 200ns time reso optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "uuid7-0.1.0-py2.py3-none-any.whl", hash = "sha256:5e259bb63c8cb4aded5927ff41b444a80d0c7124e8a0ced7cf44efa1f5cccf61"}, {file = "uuid7-0.1.0.tar.gz", hash = "sha256:8c57aa32ee7456d3cc68c95c4530bc571646defac01895cfc73545449894a63c"}, @@ -1468,7 +1428,6 @@ description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, @@ -1480,7 +1439,7 @@ h11 = ">=0.8" typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] +standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"] [[package]] name = "webencodings" @@ -1489,7 +1448,6 @@ description = "Character encoding aliases for legacy web content" optional = false python-versions = "*" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, @@ -1502,7 +1460,6 @@ description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885"}, {file = "websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397"}, @@ -1582,7 +1539,6 @@ description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, @@ -1680,18 +1636,17 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version <= \"3.11\" or python_version >= \"3.12\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] [metadata] diff --git a/openbb_platform/extensions/platform_api/pyproject.toml b/openbb_platform/extensions/platform_api/pyproject.toml index bc5ef0f78ff..5ad706d9177 100644 --- a/openbb_platform/extensions/platform_api/pyproject.toml +++ b/openbb_platform/extensions/platform_api/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "openbb-platform-api" -version = "1.1.2" +version = "1.1.3" description = "OpenBB Platform API: Launch script and widgets builder for the OpenBB Platform API and Workspace Backend Connector." authors = ["OpenBB "] license = "AGPL-3.0-only" diff --git a/openbb_platform/extensions/platform_api/tests/mock_widgets.json b/openbb_platform/extensions/platform_api/tests/mock_widgets.json index 3eee97b3124..d1a44be2620 100644 --- a/openbb_platform/extensions/platform_api/tests/mock_widgets.json +++ b/openbb_platform/extensions/platform_api/tests/mock_widgets.json @@ -154,6 +154,7 @@ }, { "field": "value", + "formatterFn": null, "headerName": "Value", "headerTooltip": "Survey value.", "cellDataType": "number" @@ -312,6 +313,7 @@ }, { "field": "consumer_sentiment", + "formatterFn": null, "headerName": "Consumer Sentiment", "headerTooltip": "Index of the results of the University of Michigan's monthly Survey of Consumers, which is used to estimate future spending and saving. (1966:Q1=100).", "cellDataType": "number" @@ -470,54 +472,63 @@ }, { "field": "activity_index", + "formatterFn": null, "headerName": "Activity Index", "headerTooltip": "Activity Index.", "cellDataType": "number" }, { "field": "one_year_outlook", + "formatterFn": null, "headerName": "One Year Outlook", "headerTooltip": "One Year Outlook Index.", "cellDataType": "number" }, { "field": "manufacturing_activity", + "formatterFn": null, "headerName": "Manufacturing Activity", "headerTooltip": "Manufacturing Activity Index.", "cellDataType": "number" }, { "field": "non_manufacturing_activity", + "formatterFn": null, "headerName": "Non Manufacturing Activity", "headerTooltip": "Non-Manufacturing Activity Index.", "cellDataType": "number" }, { "field": "capital_expenditures_expectations", + "formatterFn": null, "headerName": "Capital Expenditures Expectations", "headerTooltip": "Capital Expenditures Expectations Index.", "cellDataType": "number" }, { "field": "hiring_expectations", + "formatterFn": null, "headerName": "Hiring Expectations", "headerTooltip": "Hiring Expectations Index.", "cellDataType": "number" }, { "field": "current_hiring", + "formatterFn": null, "headerName": "Current Hiring", "headerTooltip": "Current Hiring Index.", "cellDataType": "number" }, { "field": "labor_costs", + "formatterFn": null, "headerName": "Labor Costs", "headerTooltip": "Labor Costs Index.", "cellDataType": "number" }, { "field": "non_labor_costs", + "formatterFn": null, "headerName": "Non Labor Costs", "headerTooltip": "Non-Labor Costs Index.", "cellDataType": "number" @@ -743,6 +754,7 @@ }, { "field": "diffusion_index", + "formatterFn": null, "headerName": "Diffusion Index", "headerTooltip": "Diffusion Index.", "cellDataType": "number" @@ -911,6 +923,7 @@ }, { "field": "value", + "formatterFn": null, "headerName": "Value", "headerTooltip": "", "cellDataType": "number" @@ -946,6 +959,7 @@ }, { "field": "level", + "formatterFn": "int", "headerName": "Level", "headerTooltip": "The indentation level of the element.", "cellDataType": "number" @@ -1281,6 +1295,7 @@ }, { "field": "value", + "formatterFn": null, "headerName": "Value", "headerTooltip": "CPI index value or period change.", "cellDataType": "number" @@ -1556,30 +1571,35 @@ }, { "field": "balance_total", + "formatterFn": null, "headerName": "Balance Total", "headerTooltip": "Current Account Total Balance (USD)", "cellDataType": "number" }, { "field": "balance_total_services", + "formatterFn": null, "headerName": "Balance Total Services", "headerTooltip": "Current Account Total Services Balance (USD)", "cellDataType": "number" }, { "field": "balance_total_secondary_income", + "formatterFn": null, "headerName": "Balance Total Secondary Income", "headerTooltip": "Current Account Total Secondary Income Balance (USD)", "cellDataType": "number" }, { "field": "balance_total_goods", + "formatterFn": null, "headerName": "Balance Total Goods", "headerTooltip": "Current Account Total Goods Balance (USD)", "cellDataType": "number" }, { "field": "balance_total_primary_income", + "formatterFn": null, "headerName": "Balance Total Primary Income", "headerTooltip": "Current Account Total Primary Income Balance (USD)", "cellDataType": "number" @@ -1602,30 +1622,35 @@ }, { "field": "credits_total_services", + "formatterFn": null, "headerName": "Credits Total Services", "headerTooltip": "Current Account Credits Total Services (USD)", "cellDataType": "number" }, { "field": "credits_total_goods", + "formatterFn": null, "headerName": "Credits Total Goods", "headerTooltip": "Current Account Credits Total Goods (USD)", "cellDataType": "number" }, { "field": "credits_total_primary_income", + "formatterFn": null, "headerName": "Credits Total Primary Income", "headerTooltip": "Current Account Credits Total Primary Income (USD)", "cellDataType": "number" }, { "field": "credits_total_secondary_income", + "formatterFn": null, "headerName": "Credits Total Secondary Income", "headerTooltip": "Current Account Credits Total Secondary Income (USD)", "cellDataType": "number" }, { "field": "credits_total", + "formatterFn": null, "headerName": "Credits Total", "headerTooltip": "Current Account Credits Total (USD)", "cellDataType": "number" @@ -1648,30 +1673,35 @@ }, { "field": "debits_total_services", + "formatterFn": null, "headerName": "Debits Total Services", "headerTooltip": "Current Account Debits Total Services (USD)", "cellDataType": "number" }, { "field": "debits_total_goods", + "formatterFn": null, "headerName": "Debits Total Goods", "headerTooltip": "Current Account Debits Total Goods (USD)", "cellDataType": "number" }, { "field": "debits_total_primary_income", + "formatterFn": null, "headerName": "Debits Total Primary Income", "headerTooltip": "Current Account Debits Total Primary Income (USD)", "cellDataType": "number" }, { "field": "debits_total", + "formatterFn": null, "headerName": "Debits Total", "headerTooltip": "Current Account Debits Total (USD)", "cellDataType": "number" }, { "field": "debits_total_secondary_income", + "formatterFn": null, "headerName": "Debits Total Secondary Income", "headerTooltip": "Current Account Debits Total Secondary Income (USD)", "cellDataType": "number" @@ -1813,6 +1843,7 @@ "columnsDefs": [ { "field": "release_id", + "formatterFn": null, "headerName": "Release ID", "headerTooltip": "The release ID for queries.", "cellDataType": "number" @@ -1927,12 +1958,14 @@ }, { "field": "popularity", + "formatterFn": null, "headerName": "Popularity", "headerTooltip": "Popularity of the series", "cellDataType": "number" }, { "field": "group_popularity", + "formatterFn": null, "headerName": "Group Popularity", "headerTooltip": "Group popularity of the release", "cellDataType": "number" @@ -1946,6 +1979,7 @@ }, { "field": "series_group", + "formatterFn": null, "headerName": "Series Group", "headerTooltip": "The series group ID of the series. This value is used to query for regional data.", "cellDataType": "number" @@ -2449,6 +2483,7 @@ }, { "field": "level", + "formatterFn": null, "headerName": "Level", "headerTooltip": "The indentation level of the element.", "cellDataType": "number" @@ -2462,6 +2497,7 @@ }, { "field": "line", + "formatterFn": null, "headerName": "Line", "headerTooltip": "The line number of the element.", "cellDataType": "number" @@ -2505,6 +2541,7 @@ }, { "field": "value", + "formatterFn": null, "headerName": "Value", "headerTooltip": "The reported value of the series.", "cellDataType": "number" @@ -2810,12 +2847,14 @@ }, { "field": "code", + "formatterFn": null, "headerName": "Code", "headerTooltip": "The code of the region.", "cellDataType": "number" }, { "field": "value", + "formatterFn": null, "headerName": "Value", "headerTooltip": "The obersvation value. The units are defined in the search results by series ID.", "cellDataType": "number" @@ -3025,6 +3064,7 @@ }, { "field": "value", + "formatterFn": null, "headerName": "Value", "headerTooltip": "Price, or change in price, per unit.", "cellDataType": "number" @@ -3133,6 +3173,7 @@ }, { "field": "value", + "formatterFn": null, "headerName": "Value", "headerTooltip": "", "cellDataType": "number" @@ -3168,12 +3209,14 @@ }, { "field": "level", + "formatterFn": "int", "headerName": "Level", "headerTooltip": "The indentation level of the element.", "cellDataType": "number" }, { "field": "line", + "formatterFn": "int", "headerName": "Line", "headerTooltip": "The line number of the series in the table.", "cellDataType": "number" @@ -3230,6 +3273,7 @@ "columnsDefs": [ { "field": "cik", + "formatterFn": null, "headerName": "CIK", "headerTooltip": "Central Index Key (CIK) for the requested entity.", "cellDataType": "text" @@ -3294,6 +3338,7 @@ }, { "field": "cik_number", + "formatterFn": null, "headerName": "CIK Number", "headerTooltip": "Central Index Key (CIK)", "cellDataType": "number" @@ -3546,6 +3591,7 @@ "columnsDefs": [ { "field": "sic_code", + "formatterFn": "int", "headerName": "SIC Code", "headerTooltip": "Sector Industrial Code (SIC)", "cellDataType": "number"