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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:
- name: Check out code
uses: actions/checkout@v2

- name: Set up Python 3.9
- name: Set up Python 3.7
uses: actions/setup-python@v2
with:
python-version: 3.9
python-version: 3.7

- name: Install dependencies
run: |
Expand Down
4 changes: 3 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ classifiers =
Operating System :: OS Independent
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
project_urls =
Bug Tracker = https://github.com/firebolt-db/firebolt-sdk/issues
Expand All @@ -25,7 +27,7 @@ install_requires =
httpx[http2]==0.19.0
pydantic[dotenv]==1.8.2
readerwriterlock==1.0.9
python_requires = >=3.9
python_requires = >=3.7
package_dir =
= src

Expand Down
7 changes: 4 additions & 3 deletions src/firebolt/client/client.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import time
import typing
from functools import cached_property, wraps
from functools import wraps
from inspect import cleandoc
from json import JSONDecodeError
from typing import Any, Optional, Tuple
from typing import Any, Optional, Tuple, Type

import httpx
from httpx._types import AuthTypes

from firebolt.common.exception import AuthenticationError
from firebolt.common.utils import cached_property

DEFAULT_API_URL: str = "api.app.firebolt.io"
API_REQUEST_TIMEOUT_SECONDS: Optional[int] = 60
_REQUEST_ERRORS: Tuple[type[Exception], ...] = (
_REQUEST_ERRORS: Tuple[Type, ...] = (
httpx.HTTPError,
httpx.InvalidURL,
httpx.CookieConflict,
Expand Down
8 changes: 8 additions & 0 deletions src/firebolt/common/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from functools import lru_cache
from typing import Callable, TypeVar

T = TypeVar("T")


def cached_property(func: Callable[..., T]) -> T:
return property(lru_cache()(func)) # type: ignore
8 changes: 5 additions & 3 deletions src/firebolt/db/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

from datetime import date, datetime
from enum import Enum
from functools import cached_property
from typing import Union, get_args
from typing import Union

from ciso8601 import parse_datetime

from firebolt.common.exception import DataError, NotSupportedError
from firebolt.common.utils import cached_property

_NoneType = type(None)
_col_types = (int, float, str, datetime, date, bool, list, _NoneType)
# duplicating this since 3.7 can't unpack Union
ColType = Union[int, float, str, datetime, date, bool, list, _NoneType]
RawColType = Union[int, float, str, bool, list, _NoneType]

Expand Down Expand Up @@ -53,7 +55,7 @@ class ARRAY:
_prefix = "Array("

def __init__(self, subtype: Union[type, ARRAY]):
assert (subtype in get_args(ColType) and subtype is not list) or isinstance(
assert (subtype in _col_types and subtype is not list) or isinstance(
subtype, ARRAY
), f"Invalid array subtype: {str(subtype)}"
self.subtype = subtype
Expand Down
10 changes: 5 additions & 5 deletions src/firebolt/model/database.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING, Annotated, Any, Optional
from typing import TYPE_CHECKING, Any, List, Optional

from pydantic import Field, PrivateAttr

Expand Down Expand Up @@ -33,13 +33,13 @@ class Database(FireboltBaseModel):
_service: DatabaseService = PrivateAttr()

# required
name: Annotated[str, Field(min_length=1, max_length=255, regex=r"^[0-9a-zA-Z_]+$")]
name: str = Field(min_length=1, max_length=255, regex=r"^[0-9a-zA-Z_]+$")
compute_region_key: RegionKey = Field(alias="compute_region_id")

# optional
database_key: Optional[DatabaseKey] = Field(alias="id")
description: Optional[Annotated[str, Field(max_length=255)]]
emoji: Optional[Annotated[str, Field(max_length=255)]]
description: Optional[str] = Field(max_length=255)
emoji: Optional[str] = Field(max_length=255)
current_status: Optional[str]
health_status: Optional[str]
data_size_full: Optional[int]
Expand All @@ -66,7 +66,7 @@ def database_id(self) -> Optional[str]:
return None
return self.database_key.database_id

def get_attached_engines(self) -> list[Engine]:
def get_attached_engines(self) -> List[Engine]:
"""Get a list of engines that are attached to this database."""
return self._service.resource_manager.bindings.get_engines_bound_to_database( # noqa: E501
database=self
Expand Down
6 changes: 3 additions & 3 deletions src/firebolt/model/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import logging
import time
from datetime import datetime
from typing import TYPE_CHECKING, Annotated, Any, Callable, Optional
from typing import TYPE_CHECKING, Any, Callable, Optional

from pydantic import Field, PrivateAttr

Expand Down Expand Up @@ -40,7 +40,7 @@ class EngineSettings(FireboltBaseModel):
"""

preset: str
auto_stop_delay_duration: Annotated[str, Field(regex=r"^[0-9]+[sm]$|^0$")]
auto_stop_delay_duration: str = Field(regex=r"^[0-9]+[sm]$|^0$")
minimum_logging_level: str
is_read_only: bool
warm_up: str
Expand Down Expand Up @@ -91,7 +91,7 @@ class Engine(FireboltBaseModel):
_service: EngineService = PrivateAttr()

# required
name: Annotated[str, Field(min_length=1, max_length=255, regex=r"^[0-9a-zA-Z_]+$")]
name: str = Field(min_length=1, max_length=255, regex=r"^[0-9a-zA-Z_]+$")
compute_region_key: RegionKey = Field(alias="compute_region_id")
settings: EngineSettings

Expand Down
6 changes: 3 additions & 3 deletions src/firebolt/service/binding.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional
from typing import List, Optional

from firebolt.common.exception import AlreadyBoundError
from firebolt.common.util import prune_dict
Expand All @@ -24,7 +24,7 @@ def get_many(
database_id: Optional[str] = None,
engine_id: Optional[str] = None,
is_system_database: Optional[bool] = None,
) -> list[Binding]:
) -> List[Binding]:
"""
List bindings on Firebolt, optionally filtering by database and engine.

Expand Down Expand Up @@ -64,7 +64,7 @@ def get_database_bound_to_engine(self, engine: Engine) -> Optional[Database]:
except IndexError:
return None

def get_engines_bound_to_database(self, database: Database) -> list[Engine]:
def get_engines_bound_to_database(self, database: Database) -> List[Engine]:
"""Get a list of engines that are bound to a database."""
bindings = self.get_many(database_id=database.database_id)
return self.resource_manager.engines.get_by_ids(
Expand Down
4 changes: 2 additions & 2 deletions src/firebolt/service/database.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Union
from typing import List, Optional, Union

from firebolt.model import FireboltBaseModel
from firebolt.model.database import Database
Expand Down Expand Up @@ -36,7 +36,7 @@ def get_many(
attached_engine_name_eq: str,
attached_engine_name_contains: str,
order_by: Union[str, DatabaseOrder],
) -> list[Database]:
) -> List[Database]:
"""
Get a list of databases on Firebolt.

Expand Down
14 changes: 7 additions & 7 deletions src/firebolt/service/engine.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Optional, Union
from logging import getLogger
from typing import List, Optional, Union

from firebolt.common.util import prune_dict
from firebolt.model import FireboltBaseModel
Expand All @@ -12,7 +12,7 @@
from firebolt.service.base import BaseService
from firebolt.service.types import EngineOrder, EngineType, WarmupMethod

logger = logging.getLogger(__name__)
logger = getLogger(__name__)


class EngineService(BaseService):
Expand All @@ -24,10 +24,10 @@ def get(self, id_: str) -> Engine:
engine_entry: dict = response.json()["engine"]
return Engine.parse_obj_with_service(obj=engine_entry, engine_service=self)

def get_by_ids(self, ids: list[str]) -> list[Engine]:
def get_by_ids(self, ids: List[str]) -> List[Engine]:
"""Get multiple Engines from Firebolt by their ids."""
response = self.client.post(
url=f"/core/v1/engines:getByIds",
url="/core/v1/engines:getByIds",
json={
"engine_ids": [
{"account_id": self.account_id, "engine_id": engine_id}
Expand Down Expand Up @@ -56,7 +56,7 @@ def get_many(
current_status_not_eq: str,
region_eq: str,
order_by: Union[str, EngineOrder],
) -> list[Engine]:
) -> List[Engine]:
"""
Get a list of engines on Firebolt.

Expand All @@ -73,7 +73,7 @@ def get_many(
if isinstance(order_by, str):
order_by = EngineOrder[order_by]
response = self.client.get(
url=f"/core/v1/account/engines",
url="/core/v1/account/engines",
params=prune_dict(
{
"page.first": 5000, # FUTURE: pagination support w/ generator
Expand Down
10 changes: 5 additions & 5 deletions src/firebolt/service/instance_type.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from functools import cached_property
from typing import NamedTuple, Optional
from typing import Dict, List, NamedTuple, Optional

from firebolt.common.utils import cached_property
from firebolt.model.instance_type import InstanceType, InstanceTypeKey
from firebolt.service.base import BaseService

Expand All @@ -14,20 +14,20 @@ class InstanceTypeLookup(NamedTuple):

class InstanceTypeService(BaseService):
@cached_property
def instance_types(self) -> list[InstanceType]:
def instance_types(self) -> List[InstanceType]:
"""List of instance types available on Firebolt."""
response = self.client.get(
url="/compute/v1/instanceTypes", params={"page.first": 5000}
)
return [InstanceType.parse_obj(i["node"]) for i in response.json()["edges"]]

@cached_property
def instance_types_by_key(self) -> dict[InstanceTypeKey, InstanceType]:
def instance_types_by_key(self) -> Dict[InstanceTypeKey, InstanceType]:
"""Dict of {InstanceTypeKey: InstanceType}"""
return {i.key: i for i in self.instance_types}

@cached_property
def instance_types_by_name(self) -> dict[InstanceTypeLookup, InstanceType]:
def instance_types_by_name(self) -> Dict[InstanceTypeLookup, InstanceType]:
"""Dict of {InstanceTypeLookup: InstanceType}"""
return {
InstanceTypeLookup(
Expand Down
9 changes: 5 additions & 4 deletions src/firebolt/service/region.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from functools import cached_property
from typing import Dict, List

from firebolt.common.utils import cached_property
from firebolt.model.region import Region, RegionKey
from firebolt.service.base import BaseService
from firebolt.service.manager import ResourceManager
Expand All @@ -20,20 +21,20 @@ def __init__(
super().__init__(resource_manager=resource_manager)

@cached_property
def regions(self) -> list[Region]:
def regions(self) -> List[Region]:
"""List of available AWS Regions on Firebolt."""
response = self.client.get(
url="/compute/v1/regions", params={"page.first": 5000}
)
return [Region.parse_obj(i["node"]) for i in response.json()["edges"]]

@cached_property
def regions_by_name(self) -> dict[str, Region]:
def regions_by_name(self) -> Dict[str, Region]:
"""Dict of {RegionLookup: Region}"""
return {r.name: r for r in self.regions}

@cached_property
def regions_by_key(self) -> dict[RegionKey, Region]:
def regions_by_key(self) -> Dict[RegionKey, Region]:
"""Dict of {RegionKey: Region}"""
return {r.key: r for r in self.regions}

Expand Down
8 changes: 4 additions & 4 deletions tests/unit/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Callable
from typing import Callable, List

import httpx
import pytest
Expand Down Expand Up @@ -37,7 +37,7 @@ def provider() -> Provider:


@pytest.fixture
def mock_providers(provider) -> list[Provider]:
def mock_providers(provider) -> List[Provider]:
return [provider]


Expand All @@ -64,7 +64,7 @@ def region_2(provider) -> Region:


@pytest.fixture
def mock_regions(region_1, region_2) -> list[Region]:
def mock_regions(region_1, region_2) -> List[Region]:
return [region_1, region_2]


Expand Down Expand Up @@ -93,7 +93,7 @@ def instance_type_2(provider, region_2) -> InstanceType:


@pytest.fixture
def mock_instance_types(instance_type_1, instance_type_2) -> list[InstanceType]:
def mock_instance_types(instance_type_1, instance_type_2) -> List[InstanceType]:
return [instance_type_1, instance_type_2]


Expand Down
4 changes: 3 additions & 1 deletion tests/unit/util.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from typing import Dict, List

from firebolt.model import FireboltBaseModel


def list_to_paginated_response(items: list[FireboltBaseModel]) -> dict:
def list_to_paginated_response(items: List[FireboltBaseModel]) -> Dict:
return {"edges": [{"node": i.dict()} for i in items]}