[BugFix] - Bring back mypy (#6242)

* first batch

* another

* another

* another batch unyped defs

* final untyped defs

* update yml

* pylint

* remove unecessary __init__.py files

* Revert "remove unecessary __init__.py files"

This reverts commit 5977eccfee.

* fix company_news typing

* rename func

* fix tests

* tests
This commit is contained in:
montezdesousa
2024-03-22 17:53:40 +00:00
committed by GitHub
parent 80d3ac6131
commit 23135abf1b
49 changed files with 344 additions and 280 deletions

View File

@@ -68,8 +68,8 @@ jobs:
- run: |
# Run linters for openbb_platform
if [ -n "${{ env.platform_files }}" ]; then
# TODO: Add mypy to this part of the linting workflow once we're ready
pylint ${{ env.platform_files }}
mypy ${{ env.platform_files }} --ignore-missing-imports --check-untyped-defs
else
echo "No Python files changed in openbb_platform"
fi

View File

@@ -134,19 +134,26 @@ def validate_output(c_out: OBBject) -> OBBject:
json_schema_extra = field.json_schema_extra if field else None
# case where 1st layer field needs to be excluded
if json_schema_extra and json_schema_extra.get("exclude_from_api", None):
if (
json_schema_extra
and isinstance(json_schema_extra, dict)
and json_schema_extra.get("exclude_from_api", None)
):
delattr(c_out, key)
# if it's a model with nested fields
elif is_model(type_):
for field_name, field in type_.__fields__.items():
if field.json_schema_extra and field.json_schema_extra.get(
"exclude_from_api", None
extra = getattr(field, "json_schema_extra", None)
if (
extra
and isinstance(extra, dict)
and extra.get("exclude_from_api", None)
):
delattr(value, field_name)
# if it's a yet a nested model we need to go deeper in the recursion
elif is_model(field.annotation):
elif is_model(getattr(field, "annotation", None)):
exclude_fields_from_api(field_name, getattr(value, field_name))
for k, v in c_out.model_copy():

View File

@@ -6,7 +6,7 @@ from datetime import datetime
from inspect import Parameter, signature
from sys import exc_info
from time import perf_counter_ns
from typing import Any, Callable, Dict, List, Optional, Tuple
from typing import Any, Callable, Dict, List, Optional, Tuple, Type
from warnings import catch_warnings, showwarning, warn
from fastapi.params import Query
@@ -181,7 +181,7 @@ class ParametersBuilder:
def _warn_kwargs(
provider_choices: Dict[str, Any],
extra_params: Dict[str, Any],
model: BaseModel,
model: Type[BaseModel],
) -> None:
"""Warn if kwargs received and ignored by the validation model."""
# We only check the extra_params annotation because ignored fields
@@ -247,7 +247,7 @@ class ParametersBuilder:
@classmethod
def build(
cls,
args: Tuple[Any],
args: Tuple[Any, ...],
execution_context: ExecutionContext,
func: Callable,
route: str,
@@ -317,7 +317,7 @@ class StaticCommandRunner:
async def _execute_func(
cls,
route: str,
args: Tuple[Any],
args: Tuple[Any, ...],
execution_context: ExecutionContext,
func: Callable,
kwargs: Dict[str, Any],

View File

@@ -12,10 +12,10 @@ from openbb_core.app.version import VERSION, get_major_minor
class DeprecationSummary(str):
"""A string subclass that can be used to store deprecation metadata."""
def __new__(cls, value, metadata):
def __new__(cls, value: str, metadata: DeprecationWarning):
"""Create a new instance of the class."""
obj = str.__new__(cls, value)
obj.metadata = metadata
setattr(obj, "metadata", metadata)
return obj

View File

@@ -7,7 +7,7 @@ class SingletonMeta(type, Generic[T]):
# TODO : check if we want to update this to be thread safe
_instances: Dict[T, T] = {}
def __call__(cls, *args, **kwargs):
def __call__(cls: "SingletonMeta", *args, **kwargs):
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance

View File

@@ -2,7 +2,7 @@
from abc import abstractmethod
from datetime import date, datetime, timedelta
from typing import Any, Dict, List, Literal, Optional, Union, _GenericAlias
from typing import Any, Dict, List, Literal, Optional, Union, _GenericAlias # type: ignore
from pydantic import (
BaseModel,
@@ -58,7 +58,7 @@ class APIEx(Example):
@staticmethod
def _unpack_type(type_: type) -> set:
"""Unpack types from types, example Union[List[str], int] -> {str, int}."""
"""Unpack types from types, example Union[List[str], int] -> {typing._GenericAlias, int}."""
if (
hasattr(type_, "__args__")
and type(type_) # pylint: disable=unidiomatic-typecheck

View File

@@ -1,6 +1,6 @@
from datetime import datetime
from inspect import isclass
from typing import Any, Dict
from typing import Any, Dict, Optional, Sequence, Union
import numpy as np
import pandas as pd
@@ -36,7 +36,7 @@ class Metadata(BaseModel):
value is kept or trimmed to 80 characters.
"""
for arg, arg_val in v.items():
new_arg_val = None
new_arg_val: Optional[Union[str, dict[str, Sequence[Any]]]] = None
# Data
if isclass(type(arg_val)) and issubclass(type(arg_val), Data):
@@ -47,30 +47,32 @@ class Metadata(BaseModel):
# List[Data]
if isinstance(arg_val, list) and issubclass(type(arg_val[0]), Data):
columns = [list(d.model_dump().keys()) for d in arg_val]
columns = (item for sublist in columns for item in sublist) # flatten
_columns = [list(d.model_dump().keys()) for d in arg_val]
ld_columns = (
item for sublist in _columns for item in sublist
) # flatten
new_arg_val = {
"type": f"List[{type(arg_val[0]).__name__}]",
"columns": list(set(columns)),
"columns": list(set(ld_columns)),
}
# DataFrame
elif isinstance(arg_val, pd.DataFrame):
columns = (
df_columns = (
list(arg_val.index.names) + arg_val.columns.tolist()
if any(index is not None for index in list(arg_val.index.names))
else arg_val.columns.tolist()
)
new_arg_val = {
"type": f"{type(arg_val).__name__}",
"columns": columns,
"columns": df_columns,
}
# List[DataFrame]
elif isinstance(arg_val, list) and issubclass(
type(arg_val[0]), pd.DataFrame
):
columns = [
ldf_columns = [
(
list(df.index.names) + df.columns.tolist()
if any(index is not None for index in list(df.index.names))
@@ -80,7 +82,7 @@ class Metadata(BaseModel):
]
new_arg_val = {
"type": f"List[{type(arg_val[0]).__name__}]",
"columns": columns,
"columns": ldf_columns,
}
# Series
@@ -92,7 +94,7 @@ class Metadata(BaseModel):
# List[Series]
elif isinstance(arg_val, list) and isinstance(arg_val[0], pd.Series):
columns = [
ls_columns = [
(
list(series.index.names) + [series.name]
if any(index is not None for index in list(series.index.names))
@@ -102,7 +104,7 @@ class Metadata(BaseModel):
]
new_arg_val = {
"type": f"List[{type(arg_val[0]).__name__}]",
"columns": columns,
"columns": ls_columns,
}
# ndarray

View File

@@ -8,6 +8,7 @@ from typing import (
ClassVar,
Dict,
Generic,
Hashable,
List,
Literal,
Optional,
@@ -82,30 +83,32 @@ class OBBject(Tagged, Generic[T]):
@classmethod
def results_type_repr(cls, params: Optional[Any] = None) -> str:
"""Return the results type name."""
"""Return the results type representation."""
results_field = cls.model_fields.get("results")
type_ = params[0] if params else results_field.annotation
name = type_.__name__ if hasattr(type_, "__name__") else str(type_)
type_repr = "Any"
if results_field:
type_ = params[0] if params else results_field.annotation
type_repr = getattr(type_, "__name__", str(type_))
if (json_schema_extra := results_field.json_schema_extra) is not None:
model = json_schema_extra.get("model")
if json_schema_extra := getattr(results_field, "json_schema_extra", {}):
model = json_schema_extra.get("model", "Any")
if json_schema_extra.get("is_union"):
return f"Union[List[{model}], {model}]"
if json_schema_extra.get("has_list"):
return f"List[{model}]"
if json_schema_extra.get("is_union"):
return f"Union[List[{model}], {model}]"
if json_schema_extra.get("has_list"):
return f"List[{model}]"
return model
return model
if "typing." in str(type_):
unpack_optional = sub(r"Optional\[(.*)\]", r"\1", str(type_))
name = sub(
r"(\w+\.)*(\w+)?(\, NoneType)?",
r"\2",
unpack_optional,
)
if "typing." in str(type_):
unpack_optional = sub(r"Optional\[(.*)\]", r"\1", str(type_))
type_repr = sub(
r"(\w+\.)*(\w+)?(\, NoneType)?",
r"\2",
unpack_optional,
)
return name
return type_repr
@classmethod
def model_parametrized_name(cls, params: Any) -> str:
@@ -189,9 +192,9 @@ class OBBject(Tagged, Generic[T]):
# List[List | str | int | float] | Dict[str, Dict | List | BaseModel]
else:
try:
df = pd.DataFrame(res)
df = pd.DataFrame(res) # type: ignore[call-overload]
# Set index, if any
if index is not None and index in df.columns:
if df is not None and index is not None and index in df.columns:
df.set_index(index, inplace=True)
except ValueError:
@@ -245,7 +248,7 @@ class OBBject(Tagged, Generic[T]):
orient: Literal[
"dict", "list", "series", "split", "tight", "records", "index"
] = "list",
) -> Dict[str, List]:
) -> Union[Dict[Hashable, Any], List[Dict[Hashable, Any]]]:
"""Convert results field to a dictionary using any of pandas to_dict options.
Parameters
@@ -256,25 +259,21 @@ class OBBject(Tagged, Generic[T]):
Returns
-------
Dict[str, List]
Dictionary of lists.
Union[Dict[Hashable, Any], List[Dict[Hashable, Any]]]
Dictionary of lists or list of dictionaries if orient is "records".
"""
df = self.to_dataframe(index=None) # type: ignore
transpose = False
if orient == "list":
transpose = True
if not isinstance(self.results, dict):
transpose = False
else: # Only enter the loop if self.results is a dictionary
self.results: Dict[str, Any] = self.results # type: ignore
for _, value in self.results.items():
if not isinstance(value, dict):
transpose = False
break
if transpose:
df = self.to_dataframe(index=None)
if (
orient == "list"
and isinstance(self.results, dict)
and all(
isinstance(value, dict)
for value in self.results.values() # pylint: disable=no-member
)
):
df = df.T
results = df.to_dict(orient=orient)
if orient == "list" and "index" in results:
if isinstance(results, dict) and orient == "list" and "index" in results:
del results["index"]
return results

View File

@@ -18,7 +18,7 @@ from openbb_core.provider.query_executor import QueryExecutor
from openbb_core.provider.registry_map import MapType, RegistryMap
from openbb_core.provider.utils.helpers import to_snake_case
TupleFieldType = Tuple[str, type, Any]
TupleFieldType = Tuple[str, Optional[Type], Optional[Any]]
@dataclass
@@ -26,8 +26,8 @@ class DataclassField:
"""Dataclass field."""
name: str
type_: type
default: Any
annotation: Optional[Type]
default: Optional[Any]
@dataclass
@@ -154,20 +154,19 @@ class ProviderInterface(metaclass=SingletonMeta):
def create_executor(self) -> QueryExecutor:
"""Get query executor."""
return self._query_executor(self._registry_map.registry) # type: ignore
return self._query_executor(self._registry_map.registry) # type: ignore[operator]
@staticmethod
def _merge_fields(
current: DataclassField, incoming: DataclassField, query: bool = False
) -> DataclassField:
current_name = current.name
current_type = current.type_
current_desc = getattr(current.default, "description", "")
"""Merge 2 dataclass fields."""
curr_name = current.name
curr_type: Optional[Type] = current.annotation
curr_desc = getattr(current.default, "description", "")
incoming_type = incoming.type_
incoming_desc = getattr(incoming.default, "description", "")
F: Union[Callable, object] = Query if query else FieldInfo
inc_type: Optional[Type] = incoming.annotation
inc_desc = getattr(incoming.default, "description", "")
def split_desc(desc: str) -> str:
"""Split field description."""
@@ -175,34 +174,33 @@ class ProviderInterface(metaclass=SingletonMeta):
detail = item[0] if item else ""
return detail
curr_detail = split_desc(current_desc)
inc_detail = split_desc(incoming_desc)
curr_detail = split_desc(curr_desc)
inc_detail = split_desc(inc_desc)
providers = f"{current.default.title},{incoming.default.title}"
curr_title = getattr(current.default, "title", "")
inc_title = getattr(incoming.default, "title", "")
providers = ",".join([curr_title, inc_title])
formatted_prov = providers.replace(",", ", ")
if SequenceMatcher(None, curr_detail, inc_detail).ratio() > 0.8:
new_desc = f"{curr_detail} (provider: {formatted_prov})"
else:
new_desc = f"{current_desc};\n {incoming_desc}"
new_desc = f"{curr_desc};\n {inc_desc}"
merged_default = F( # type: ignore
default=current.default.default,
QF: Callable = Query if query else FieldInfo # type: ignore[assignment]
merged_default = QF(
default=getattr(current.default, "default", None),
title=providers,
description=new_desc,
)
merged_type = (
Union[current_type, incoming_type]
if current_type != incoming_type
else current_type
merged_type: Optional[Type] = (
Union[curr_type, inc_type] # type: ignore[assignment]
if curr_type != inc_type
else curr_type
)
return DataclassField(
name=current_name,
type_=merged_type, # type: ignore
default=merged_default,
)
return DataclassField(curr_name, merged_type, merged_default)
@staticmethod
def _create_field(
@@ -213,9 +211,7 @@ class ProviderInterface(metaclass=SingletonMeta):
force_optional: bool = False,
) -> DataclassField:
new_name = name.replace(".", "_")
# field.type_ don't work for nested types
# field.outer_type_ don't work for Optional nested types
type_ = field.annotation
annotation = field.annotation
additional_description = ""
if (extra := field.json_schema_extra) and (
@@ -239,7 +235,7 @@ class ProviderInterface(metaclass=SingletonMeta):
if field.is_required():
if force_optional:
type_ = Optional[type_] # type: ignore
annotation = Optional[annotation] # type: ignore
default = None
else:
default = ...
@@ -247,24 +243,32 @@ class ProviderInterface(metaclass=SingletonMeta):
default = field.default
if query:
# We need to use query if we want the field description to show up in the
# swagger, it's a fastapi limitation
default = Query(
default=default,
title=provider_name,
description=description,
alias=field.alias or None,
json_schema_extra=field.json_schema_extra,
# We need to use query if we want the field description to show
# up in the swagger, it's a fastapi limitation
return DataclassField(
new_name,
annotation,
Query(
default=default,
title=provider_name,
description=description,
alias=field.alias or None,
json_schema_extra=getattr(field, "json_schema_extra", None),
),
)
elif provider_name:
default: FieldInfo = Field(
default=default or None,
title=provider_name,
description=description,
json_schema_extra=field.json_schema_extra,
if provider_name:
return DataclassField(
new_name,
annotation,
Field(
default=default or None,
title=provider_name,
description=description,
json_schema_extra=field.json_schema_extra,
),
)
return DataclassField(new_name, type_, default)
return DataclassField(new_name, annotation, default)
@classmethod
def _extract_params(
@@ -282,7 +286,7 @@ class ProviderInterface(metaclass=SingletonMeta):
standard[incoming.name] = (
incoming.name,
incoming.type_,
incoming.annotation,
incoming.default,
)
else:
@@ -305,7 +309,7 @@ class ProviderInterface(metaclass=SingletonMeta):
extra[updated.name] = (
updated.name,
updated.type_,
updated.annotation,
updated.default,
)
@@ -331,7 +335,7 @@ class ProviderInterface(metaclass=SingletonMeta):
standard[incoming.name] = (
incoming.name,
incoming.type_,
incoming.annotation,
incoming.default,
)
else:
@@ -357,7 +361,7 @@ class ProviderInterface(metaclass=SingletonMeta):
extra[updated.name] = (
updated.name,
updated.type_,
updated.annotation,
updated.default,
)
@@ -393,14 +397,14 @@ class ProviderInterface(metaclass=SingletonMeta):
standard, extra = self._extract_params(providers)
result[model_name] = {
"standard": make_dataclass( # type: ignore
"standard": make_dataclass(
cls_name=model_name,
fields=list(standard.values()),
fields=list(standard.values()), # type: ignore[arg-type]
bases=(StandardParams,),
),
"extra": make_dataclass( # type: ignore
"extra": make_dataclass(
cls_name=model_name,
fields=list(extra.values()),
fields=list(extra.values()), # type: ignore[arg-type]
bases=(ExtraParams,),
),
}
@@ -464,14 +468,14 @@ class ProviderInterface(metaclass=SingletonMeta):
extra: dict
standard, extra = self._extract_data(providers)
result[model_name] = {
"standard": make_dataclass( # type: ignore
"standard": make_dataclass(
cls_name=model_name,
fields=list(standard.values()),
fields=list(standard.values()), # type: ignore[arg-type]
bases=(StandardData,),
),
"extra": make_dataclass( # type: ignore
"extra": make_dataclass(
cls_name=model_name,
fields=list(extra.values()),
fields=list(extra.values()), # type: ignore[arg-type]
bases=(ExtraData,),
),
}

View File

@@ -350,7 +350,7 @@ class SignatureInspector:
func = cls.inject_return_type(
func=func,
return_map=provider_interface.return_map.get(model),
return_map=provider_interface.return_map.get(model, {}),
model=model,
)
@@ -374,11 +374,7 @@ class SignatureInspector:
return_map: Dict[str, dict],
model: str,
) -> Callable[P, OBBject]:
"""
Inject full return model into the function.
Also updates __name__ and __doc__ for API schemas.
"""
"""Inject full return model into the function. Also updates __name__ and __doc__ for API schemas."""
results: Dict[str, Any] = {"list_type": [], "dict_type": []}
for provider, return_data in return_map.items():
@@ -397,7 +393,7 @@ class SignatureInspector:
if not v:
continue
inner_type = SerializeAsAny[
inner_type: Any = SerializeAsAny[ # type: ignore[misc,valid-type]
Annotated[
Union[tuple(v)], # type: ignore
Field(discriminator="provider"),

View File

@@ -42,10 +42,11 @@ class Account:
def _log_account_command(func): # pylint: disable=E0213
"""Log account command."""
@wraps(func)
@wraps(func) # type: ignore[arg-type]
def wrapped(self, *args, **kwargs):
try:
result = func(self, *args, **kwargs) # pylint: disable=E1102
# pylint: disable=E1102
result = func(self, *args, **kwargs) # type: ignore[operator]
except Exception as e:
raise OpenBBError(e) from e
finally:
@@ -57,8 +58,9 @@ class Account:
ls.log(
user_settings=user_settings,
system_settings=system_settings,
route=f"/account/{func.__name__}", # pylint: disable=E1101
func=func,
# pylint: disable=E1101
route=f"/account/{func.__name__}", # type: ignore[attr-defined]
func=func, # type: ignore[arg-type]
kwargs={}, # don't want any credentials being logged by accident
exec_info=exc_info(),
)

View File

@@ -54,8 +54,9 @@ def exception_handler(func: Callable[P, R]) -> Callable[P, R]:
# Get the last traceback object from the exception
tb = e.__traceback__
while tb.tb_next is not None:
tb = tb.tb_next
if tb:
while tb.tb_next is not None:
tb = tb.tb_next
if isinstance(e, ValidationError):
error_list = []
@@ -70,7 +71,10 @@ def exception_handler(func: Callable[P, R]) -> Callable[P, R]:
f"input_type={type(error['input']).__name__}, "
f"input_value={error['input']}]\n"
)
error_info = f" For further information visit {error['url']}\n"
url = error.get("url")
error_info = (
f" For further information visit {url}\n" if url else ""
)
error_list.append(arg_error + error_details + error_info)
error_list.insert(0, validation_error)

View File

@@ -3,7 +3,7 @@
import ast
import json
from datetime import time
from typing import Dict, Iterable, List, Optional, Union
from typing import Dict, List, Optional, Union
import numpy as np
import pandas as pd
@@ -17,7 +17,7 @@ from openbb_core.provider.abstract.data import Data
def basemodel_to_df(
data: Union[List[Data], Data],
index: Optional[Union[None, str, Iterable]] = None,
index: Optional[str] = None,
) -> pd.DataFrame:
"""Convert list of BaseModel to a Pandas DataFrame."""
if isinstance(data, list):
@@ -44,9 +44,7 @@ def basemodel_to_df(
df.set_index("date", inplace=True)
df.sort_index(axis=0, inplace=True)
else:
df = (
df.set_index(index) if index is not None and index in df.columns else df
)
df = df.set_index(index) if index and index in df.columns else df
return df

View File

@@ -65,7 +65,7 @@ class Fetcher(Generic[Q, R]):
super().__init_subclass__(*args, **kwargs)
if cls.aextract_data != Fetcher.aextract_data:
cls.extract_data = cls.aextract_data
cls.extract_data = cls.aextract_data # type: ignore[method-assign]
elif cls.extract_data == Fetcher.extract_data:
raise NotImplementedError(
"Fetcher subclass must implement either extract_data or aextract_data"

View File

@@ -146,7 +146,7 @@ class RegistryMap:
def extract_data_model(fetcher: Fetcher, provider_str: str) -> BaseModel:
"""Extract info (fields and docstring) from fetcher query params or data."""
model: BaseModel = RegistryMap._get_model(fetcher, "data")
model_name = getattr(model, "__name__", "")
fields = {}
for field_name, field in model.model_fields.items():
field.serialization_alias = field_name
@@ -161,8 +161,8 @@ class RegistryMap:
),
)
provider_model = create_model(
model.__name__.replace("Data", ""),
provider_model = create_model( # type: ignore[call-overload]
model_name.replace("Data", ""),
__base__=model,
__doc__=model.__doc__,
__module__=model.__module__,
@@ -171,7 +171,11 @@ class RegistryMap:
# Replace the provider models in the modules with the new models we created
# To make sure provider field is defined to be the provider string
setattr(sys.modules[model.__module__], model.__name__, provider_model)
# This is hacky, but we need to have `provider: Literal['provider_name']`
# in the model to serve as union discriminator for the API validation
# the alternative would be to specify it manually in all the models
if model_name:
setattr(sys.modules[model.__module__], model_name, provider_model)
return provider_model

View File

@@ -76,7 +76,7 @@ class ConsumerPriceIndexQueryParams(QueryParams):
country: str = Field(
description=QUERY_DESCRIPTIONS.get("country"),
choices=CPI_COUNTRIES, # type: ignore
json_schema_extra={"choices": CPI_COUNTRIES}, # type: ignore[dict-item]
)
units: CPI_UNITS = Field(
default="growth_same",

View File

@@ -32,7 +32,7 @@ class SpotRateQueryParams(QueryParams):
category: str = Field(
default="spot_rate",
description="Rate category. Options: spot_rate, par_yield.",
choices=["par_yield", "spot_rate"],
json_schema_extra={"choices": ["par_yield", "spot_rate"]},
)
@field_validator("category", mode="before", check_fields=False)

View File

@@ -2,11 +2,20 @@
import asyncio
import re
from datetime import datetime
from datetime import date, datetime
from difflib import SequenceMatcher
from functools import partial
from inspect import iscoroutinefunction
from typing import Awaitable, Callable, List, Literal, Optional, TypeVar, Union, cast
from typing import (
Awaitable,
Callable,
List,
Literal,
Optional,
TypeVar,
Union,
cast,
)
import requests
from anyio import start_blocking_portal
@@ -21,6 +30,7 @@ from openbb_core.provider.utils.client import (
T = TypeVar("T")
P = ParamSpec("P")
D = TypeVar("D", bound="Data")
def check_item(item: str, allowed: List[str], threshold: float = 0.75) -> None:
@@ -177,13 +187,13 @@ async def amake_requests(
is_exception = isinstance(result, Exception)
if is_exception and kwargs.get("raise_for_status", False):
raise result
raise result # type: ignore[misc]
if is_exception or not result:
continue
results.extend( # type: ignore
result if isinstance(result, list) else [result]
results.extend(
result if isinstance(result, list) else [result] # type: ignore[list-item]
)
return results
@@ -283,17 +293,23 @@ def run_async(
def filter_by_dates(
data: List[Data],
start_date: datetime,
end_date: datetime,
) -> List[Data]:
data: List[D], start_date: Optional[date] = None, end_date: Optional[date] = None
) -> List[D]:
"""Filter data by dates."""
if not any([start_date, end_date]):
if start_date is None and end_date is None:
return data
return list(
filter(
lambda d: start_date <= d.date.date() <= end_date,
data,
)
)
def _filter(d: Data) -> bool:
_date = getattr(d, "date", None)
dt = _date.date() if _date and isinstance(_date, datetime) else _date
if dt:
if start_date and end_date:
return start_date <= dt <= end_date
if start_date:
return dt >= start_date
if end_date:
return dt <= end_date
return True
return False
return list(filter(_filter, data))

View File

@@ -59,6 +59,6 @@ def test_get_user_settings_(mock_user_service):
mock_user_settings = MagicMock(spec=UserSettings, profile=MagicMock(active=True))
mock_user_service.default_user_settings = mock_user_settings
mock_user_service.return_value = mock_user_service
result = asyncio.run(get_user_settings(MagicMock(), mock_user_service))
result = asyncio.run(get_user_settings(MagicMock(), mock_user_service)) # type: ignore[arg-type]
assert result == mock_user_settings

View File

@@ -9,6 +9,6 @@ from openbb_core.api.dependency.coverage import get_command_map
def test_get_system_settings():
"""Test get_system_settings."""
response = asyncio.run(get_command_map(MagicMock()))
response = asyncio.run(get_command_map(MagicMock())) # type: ignore[arg-type]
assert response

View File

@@ -14,6 +14,6 @@ def test_get_system_settings(mock_system_service):
"""Test get_system_settings."""
mock_system_service.return_value.system_settings = SystemSettings()
response = asyncio.run(get_system_settings(MagicMock(), mock_system_service))
response = asyncio.run(get_system_settings(MagicMock(), mock_system_service)) # type: ignore[arg-type]
assert response

View File

@@ -272,7 +272,7 @@ def test_filter_log_line(input_text, expected_output, formatter):
def test_formatException_invalid():
with pytest.raises(Exception):
formatter.formatException(Exception("Big bad error"))
formatter.formatException(Exception("Big bad error")) # type: ignore[attr-defined]
def test_format(formatter):

View File

@@ -85,4 +85,4 @@ def test_update_handlers():
for hdlr in handlers:
if isinstance(hdlr, (MockPosthogHandler, MockPathTrackingFileHandler)):
assert hdlr.settings == changed_settings
assert hdlr.formatter.settings == changed_settings
assert hdlr.formatter.settings == changed_settings # type: ignore[union-attr]

View File

@@ -8,6 +8,7 @@ from openbb_core.app.model.abstract.error import OpenBBError
from pydantic import BaseModel
# ruff: noqa: S106
# pylint: disable=redefined-outer-name, protected-access
class MockLoggingSettings:
@@ -24,9 +25,7 @@ class MockOBBject(BaseModel):
@pytest.fixture(scope="function")
def logging_service():
mock_system_settings = Mock()
mock_system_settings = "mock_system_settings"
mock_user_settings = Mock()
mock_user_settings = "mock_user_settings"
mock_setup_handlers = Mock()
mock_log_startup = Mock()
@@ -40,19 +39,37 @@ def logging_service():
"openbb_core.app.logs.logging_service.LoggingService._log_startup",
mock_log_startup,
):
logging_service = LoggingService(
_logging_service = LoggingService(
system_settings=mock_system_settings,
user_settings=mock_user_settings,
)
assert mock_setup_handlers.assert_called_once
assert mock_log_startup.assert_called_once
return logging_service
return _logging_service
def test_correctly_initialized(logging_service):
assert logging_service
def test_correctly_initialized():
mock_system_settings = Mock()
mock_user_settings = Mock()
mock_setup_handlers = Mock()
mock_log_startup = Mock()
with patch(
"openbb_core.app.logs.logging_service.LoggingSettings",
MockLoggingSettings,
), patch(
"openbb_core.app.logs.logging_service.LoggingService._setup_handlers",
mock_setup_handlers,
), patch(
"openbb_core.app.logs.logging_service.LoggingService._log_startup",
mock_log_startup,
):
LoggingService(
system_settings=mock_system_settings,
user_settings=mock_user_settings,
)
mock_setup_handlers.assert_called_once()
mock_log_startup.assert_called_once()
def test_logging_settings_setter(logging_service):
@@ -68,8 +85,8 @@ def test_logging_settings_setter(logging_service):
custom_user_settings,
)
assert logging_service.logging_settings.system_settings == "custom_system_settings"
assert logging_service.logging_settings.user_settings == "custom_user_settings"
assert logging_service.logging_settings.system_settings == "custom_system_settings" # type: ignore[attr-defined]
assert logging_service.logging_settings.user_settings == "custom_user_settings" # type: ignore[attr-defined]
def test_log_startup(logging_service):
@@ -93,7 +110,10 @@ def test_log_startup(logging_service):
expected_log_data = {
"route": "test_route",
"PREFERENCES": "your_preferences",
"KEYS": {"username": "defined", "password": "defined"},
"KEYS": {
"username": "defined",
"password": "defined", # pragma: allowlist secret
},
"SYSTEM": "your_system_settings",
"custom_headers": {"X-OpenBB-Test": "test"},
}
@@ -101,7 +121,7 @@ def test_log_startup(logging_service):
"STARTUP: %s ",
json.dumps(expected_log_data),
)
mock_get_logger.assert_called_once
mock_get_logger.assert_called_once()
@pytest.mark.parametrize(
@@ -163,7 +183,7 @@ def test_log(
exec_info=exec_info,
custom_headers=custom_headers,
)
assert mock_log_startup.assert_called_once
mock_log_startup.assert_called_once()
else:
mock_info = mock_get_logger.return_value.info

View File

@@ -2,6 +2,7 @@ import os
import tempfile
from pathlib import Path
from time import time
from typing import List
from unittest.mock import MagicMock, patch
import pytest
@@ -92,7 +93,7 @@ def mock_path():
def test_remove_file_list_no_files(mock_path):
# Arrange
# Let's assume the file list is empty, meaning there are no files to remove
file_list = []
file_list: List[Path] = []
# Act
remove_file_list(file_list)

View File

@@ -8,7 +8,7 @@ def test_tagged_model():
def test_fields():
fields = Tagged.__fields__
fields = Tagged.model_fields
fields_keys = fields.keys()
assert "id" in fields_keys

View File

@@ -19,7 +19,7 @@ def test_warn_model(category, message):
def test_fields():
fields = Warning_.__fields__
fields = Warning_.model_fields
fields_keys = fields.keys()
assert "category" in fields_keys

View File

@@ -41,7 +41,7 @@ def test_charting_config_validation():
chart = Chart(content=content, format=chart_format)
with pytest.raises(ValueError):
chart.content = "Invalid Content"
chart.content = "Invalid Content" # type: ignore[assignment]
assert chart.content == content
assert chart.format == chart_format

View File

@@ -1,11 +1,12 @@
from openbb_core.app.model.hub.hub_session import HubSession
from pydantic import SecretStr
# ruff: noqa: S105 S106
def test_hub_session():
session = HubSession(
access_token="mock_access_token",
access_token=SecretStr("mock_access_token"),
token_type="mock_token_type",
email="mock_email",
user_uuid="mock_user_uuid",
@@ -21,7 +22,7 @@ def test_hub_session():
def test_fields():
fields = HubSession.__fields__
fields = HubSession.model_fields
fields_keys = fields.keys()
assert "access_token" in fields_keys

View File

@@ -13,7 +13,7 @@ def test_command_context():
def test_fields():
fields = CommandContext.__fields__
fields = CommandContext.model_fields
fields_keys = fields.keys()
assert "user_settings" in fields_keys

View File

@@ -1,14 +1,14 @@
from openbb_core.app.model.defaults import Defaults
def test_defaultst():
def test_defaults():
cc = Defaults(routes={"test": {"test": "test"}})
assert isinstance(cc, Defaults)
assert cc.routes == {"test": {"test": "test"}}
def test_fields():
fields = Defaults.__fields__
fields = Defaults.model_fields
fields_keys = fields.keys()
assert "routes" in fields_keys

View File

@@ -21,7 +21,7 @@ def test_Metadata():
def test_fields():
"Smoke test"
fields = Metadata.__fields__.keys()
fields = Metadata.model_fields.keys()
assert "arguments" in fields
assert "duration" in fields
assert "route" in fields

View File

@@ -12,7 +12,7 @@ from pandas.testing import assert_frame_equal
def test_OBBject():
"""Smoke test."""
co = OBBject()
co: OBBject = OBBject()
assert isinstance(co, OBBject)
@@ -29,7 +29,7 @@ def test_fields():
def test_to_dataframe_no_results():
"""Test helper."""
co = OBBject()
co: OBBject = OBBject()
with pytest.raises(Exception):
co.to_dataframe()
@@ -214,7 +214,7 @@ class MockDataFrame(Data):
def test_to_dataframe(results, expected_df):
"""Test helper."""
# Arrange
co = OBBject(results=results)
co: OBBject = OBBject(results=results)
# Act and Assert
if isinstance(expected_df, pd.DataFrame):
@@ -253,7 +253,7 @@ def test_to_dataframe(results, expected_df):
def test_to_dataframe_w_args(results, index, sort_by):
"""Test helper."""
# Arrange
co = OBBject(results=results)
co: OBBject = OBBject(results=results)
# Act and Assert
result = co.to_dataframe(index=index, sort_by=sort_by)
@@ -281,7 +281,7 @@ def test_to_dataframe_w_args(results, index, sort_by):
def test_to_df_daylight_savings(results):
"""Test helper."""
# Arrange
co = OBBject(results=results)
co: OBBject = OBBject(results=results)
# Act and Assert
expected_df = basemodel_to_df(results, index="date")
@@ -342,7 +342,7 @@ def test_to_df_daylight_savings(results):
def test_to_dict(results, expected_dict):
"""Test helper."""
# Arrange
co = OBBject(results=results)
co: OBBject = OBBject(results=results)
# Act and Assert
if isinstance(expected_dict, (list, dict)):
@@ -357,7 +357,7 @@ def test_to_dict(results, expected_dict):
def test_show_chart_exists():
"""Test helper."""
mock_instance = OBBject()
mock_instance: OBBject = OBBject()
# Arrange
mock_instance.chart = MagicMock(spec=Chart)
mock_instance.chart.fig = MagicMock()
@@ -372,7 +372,7 @@ def test_show_chart_exists():
def test_show_chart_no_chart():
"""Test helper."""
mock_instance = OBBject()
mock_instance: OBBject = OBBject()
# Act and Assert
with pytest.raises(OpenBBError, match="Chart not found."):
@@ -381,7 +381,7 @@ def test_show_chart_no_chart():
def test_show_chart_no_fig():
"""Test helper."""
mock_instance = OBBject()
mock_instance: OBBject = OBBject()
# Arrange
mock_instance.chart = Chart()

View File

@@ -28,12 +28,12 @@ def test_create_openbb_directory_directory_and_files_not_exist(tmpdir):
)
# Act
SystemSettings.create_openbb_directory(values)
SystemSettings.create_openbb_directory(values) # type: ignore[operator]
# Assert
assert os.path.exists(values.openbb_directory)
assert os.path.exists(values.user_settings_path)
assert os.path.exists(values.system_settings_path)
assert os.path.exists(values.openbb_directory) # type: ignore[attr-defined]
assert os.path.exists(values.user_settings_path) # type: ignore[attr-defined]
assert os.path.exists(values.system_settings_path) # type: ignore[attr-defined]
def test_create_openbb_directory_directory_exists_user_settings_missing(tmpdir):
@@ -47,15 +47,15 @@ def test_create_openbb_directory_directory_exists_user_settings_missing(tmpdir):
)
# Create the openbb directory
Path(values.openbb_directory).mkdir(parents=True, exist_ok=True)
Path(values.openbb_directory).mkdir(parents=True, exist_ok=True) # type: ignore[attr-defined]
# Act
SystemSettings.create_openbb_directory(values)
SystemSettings.create_openbb_directory(values) # type: ignore[operator]
# Assert
assert os.path.exists(values.openbb_directory)
assert os.path.exists(values.user_settings_path)
assert os.path.exists(values.system_settings_path)
assert os.path.exists(values.openbb_directory) # type: ignore[attr-defined]
assert os.path.exists(values.user_settings_path) # type: ignore[attr-defined]
assert os.path.exists(values.system_settings_path) # type: ignore[attr-defined]
def test_create_openbb_directory_directory_exists_system_settings_missing(tmpdir):
@@ -69,19 +69,19 @@ def test_create_openbb_directory_directory_exists_system_settings_missing(tmpdir
)
# Create the openbb directory
Path(values.openbb_directory).mkdir(parents=True, exist_ok=True)
Path(values.openbb_directory).mkdir(parents=True, exist_ok=True) # type: ignore[attr-defined]
# Create the user_settings.json file
with open(values.user_settings_path, "w") as f:
with open(values.user_settings_path, "w") as f: # type: ignore[attr-defined]
f.write("{}")
# Act
SystemSettings.create_openbb_directory(values)
SystemSettings.create_openbb_directory(values) # type: ignore[operator]
# Assert
assert os.path.exists(values.openbb_directory)
assert os.path.exists(values.user_settings_path)
assert os.path.exists(values.system_settings_path)
assert os.path.exists(values.openbb_directory) # type: ignore[attr-defined]
assert os.path.exists(values.user_settings_path) # type: ignore[attr-defined]
assert os.path.exists(values.system_settings_path) # type: ignore[attr-defined]
@pytest.mark.parametrize(
@@ -138,7 +138,7 @@ def test_create_openbb_directory_directory_exists_system_settings_missing(tmpdir
def test_validate_posthog_handler(values, expected_handlers):
values = MockSystemSettings(**values)
# Act
result = SystemSettings.validate_posthog_handler(values)
result = SystemSettings.validate_posthog_handler(values) # type: ignore[operator]
# Assert
assert result.logging_handlers == expected_handlers
@@ -160,7 +160,7 @@ def test_validate_posthog_handler(values, expected_handlers):
def test_validate_logging_handlers(handlers, valid):
# Act and Assert
if valid:
assert SystemSettings.validate_logging_handlers(handlers) == handlers
assert SystemSettings.validate_logging_handlers(handlers) == handlers # type: ignore[call-arg]
else:
with pytest.raises(ValueError, match="Invalid logging handler"):
SystemSettings.validate_logging_handlers(handlers)
SystemSettings.validate_logging_handlers(handlers) # type: ignore[call-arg]

View File

@@ -4,6 +4,7 @@ import json
import tempfile
from pathlib import Path
from openbb_core.app.model.defaults import Defaults
from openbb_core.app.service.user_service import (
UserService,
UserSettings,
@@ -26,9 +27,9 @@ def test_write_default_user_settings():
# Create a UserSettings object with some test data
user_settings = UserSettings()
user_settings.credentials = {"username": "test"}
user_settings.preferences = {"theme": "dark"}
user_settings.defaults = {"language": "en"}
user_settings.credentials = {"username": "test"} # type: ignore[assignment]
user_settings.preferences = {"theme": "dark"} # type: ignore[assignment]
user_settings.defaults = {"language": "en"} # type: ignore[assignment]
# Write the user settings to the temporary file
UserService.write_default_user_settings(user_settings, temp_path)
@@ -50,13 +51,13 @@ def test_update_default():
"""Test update default user settings."""
# Some settings
defaults_test = {"routes": {"test": {"test": "test"}}}
defaults_test = Defaults(routes={"test": {"test": "test"}})
other_settings = UserSettings(defaults=defaults_test)
# Update the default settings
updated_settings = UserService.update_default(other_settings)
assert updated_settings.defaults.model_dump() == defaults_test
assert updated_settings.defaults.model_dump() == defaults_test.model_dump()
def test_merge_dicts():

View File

@@ -10,7 +10,7 @@ from openbb_core.provider.abstract.data import Data
def test_filter_inputs_not_df():
"""Test filter_inputs."""
kwargs = {"num": 1}
kwargs = filter_inputs(**kwargs)
kwargs = filter_inputs(False, None, **kwargs)
assert kwargs["num"] == 1
@@ -19,7 +19,7 @@ def test_filter_inputs_df():
"""Test filter_inputs."""
df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
kwargs = {"data": df}
kwargs = filter_inputs(data_processing=True, **kwargs)
kwargs = filter_inputs(True, None, **kwargs)
assert isinstance(kwargs["data"], list)

View File

@@ -89,7 +89,7 @@ def test_parameters_builder_get_polished_func(input_func, expected_annotations):
polished_func = ParametersBuilder.get_polished_func(input_func)
assert polished_func.__annotations__ == expected_annotations
assert polished_func.__signature__ == input_func.__signature__
assert polished_func.__signature__ == input_func.__signature__ # type: ignore[attr-defined]
@pytest.mark.parametrize(
@@ -206,10 +206,8 @@ def test_parameters_builder_update_command_context(
def test_parameters_builder_update_provider_choices(
command_coverage, route, kwargs, route_default, expected_result
):
with patch(
"openbb_core.app.command_runner.ProviderInterface",
**{"return_value.available_providers": ["provider1", "provider2"]},
):
with patch("openbb_core.app.command_runner.ProviderInterface") as mock_pi:
mock_pi.available_providers = ["provider1", "provider2"]
result = ParametersBuilder.update_provider_choices(
mock_func, command_coverage, route, kwargs, route_default
)
@@ -293,12 +291,11 @@ def test_parameters_builder_build(mock_func, execution_context):
# TODO: add more test cases with @pytest.mark.parametrize
with patch(
"openbb_core.app.command_runner.ProviderInterface",
**{"return_value.available_providers": ["provider1", "provider2"]},
):
with patch("openbb_core.app.command_runner.ProviderInterface") as mock_pi:
mock_pi.available_providers = ["provider1", "provider2"]
result = ParametersBuilder.build(
args=[1, 2],
args=(1, 2),
kwargs={
"c": 3,
"d": "4",

View File

@@ -16,8 +16,10 @@ class DeprecatedCommandsTest(unittest.TestCase):
with self.subTest(i=path):
if getattr(route, "deprecated", False):
deprecation_message = getattr(route, "summary", "")
obb_deprecation_warning = deprecation_message.metadata
if hasattr(deprecation_message, "metadata"):
obb_deprecation_warning = deprecation_message.metadata
assert (
obb_deprecation_warning.expected_removal != current_major_minor
), f"The expected removal version of `{path}` matches the current version, please remove it."
assert (
obb_deprecation_warning.expected_removal
!= current_major_minor
), f"The expected removal version of `{path}` matches the current version, please remove it."

View File

@@ -79,13 +79,15 @@ def test_get_entry_point():
el = ExtensionLoader()
# pylint: disable=protected-access
result = el._get_entry_point(el.provider_entry_points, "fmp")
assert result.group == OpenBBGroups.provider.value
assert result.name == "fmp"
if result:
assert result.group == OpenBBGroups.provider.value
assert result.name == "fmp"
# pylint: disable=protected-access
result = el._get_entry_point(el.core_entry_points, "equity")
assert result.group == OpenBBGroups.core.value
assert result.name == "equity"
if result:
assert result.group == OpenBBGroups.core.value
assert result.name == "equity"
def test_get_entry_point_not_found():
@@ -106,8 +108,9 @@ def test_get_obbject_entry_point(mock_get_entry_point):
el = ExtensionLoader()
result = el.get_obbject_entry_point("mock_extension")
assert result.group == OpenBBGroups.obbject.value
assert result.name == "mock_extension"
if result:
assert result.group == OpenBBGroups.obbject.value
assert result.name == "mock_extension"
@patch("openbb_core.app.extension_loader.ExtensionLoader._get_entry_point")
@@ -120,8 +123,9 @@ def test_get_entry_point_core(mock_get_entry_point):
el = ExtensionLoader()
result = el.get_core_entry_point("mock_extension")
assert result.group == OpenBBGroups.obbject.value
assert result.name == "mock_extension"
if result:
assert result.group == OpenBBGroups.obbject.value
assert result.name == "mock_extension"
@patch("openbb_core.app.extension_loader.ExtensionLoader._get_entry_point")
@@ -134,8 +138,9 @@ def test_get_entry_point_provider(mock_get_entry_point):
el = ExtensionLoader()
result = el.get_provider_entry_point("mock_extension")
assert result.group == OpenBBGroups.obbject.value
assert result.name == "mock_extension"
if result:
assert result.group == OpenBBGroups.obbject.value
assert result.name == "mock_extension"
def test_obbject_objects():

View File

@@ -187,7 +187,7 @@ def test_signature_inspector_init(signature_inspector):
def test_complete_signature(signature_inspector):
"""Test complete_signature."""
async def sample_function(
async def sample_function( # type: ignore[empty-body]
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,

View File

@@ -25,11 +25,11 @@ df = pd.DataFrame(
df_multiindex = df.set_index(["x", "y"])
simple_base_model = [
Data(x=i, y=j, z=k) for i in range(2) for j in range(6, 8) for k in range(10, 12)
Data(x=i, y=j, z=k) for i in range(2) for j in range(6, 8) for k in range(10, 12) # type: ignore[call-arg]
]
multi_index_base_model = [
Data(x=i, y=j, z=k, is_multiindex=True, multiindex_names="['x','y']")
Data(x=i, y=j, z=k, is_multiindex=True, multiindex_names="['x','y']") # type: ignore[call-arg]
for i in range(2)
for j in range(6, 8)
for k in range(10, 12)
@@ -39,7 +39,7 @@ multi_index_base_model = [
def test_df_to_basemodel():
base_model = df_to_basemodel(df)
assert isinstance(base_model, list)
assert base_model[0].x == 1
assert base_model[0].x == 1 # type: ignore[attr-defined]
def test_df_to_basemodel_multiindex():
@@ -93,11 +93,11 @@ def test_list_to_basemodel(data_list, expected):
"data_dict, expected",
[
# Simple dictionary
({"a": 10}, Data(a=10)),
({"a": 10}, Data(a=10)), # type: ignore[call-arg]
# Nested dictionary (assuming Data can handle nested dicts)
({"b": {"c": 20}}, Data(b={"c": 20})),
({"b": {"c": 20}}, Data(b={"c": 20})), # type: ignore[call-arg]
# Dictionary with list (assuming Data can handle lists)
({"d": [30, 40]}, Data(d=[30, 40])),
({"d": [30, 40]}, Data(d=[30, 40])), # type: ignore[call-arg]
],
)
def test_dict_to_basemodel(data_dict, expected):

View File

@@ -14,14 +14,14 @@ def test_check_int_valid():
def test_check_int_invalid():
"""Test if the check_int function raises an error when the value is not an int."""
with pytest.raises(TypeError):
check_int("not_an_integer")
check_int("not_an_integer") # type: ignore[arg-type]
def test_data_model():
"""Test the Data model."""
some_data = Data(test="test")
some_data = Data(test="test") # type: ignore[call-arg]
assert some_data.test == "test"
assert some_data.test == "test" # type: ignore[attr-defined]
assert not some_data.__alias_dict__
assert some_data.__repr__() == "Data(test=test)"
assert some_data.model_dump() == {"test": "test"}
@@ -35,9 +35,9 @@ def test_data_model_alias():
__alias_dict__ = {"test_alias": "test"}
some_data = SomeData(test="Hello")
some_data = SomeData(test="Hello") # type: ignore[call-arg]
assert some_data.__alias_dict__ == {"test_alias": "test"}
assert some_data.__repr__() == "SomeData(test_alias=Hello)"
assert some_data.model_dump() == {"test_alias": "Hello"}
assert some_data.test_alias == "Hello"
assert some_data.test_alias == "Hello" # type: ignore[attr-defined]

View File

@@ -1,5 +1,6 @@
"""Test the Provider."""
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.abstract.provider import Provider
@@ -21,14 +22,14 @@ def test_provider_with_optional_parameters():
description="A simple test provider.",
website="https://testprovider.example.com",
credentials=["api_key"],
fetcher_dict={"fetcher1": None},
fetcher_dict={"fetcher1": Fetcher},
)
assert provider.name == "TestProvider"
assert provider.description == "A simple test provider."
assert provider.website == "https://testprovider.example.com"
assert provider.credentials == ["testprovider_api_key"]
assert provider.fetcher_dict == {"fetcher1": None}
assert provider.fetcher_dict == {"fetcher1": Fetcher}
def test_provider_credentials_formatting():

View File

@@ -5,14 +5,14 @@ from openbb_core.provider.abstract.query_params import QueryParams
def test_query_params_repr():
"""Test the __repr__ method of QueryParams."""
params = QueryParams(param1="value1", param2="value2")
params = QueryParams(param1="value1", param2="value2") # type: ignore[call-arg]
assert "param1='value1'" in str(params)
assert "param2='value2'" in str(params)
def test_query_params_no_alias():
"""Test model_dump without aliases."""
params = QueryParams(param1="value1", param2="value2")
params = QueryParams(param1="value1", param2="value2") # type: ignore[call-arg]
dumped_params = params.model_dump()
assert dumped_params == {"param1": "value1", "param2": "value2"}
@@ -24,7 +24,7 @@ def test_query_params_with_alias():
class AliasedQueryParams(QueryParams):
__alias_dict__ = {"param1": "alias1"}
params = AliasedQueryParams(param1="value1", param2="value2")
params = AliasedQueryParams(param1="value1", param2="value2") # type: ignore[call-arg]
dumped_params = params.model_dump()
assert dumped_params == {"alias1": "value1", "param2": "value2"}

View File

@@ -54,14 +54,14 @@ def test_standard_models(standard_model):
), f"Field {name} should be a ModelField instance"
if "QueryParams" in standard_model.__name__:
if name in QUERY_DESCRIPTIONS:
assert QUERY_DESCRIPTIONS[name] in field.description, (
assert QUERY_DESCRIPTIONS[name] in getattr(field, "description"), (
f"Description for {name} is incorrect for the {standard_model.__name__}.\n"
f"Please modify the description or change the field name to a non-reserved name."
f"To get a full list of reserved descriptions, navigate to openbb_core.provider.utils.descriptions.py"
f"You can also add extra information to the existing reserved field description in your model."
)
elif name in DATA_DESCRIPTIONS:
assert DATA_DESCRIPTIONS[name] in field.description, (
assert DATA_DESCRIPTIONS[name] in getattr(field, "description"), (
f"Description for {name} is incorrect for the {standard_model.__name__}.\n"
f"Please modify the description or change the field name to a non-reserved name."
f"To get a full list of reserved descriptions, navigate to openbb_core.provider.utils.descriptions.py"

View File

@@ -11,14 +11,16 @@ from yarl import URL
def test_obfuscate():
"""Test the obfuscate helper."""
params = {
"api_key": "1234",
"token": "1234",
"auth": "1234",
"auth_token": "1234",
"c": "1234",
"api_key2": "1234",
}
params = CIMultiDict(
{
"api_key": "1234",
"token": "1234",
"auth": "1234",
"auth_token": "1234",
"c": "1234",
"api_key2": "1234",
}
)
assert client.obfuscate(params) == {
"api_key": "********",

View File

@@ -108,7 +108,7 @@ async def test_amake_request(monkeypatch):
)
with pytest.raises(ValueError):
await amake_request("http://mock.url", method="PUT")
await amake_request("http://mock.url", method="PUT") # type: ignore[arg-type]
@pytest.mark.asyncio

View File

@@ -90,4 +90,4 @@ class FMPCompanyNewsFetcher(
) -> List[FMPCompanyNewsData]:
"""Return the transformed data."""
modeled_data = [FMPCompanyNewsData.model_validate(d) for d in data]
return filter_by_dates(modeled_data, query.start_date, query.end_date) # type: ignore
return filter_by_dates(modeled_data, query.start_date, query.end_date)

View File

@@ -79,11 +79,13 @@ class IntrinioCompanyNewsFetcher(
symbol = response.url.parts[-2]
data = await response.json()
return [{**d, "symbol": symbol} for d in data.get("news", [])]
if isinstance(data, dict):
return [{**d, "symbol": symbol} for d in data.get("news", [])]
return []
urls = [
f"{base_url}/{symbol}/news?{query_str}&api_key={api_key}"
for symbol in [s.strip() for s in query.symbol.split(",")]
for symbol in [s.strip() for s in getattr(query, "symbol", "").split(",")]
]
return await amake_requests(urls, callback, **kwargs)
@@ -95,4 +97,4 @@ class IntrinioCompanyNewsFetcher(
) -> List[IntrinioCompanyNewsData]:
"""Return the transformed data."""
modeled_data = [IntrinioCompanyNewsData.model_validate(d) for d in data]
return filter_by_dates(modeled_data, query.start_date, query.end_date) # type: ignore
return filter_by_dates(modeled_data, query.start_date, query.end_date)