Skip to content

Commit 9438db7

Browse files
committed
move files
1 parent 63a04ce commit 9438db7

3 files changed

Lines changed: 146 additions & 96 deletions

File tree

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from __future__ import annotations
22
from abc import ABC
3-
from typing import get_type_hints, get_origin, get_args, TypeVar, Tuple, Type
3+
from typing import get_type_hints, get_origin, get_args, TypeVar, Tuple, Type, Generic
44
from types import UnionType, NoneType
55
from . import io_safe
6-
from . sessions import SessionFileFull, SessionFileKey, SessionFileWhere, SessionDirFull, SessionDirWhere
6+
from .sessions import SessionFileFull, SessionFileKey, SessionFileWhere, SessionDirFull, SessionDirWhere
77

88

99

@@ -12,15 +12,13 @@
1212

1313

1414
def get_type_hints_excluding_internals(cls):
15-
type_hints = {}
15+
"""
16+
Get type hints of the class, excluding double dunder variables.
17+
"""
1618
for var_name, var_type in get_type_hints(cls).items():
1719
if var_name.startswith("__") and var_name.endswith("__"):
1820
continue
19-
type_hints[var_name] = var_type
20-
return type_hints
21-
22-
23-
21+
yield var_name, var_type
2422

2523

2624

@@ -29,26 +27,31 @@ def fill_object_from_dict_using_type_hints(obj, cls, data: dict):
2927
Attributes of obj are set using the data dict.
3028
The type hints of the class cls are used to determine which attributes to set.
3129
"""
32-
for var_name, var_type in get_type_hints_excluding_internals(cls).items():
30+
for var_name, var_type in get_type_hints_excluding_internals(cls):
31+
var_type_args = get_args(var_type)
32+
var_type_origin = get_origin(var_type)
3333
# Check if variable is nullable (e.g. email: str | None)
34-
nullable = get_origin(var_type) is UnionType and NoneType in get_args(var_type)
3534
# When it is not nullable but not in the data, raise an error
3635
if var_name not in data:
37-
print(var_name, get_origin(var_type), get_args(var_type))
36+
nullable = var_type_origin is UnionType and NoneType in var_type_args
3837
if not nullable:
3938
raise RuntimeError(f"Missing variable '{var_name}' in {cls.__name__}.")
40-
else:
41-
continue
39+
continue
4240
# When it is a list, fill the list with the items
43-
if get_origin(var_type) is list and len(arg := get_args(var_type)) == 1:
44-
item_type = arg[0]
41+
if var_type_origin is list and len(var_type_args) == 1:
42+
item_type = var_type_args[0]
4543
setattr(obj, var_name, [item_type.from_dict(x) for x in data[var_name]])
4644
else:
4745
setattr(obj, var_name, data.get(var_name, None))
4846
return obj
4947

5048

5149

50+
def fill_dict_from_object_using_type_hints(cls, obj):
51+
raise NotImplementedError
52+
53+
54+
5255

5356

5457
########################################################################################
@@ -58,22 +61,24 @@ def fill_object_from_dict_using_type_hints(obj, cls, data: dict):
5861

5962

6063

61-
class FileDictModel(ABC):
64+
class FileDictModel(ABC, Generic[T]):
6265
"""
6366
A file base refers to a file that is stored in the database.
6467
At the top level the file must contain a dictionary with strings as keys.
6568
"""
6669

6770
__file__ = None
68-
__item_model__: T = None
71+
__item_model__: Type[T]
6972

7073
@classmethod
7174
def get_at_key(cls, key) -> T:
7275
"""
7376
Gets an item by key.
7477
The data is partially read from the __file__.
7578
"""
76-
return cls.__item_model__.partial_read_by_key(key)
79+
data = io_safe.partial_read(cls.__file__, key)
80+
res: T = cls.__item_model__.from_key_value(key, data)
81+
return res
7782

7883
@classmethod
7984
def session_at_key(cls, key):
@@ -85,7 +90,7 @@ def items(cls) -> list[Tuple[str, T]]:
8590
Gets all items as a list of tuples (key, ORM model of value).
8691
"""
8792
data = io_safe.read(cls.__file__)
88-
return [(k, cls.__item_model__(k, v)) for k, v in data.items()]
93+
return [(k, cls.__item_model__.from_key_value(k, v)) for k, v in data.items()]
8994

