Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 6 additions & 15 deletions .github/scripts/noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,42 @@

ROOT_DIR = Path(__file__).parent.parent.parent
PLATFORM_DIR = ROOT_DIR / "openbb_platform"
PLATFORM_TESTS = [
str(PLATFORM_DIR / p) for p in ["tests", "core", "providers", "extensions"]
]
PLATFORM_TESTS = [str(PLATFORM_DIR / p) for p in ["tests", "core", "providers", "extensions"]]
CLI_DIR = ROOT_DIR / "cli"
CLI_TESTS = CLI_DIR / "tests"


@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13"])
@nox.session(python=["3.10", "3.11", "3.12", "3.13"])
def unit_test_platform(session):
"""Run the test suite."""
session.install("poetry", "toml")
session.install("poetry")
session.run(
"python",
str(PLATFORM_DIR / "dev_install.py"),
"-e",
"all",
external=True,
)
session.install("pytest")
session.install("pytest-cov")

# Determine if we should ignore mcp_server tests based on Python version
ignore_args = []
if session.python == "3.9":
ignore_args = ["--ignore=" + str(PLATFORM_DIR / "extensions/mcp_server/tests")]

session.run(
"pytest",
*PLATFORM_TESTS,
*ignore_args,
f"--cov={PLATFORM_DIR}",
"-m",
"not integration",
)


@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13"])
@nox.session(python=["3.10", "3.11", "3.12", "3.13"])
def unit_test_cli(session):
"""Run the test suite."""
session.install("poetry", "toml")
session.install("poetry")
session.run(
"python",
str(PLATFORM_DIR / "dev_install.py"),
"-e",
"all",
"--cli",
external=True,
)
session.install("pytest")
Expand Down
22 changes: 4 additions & 18 deletions .github/scripts/process_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,17 @@ def process_changelog(file_path, release_pr_number):
pr_occurrences[pr_number].append(i)

# Set of indices to remove: includes all but last occurrence of each PR number
to_remove = {
i
for pr, indices in pr_occurrences.items()
if len(indices) > 1
for i in indices[:-1]
}
to_remove = {i for pr, indices in pr_occurrences.items() if len(indices) > 1 for i in indices[:-1]}
# Also remove any PR entries less than or equal to the specified release PR number
to_remove.update(
i
for pr, indices in pr_occurrences.items()
for i in indices
if pr <= release_pr_number
)
to_remove.update(i for pr, indices in pr_occurrences.items() for i in indices if pr <= release_pr_number)

# Filter out lines marked for removal
processed_lines = [line for i, line in enumerate(lines) if i not in to_remove]

# Final sweep: Ensure no missed duplicates, keeping only the last occurrence
final_lines = []
seen_pr_numbers = set() # Track seen PR numbers to identify duplicates
for line in reversed(
processed_lines
): # Start from the end to keep the last occurrence
for line in reversed(processed_lines): # Start from the end to keep the last occurrence
match = re.search(r"\(#(\d+)\)", line)
if match:
pr_number = int(match.group(1))
Expand All @@ -72,9 +60,7 @@ def process_changelog(file_path, release_pr_number):
if __name__ == "__main__":
# Ensure correct command line arguments
if len(sys.argv) < 3:
logging.error(
"Usage: python process_changelog.py <changelog_file> <release_pr_number>"
)
logging.error("Usage: python process_changelog.py <changelog_file> <release_pr_number>")
sys.exit(1)

file_path = sys.argv[1]
Expand Down
30 changes: 7 additions & 23 deletions .github/scripts/summarize_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import logging
import re
import sys
from typing import Dict

import requests

Expand All @@ -24,12 +23,10 @@ def fetch_pr_details(owner: str, repo: str, pr_number: str, github_token: str) -
return {}


def parse_and_fetch_pr_details(
markdown_text: str, owner: str, repo: str, github_token: str
) -> Dict[str, str]:
def parse_and_fetch_pr_details(markdown_text: str, owner: str, repo: str, github_token: str) -> dict[str, str]:
"""Parse the markdown text and fetch details of PRs mentioned in the text."""
sections = re.split(r"\n## ", markdown_text)
categories: Dict[str, str] = {}
categories: dict[str, str] = {}

