Skip to content

Commit 94d0653

Browse files
authored
add more internal docs about base classes (#614)
1 parent fa1582b commit 94d0653

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed

docs/wiki/internals/baseclasses.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Base classes
2+
3+
Base classes are intended to provide data modeling, validation and configuration management by utilizing normal python classes. For example, given the following simple class
4+
5+
```python
6+
class Person:
7+
name = fields.String(default="ahmed")
8+
```
9+
10+
The user should be able to create/store multiple instances of `Person` with valid data.
11+
12+
To achieve this, we implemented base classes using `meta` classes and property descriptors. (a small note, after python `3.7` data classes seems a better option that can be used later).
13+
14+
At first, to explain why we need to implement custom base class/model, we will illustrate the following examples:
15+
16+
If we have the same class called `Person`, with the following definition:
17+
18+
19+
```python
20+
class Person:
21+
name = fields.String(default="ahmed")
22+
```
23+
24+
Accessing the class variable `name` from class or instance level will yield the same value, an instance of `String` field:
25+
26+
```python
27+
Person.name #=> <jumpscale.core.base.fields.String object at 0x7efd89980c18>
28+
29+
p = Person()
30+
p.name #=> <jumpscale.core.base.fields.String object at 0x7efd89980c18>
31+
```
32+
33+
The solution to this problem is using data descriptors (see https://docs.python.org/3/howto/descriptor.html), so the following class:
34+
35+
```python
36+
class Person(Base):
37+
name = fields.String(default="ahmed")
38+
```
39+
40+
Should have different behavior when accessing `name` from a class or an objects, so, it will be converted by the meta class to a new class like:
41+
42+
```python
43+
class Person(Base):
44+
def __init__(self):
45+
self.__name = "ahmed"
46+
47+
@property
48+
def get_name(self):
49+
return self.__name
50+
51+
@property
52+
def set_name(self, value):
53+
self.__name == value
54+
55+
name = property(get_name, set_name)
56+
```
57+
58+
And accessing `name` from class and object levels will yield:
59+
60+
```python
61+
Person.name #=> <property object at 0x7efd89a259f8>
62+
63+
64+
p = Person()
65+
p.name #=> "ahmed"
66+
```
67+
68+
Parent relationship is supported too, every instance can have a parent object (which must be a `Base` type too)
69+
70+
## BaseMeta
71+
72+
This [metaclass](https://docs.python.org/3/reference/datamodel.html#metaclasses) will convert normal classes to a new class with "injected" properties.
73+
74+
Also, this `metaclass` adds all field information inside `_fields` class variable.
75+
76+
77+
```python
78+
def __new__(cls, name: str, based: tuple, attrs: dict) -> type:
79+
"""
80+
get a new class with all field attributes replaced by property data descriptors.
81+
82+
Args:
83+
name (str): class name
84+
based (tuple): super class types (classes)
85+
attrs (dict): current attributes
86+
87+
Returns:
88+
type: a new class
89+
"""
90+
# will collect class fields
91+
cls_fields = {}
92+
...
93+
...
94+
```
95+
96+
See complete implementation at [meta.py](https://github.com/threefoldtech/js-ng/blob/fa1582b83c36a8b18094fd208d04499a8d0f289d/jumpscale/core/base/meta.py#L145)
97+
98+
99+
## Base
100+
101+
This base class uses `BaseMeta` as its meta class, hence we have all information about defined fields/properties.
102+
103+
Then it implements
104+
- Serialization: to_dict/from_dict methods
105+
- Hierarchy: using an optional `parent_`
106+
107+
108+
See full implementation at [meta.py](https://github.com/threefoldtech/js-ng/blob/fa1582b83c36a8b18094fd208d04499a8d0f289d/jumpscale/core/base/meta.py#L200)
109+
110+
Any one who uses this class as base class/model for his type, he will be able to define [custom fields](https://github.com/threefoldtech/js-ng/blob/development/jumpscale/core/base/fields.py) as class variables, then set/get a serializable and validated data.

0 commit comments

Comments
 (0)