9095
@classmethod
9196
def session(cls):
@@ -109,8 +114,6 @@ def get_where(cls, where: callable) -> list[Tuple[str, T]]:
109114
"""
110115
return [(k, v) for k, v in cls.items() if where(v)]
111116

112-
# Not Implemented:
113-
# - select by filter callback (file_where): Not implemented because no performance advantage.
114117

115118

116119

@@ -123,11 +126,6 @@ def from_key_value(cls: Type[T2], key, value) -> T2:
123126
obj.__key__ = key
124127
return obj
125128

126-
@classmethod
127-
def read_by_key(cls, key) -> T:
128-
data = io_safe.partial_read(cls.__file__, key)
129-
return cls.from_key_value(key, data)
130-
131129
@classmethod
132130
def session(cls, key):
133131
def partial_func(x):
@@ -152,7 +150,6 @@ def to_dict(self) -> dict:
152150

153151

154152

155-
156153
########################################################################################
157154
# Scenario 2:
158155
# Add in a later version of DDB

test_orm.py

Lines changed: 0 additions & 69 deletions
This file was deleted.

testing_orm.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from dictdatabase.object_mapper import DictModel, FileDictItemModel, FileDictModel
2+
import dictdatabase as DDB
3+
4+
5+
class WorkTime(DictModel):
6+
start: str
7+
end: str
8+
9+
10+
class User(FileDictItemModel):
11+
first_name: str
12+
last_name: str
13+
email: str | None
14+
15+
work_times: list[WorkTime]
16+
17+
18+
def full_name(self):
19+
return f"{self.first_name} {self.last_name}"
20+
21+
22+
class Users(FileDictModel[User]):
23+
__file__ = "users"
24+
__item_model__ = User
25+
26+
27+
28+
u = User.from_key_value("uid1", {
29+
"first_name": "John",
30+
"last_name": "Doe",
31+
"none": "no",
32+
"work_times": [
33+
{"start": "08:00", "end": "12:00"},
34+
{"start": "13:00", "end": "17:00"},
35+
]
36+
})
37+
38+
39+
assert u.first_name == "John"
40+
assert u.last_name == "Doe"
41+
assert u.full_name() == "John Doe"
42+
assert u.work_times[0].start == "08:00"
43+
assert u.work_times[0].end == "12:00"
44+
assert u.work_times[1].start == "13:00"
45+
assert u.work_times[1].end == "17:00"
46+
assert len(u.work_times) == 2
47+
48+
print("check")
49+
print(u)
50+
51+
52+
53+
54+
DDB.at("users").create({
55+
"uid1": {
56+
"first_name": "John",
57+
"last_name": "Doe",
58+
"none": "no",
59+
"work_times": [
60+
{"start": "08:00", "end": "12:00"},
61+
{"start": "13:00", "end": "17:00"},
62+
]
63+
},
64+
"uid2": {
65+
"first_name": "Jane",
66+
"last_name": "Smith",
67+
"none": "no",
68+
"work_times": [
69+
{"start": "08:00", "end": "12:00"},
70+
{"start": "13:00", "end": "17:00"},
71+
]
72+
},
73+
"uid3": {
74+
"first_name": "Pete",
75+
"last_name": "Griffin",
76+
"none": "no",
77+
"work_times": [
78+
{"start": "08:00", "end": "12:00"},
79+
{"start": "13:00", "end": "17:00"},
80+
]
81+
}
82+
}, force_overwrite=True)
83+
84+
85+
u1 = Users.get_at_key("uid1")
86+
assert u1.first_name == "John"
87+
assert u1.last_name == "Doe"
88+
assert u1.full_name() == "John Doe"
89+
assert u1.work_times[0].start == "08:00"
90+
assert u1.work_times[0].end == "12:00"
91+
assert u1.work_times[1].start == "13:00"
92+
assert u1.work_times[1].end == "17:00"
93+
assert len(u1.work_times) == 2
94+
95+
96+
97+
u2 = Users.get_at_key("uid2")
98+
99+
100+
101+
for uid, u in Users.items():
102+
print(u.full_name())
103+
104+
105+
106+
# # Iterate FileDictModel
107+
# for user_id, user in Users.items():
108+
# print(user_id, user.first_name, user.last_name, user.email)
109+
110+
# # Get one item
111+
# user: User = Users.get_at_key("user_id")
112+
113+
114+
# # Get by lambda
115+
# users: Users = Users.where(lambda user: user.first_name != "John")
116+
117+
118+
# with Users.session_at_key(user_id) as (session, user):
119+
# ...
120+
121+
# with Users.session() as (session, users): Dict[str, User]
122+
# ...

0 commit comments

Comments
 (0)