for section in sections:
split_section = section.split("\n", 1)
Expand All @@ -49,9 +46,7 @@ def parse_and_fetch_pr_details(
"body": re.sub(r"\s+", " ", pr_details["body"].strip()).strip(),
}
except Exception as e:
logging.error(
"Failed to fetch PR details for PR #%s: %s", pr_number, e
)
logging.error("Failed to fetch PR details for PR #%s: %s", pr_number, e)
if category_name in categories:
categories[category_name].append(pr_info) # type: ignore
else:
Expand All @@ -60,9 +55,7 @@ def parse_and_fetch_pr_details(
return categories


def insert_summary_into_markdown(
markdown_text: str, category_name: str, summary: str
) -> str:
def insert_summary_into_markdown(markdown_text: str, category_name: str, summary: str) -> str:
"""Insert a summary into the markdown text directly under the specified category name."""
marker = f"## {category_name}"
if marker in markdown_text:
Expand All @@ -73,12 +66,7 @@ def insert_summary_into_markdown(
if newline_pos != -1:
# Insert the summary right after the newline that follows the category name
# Ensuring it's on a new line and followed by two newlines before any subsequent content
updated_markdown = (
markdown_text[: newline_pos + 1]
+ "\n"
+ summary
+ markdown_text[newline_pos + 1 :]
)
updated_markdown = markdown_text[: newline_pos + 1] + "\n" + summary + markdown_text[newline_pos + 1 :]
else:
# If there's no newline (e.g., end of file), just append the summary
updated_markdown = markdown_text + "\n\n" + summary + "\n"
Expand Down Expand Up @@ -144,9 +132,7 @@ def summarize_changelog_v2(
]
)
summary = summarize_text_with_openai(aggregated_text, openai_api_key)
updated_markdown = insert_summary_into_markdown(
updated_markdown, category_of_interest, summary
)
updated_markdown = insert_summary_into_markdown(updated_markdown, category_of_interest, summary)

with open(changelog_v2, "w") as file:
logging.info("Writing updated file: %s", changelog_v2)
Expand All @@ -155,9 +141,7 @@ def summarize_changelog_v2(

if __name__ == "__main__":
if len(sys.argv) < 3:
logging.error(
"Usage: python summarize_changelog.py <github_token> <openai_api_key>"
)
logging.error("Usage: python summarize_changelog.py <github_token> <openai_api_key>")
sys.exit(1)

token = sys.argv[1]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-unit-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

matrix:
python_version:
["3.9", "3.10", "3.11", "3.12", "3.13"]
["3.10", "3.11", "3.12", "3.13"]
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-unit-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

matrix:
python_version:
["3.9", "3.10", "3.11", "3.12", "3.13"]
["3.10", "3.11", "3.12", "3.13"]
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
22 changes: 9 additions & 13 deletions assets/scripts/generate_extension_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from importlib import import_module
from json import dump
from pathlib import Path
from typing import Any, Dict, List
from typing import Any

from poetry.core.pyproject.toml import PyProjectTOML

Expand All @@ -21,11 +21,11 @@ def to_title(string: str) -> str:
return " ".join(string.split("_")).title()


def get_packages(path: Path, plugin_key: str) -> Dict[str, Any]:
def get_packages(path: Path, plugin_key: str) -> dict[str, Any]:
"""Get packages."""
SKIP = ["tests", "__pycache__"]
folders = [f for f in path.glob("*") if f.is_dir() and f.stem not in SKIP]
packages: Dict[str, Any] = {}
packages: dict[str, Any] = {}
for f in folders:
pyproject = PyProjectTOML(Path(f, "pyproject.toml"))

Expand All @@ -51,23 +51,19 @@ def to_camel(string: str):
return components[0] + "".join(x.title() for x in components[1:])


def create_item(package_name: str, obj: object, obj_attrs: List[str]) -> Dict[str, Any]:
def create_item(package_name: str, obj: object, obj_attrs: list[str]) -> dict[str, Any]:
"""Create dictionary item from object attributes."""
pkg_spec = OPENBB_PLATFORM_TOML.data["tool"]["poetry"]["dependencies"].get(
package_name
)
pkg_spec = OPENBB_PLATFORM_TOML.data["tool"]["poetry"]["dependencies"].get(package_name)
optional = pkg_spec.get("optional", False) if isinstance(pkg_spec, dict) else False
item = {"packageName": package_name, "optional": optional}
item.update(
{to_camel(a): getattr(obj, a) for a in obj_attrs if getattr(obj, a) is not None}
)
item.update({to_camel(a): getattr(obj, a) for a in obj_attrs if getattr(obj, a) is not None})
return item


def generate_provider_extensions() -> None:
"""Generate providers_extensions.json."""
packages = get_packages(PROVIDERS_PATH, "openbb_provider_extension")
data: List[Dict[str, Any]] = []
data: list[dict[str, Any]] = []
obj_attrs = [
"repr_name",
"description",
Expand All @@ -91,7 +87,7 @@ def generate_provider_extensions() -> None:
def generate_router_extensions() -> None:
"""Generate router_extensions.json."""
packages = get_packages(EXTENSIONS_PATH, "openbb_core_extension")
data: List[Dict[str, Any]] = []
data: list[dict[str, Any]] = []
obj_attrs = ["description"]
for pkg_name, details in sorted(packages.items()):
plugin = details.get("plugin", "")
Expand All @@ -107,7 +103,7 @@ def generate_router_extensions() -> None:
def generate_obbject_extensions() -> None:
"""Generate obbject_extensions.json."""
packages = get_packages(OBBJECT_EXTENSIONS_PATH, "openbb_obbject_extension")
data: List[Dict[str, Any]] = []
data: list[dict[str, Any]] = []
obj_attrs = ["description"]
for pkg_name, details in sorted(packages.items()):
plugin = details.get("plugin", "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ readme = "README.md"
packages = [{ include = "empty_obbject" }]

[tool.poetry.dependencies]
python = "^3.9.21,<3.14"
python = ">=3.10,<3.14"
openbb-core = "^1.4.6"

[build-system]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ readme = "README.md"
packages = [{ include = "empty_provider" }]

[tool.poetry.dependencies]
python = "^3.9.21,<3.14"
python = ">=3.10,<3.14"
openbb-core = "^1.4.6"

[build-system]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ readme = "README.md"
packages = [{ include = "empty_router" }]

[tool.poetry.dependencies]
python = "^3.9.21,<3.14"
python = ">=3.10,<3.14"
openbb-core = "^1.4.6"

[build-system]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ packages = [{ include = "openbb_platform_installer" }] # Update accordingly - t
openbb-update = "openbb_platform_installer.update:main"

[tool.poetry.dependencies]
python = ">=3.9.21,<3.14"
python = ">=3.10,<3.14"
openbb-platform-api = "^1.1.8"

[tool.poetry.group.openbb-all]
Expand Down
6 changes: 3 additions & 3 deletions cli/integration/test_integration_base_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# pylint: disable=unused-variable, redefined-outer-name


class TestController(BaseController):
class DummyController(BaseController):
"""Test controller for the BaseController."""

PATH = "/test/"
Expand All @@ -22,7 +22,7 @@ def print_help(self):
def base_controller():
"""Set up the environment for each test function."""
session = Session() # noqa: F841
controller = TestController()
controller = DummyController()
return controller


Expand Down Expand Up @@ -52,7 +52,7 @@ def test_check_path_invalid(base_controller):
def test_parse_input(base_controller):
"""Test the parse_input method."""
input_str = "/equity/price/help"
expected_output = ["", "equity", "price", "help"]
expected_output = ["equity", "price", "help"]
assert (
base_controller.parse_input(input_str) == expected_output
), "Input parsing failed"
Expand Down
2 changes: 0 additions & 2 deletions cli/integration/test_integration_hub_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ def test_upload_routine_timeout(auth_header, routine_data):
with patch(
"requests.post", side_effect=requests.exceptions.Timeout
) as mocked_post: # noqa: F841

response = upload_routine(auth_header, **routine_data)

assert response is None
Expand All @@ -56,7 +55,6 @@ def test_upload_routine_connection_error(auth_header, routine_data):
with patch(
"requests.post", side_effect=requests.exceptions.ConnectionError
) as mocked_post: # noqa: F841

response = upload_routine(auth_header, **routine_data)

assert response is None
11 changes: 4 additions & 7 deletions cli/openbb_cli/argparse_translator/argparse_argument.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

from typing import (
Any,
List,
Literal,
Optional,
Tuple,
)

from pydantic import BaseModel, model_validator
Expand All @@ -22,9 +19,9 @@ class ArgparseArgumentModel(BaseModel):
default: Any
required: bool
action: Literal["store_true", "store"]
help: Optional[str]
nargs: Optional[Literal["+"]]
choices: Optional[Tuple]
help: str | None
nargs: Literal["+"] | None
choices: tuple | None

@model_validator(mode="after") # type: ignore
@classmethod
Expand Down Expand Up @@ -60,4 +57,4 @@ class ArgparseArgumentGroupModel(BaseModel):
"""Pydantic model for a custom argument group."""

name: str
arguments: List[ArgparseArgumentModel]
arguments: list[ArgparseArgumentModel]
Loading
Loading