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
3 changes: 1 addition & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
pydantic[dotenv]
httpx[http2]
aiorwlock
async-property
readerwriterlock
sqlparse
appdirs
appdirs-stubs
cryptography
cryptography
7 changes: 1 addition & 6 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ install_requires =
aiorwlock==1.1.0
appdirs>=1.4.4
appdirs-stubs>=0.1.0
async-generator>=1.10
async-property>=0.2.1
cryptography>=3.4.0
httpx[http2]==0.24.0
pydantic[dotenv]>=1.8.2
python-dateutil>=2.8.2
readerwriterlock>=1.0.9
sqlparse>=0.4.2
Expand Down Expand Up @@ -66,15 +66,10 @@ dev =
firebolt = py.typed

[mypy]
plugins = pydantic.mypy
disallow_untyped_defs = True
show_error_codes = True
files = src/

[pydantic-mypy]
warn_required_dynamic_aliases = True
warn_untyped_fields = True

[flake8]
exclude = tests/*
max-line-length = 88
Expand Down
21 changes: 17 additions & 4 deletions src/firebolt/common/base_cursor.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from __future__ import annotations

import logging
from dataclasses import dataclass, fields
from enum import Enum
from functools import wraps
from types import TracebackType
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union

from httpx import Response
from pydantic import BaseModel

from firebolt.common._types import (
ColType,
Expand Down Expand Up @@ -51,7 +51,8 @@ class QueryStatus(Enum):
EXECUTION_ERROR = 8


class Statistics(BaseModel):
@dataclass
class Statistics:
"""
Class for query execution statistics.
"""
Expand All @@ -61,8 +62,20 @@ class Statistics(BaseModel):
bytes_read: int
time_before_execution: float
time_to_execute: float
scanned_bytes_cache: Optional[float]
scanned_bytes_storage: Optional[float]
scanned_bytes_cache: Optional[float] = None
scanned_bytes_storage: Optional[float] = None

def __post_init__(self) -> None:
for field in fields(self):
value = getattr(self, field.name)
_type = eval(field.type) # type: ignore

# Unpack Optional
if hasattr(_type, "__args__"):
_type = _type.__args__[0]
if value is not None and not isinstance(value, _type):
# convert values to proper types
setattr(self, field.name, _type(value))


def check_not_closed(func: Callable) -> Callable:
Expand Down
39 changes: 22 additions & 17 deletions src/firebolt/model/__init__.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import json
from typing import Any
from dataclasses import dataclass, field, fields
from typing import ClassVar, Dict, Optional, Type, TypeVar

from pydantic import BaseModel
from firebolt.service.base import BaseService

Model = TypeVar("Model", bound="FireboltBaseModel")

class FireboltBaseModel(BaseModel):
class Config:
allow_population_by_field_name = True
extra = "forbid"

def jsonable_dict(self, *args: Any, **kwargs: Any) -> dict:
"""
Generate a dictionary representation of the service that contains serialized
primitive types, and is therefore JSON-ready.
@dataclass
class FireboltBaseModel:
_service: BaseService = field(repr=False, compare=False)

This could be replaced with something native once this issue is resolved:
https://github.com/samuelcolvin/pydantic/issues/1409
@classmethod
def _get_field_overrides(cls) -> Dict[str, str]:
"""Create a mapping of db field name to class name where they are different."""
return {
f.metadata["db_name"]: f.name
for f in fields(cls)
if "db_name" in f.metadata
}

This function is intended to improve the compatibility with HTTPX, which
expects to take in a dictionary of primitives as input to the JSON parameter
of its request function. See: https://www.python-httpx.org/api/#helper-functions
"""
return json.loads(self.json(*args, **kwargs))
@classmethod
def _from_dict(
cls: Type[Model], data: dict, service: Optional[BaseService] = None
) -> Model:
data["_service"] = service
field_name_overrides = cls._get_field_overrides()
return cls(**{field_name_overrides.get(k, k): v for k, v in data.items()})
38 changes: 0 additions & 38 deletions src/firebolt/model/binding.py

This file was deleted.

Loading