excel-orm 0.1.3__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {excel_orm-0.1.3 → excel_orm-0.1.5}/PKG-INFO +1 -1
- {excel_orm-0.1.3 → excel_orm-0.1.5}/pyproject.toml +1 -1
- {excel_orm-0.1.3 → excel_orm-0.1.5}/src/excel_orm/__init__.py +10 -1
- excel_orm-0.1.5/src/excel_orm/base.py +21 -0
- {excel_orm-0.1.3 → excel_orm-0.1.5}/src/excel_orm/column.py +6 -6
- {excel_orm-0.1.3 → excel_orm-0.1.5}/tests/conftest.py +4 -3
- {excel_orm-0.1.3 → excel_orm-0.1.5}/tests/test_columns.py +12 -4
- {excel_orm-0.1.3 → excel_orm-0.1.5}/tests/test_orm.py +2 -2
- {excel_orm-0.1.3 → excel_orm-0.1.5}/tests/test_orm_helpers.py +3 -2
- {excel_orm-0.1.3 → excel_orm-0.1.5}/uv.lock +1 -1
- {excel_orm-0.1.3 → excel_orm-0.1.5}/.gitignore +0 -0
- {excel_orm-0.1.3 → excel_orm-0.1.5}/.pre-commit-config.yaml +0 -0
- {excel_orm-0.1.3 → excel_orm-0.1.5}/.python-version +0 -0
- {excel_orm-0.1.3 → excel_orm-0.1.5}/README.md +0 -0
- {excel_orm-0.1.3 → excel_orm-0.1.5}/src/excel_orm/orm.py +0 -0
- {excel_orm-0.1.3 → excel_orm-0.1.5}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: excel-orm
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A lightweight Excel ORM for generating templates and parsing typed row models.
|
|
5
5
|
Project-URL: Homepage, https://github.com/acdelrusso/excel-orm
|
|
6
6
|
Project-URL: Repository, https://github.com/acdelrusso/excel-orm
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
from .
|
|
1
|
+
from .base import RowBase
|
|
2
|
+
from .column import (
|
|
3
|
+
Column,
|
|
4
|
+
ColumnSpec,
|
|
5
|
+
bool_column,
|
|
6
|
+
date_column,
|
|
7
|
+
int_column,
|
|
8
|
+
text_column,
|
|
9
|
+
)
|
|
2
10
|
from .orm import ExcelFile, PivotSheetSpec, SheetSpec
|
|
3
11
|
|
|
4
12
|
__all__ = [
|
|
@@ -6,6 +14,7 @@ __all__ = [
|
|
|
6
14
|
"ColumnSpec",
|
|
7
15
|
"ExcelFile",
|
|
8
16
|
"PivotSheetSpec",
|
|
17
|
+
"RowBase",
|
|
9
18
|
"SheetSpec",
|
|
10
19
|
"bool_column",
|
|
11
20
|
"date_column",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from excel_orm import Column
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RowBase:
|
|
10
|
+
__columns__: list[Column[Any]]
|
|
11
|
+
|
|
12
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
13
|
+
self._values: dict[str, Any] = {}
|
|
14
|
+
|
|
15
|
+
for col in getattr(self, "__columns__", []):
|
|
16
|
+
if col.name is None:
|
|
17
|
+
continue
|
|
18
|
+
self._values[col.name] = col.spec.default
|
|
19
|
+
|
|
20
|
+
for k, v in kwargs.items():
|
|
21
|
+
setattr(self, k, v)
|
|
@@ -41,9 +41,9 @@ class Column[T]:
|
|
|
41
41
|
@overload
|
|
42
42
|
def __get__(self, obj: None, objtype: type[Owner] | None = None) -> Column[T]: ...
|
|
43
43
|
@overload
|
|
44
|
-
def __get__(self, obj: Owner, objtype: type[Owner] | None = None) -> T
|
|
44
|
+
def __get__(self, obj: Owner, objtype: type[Owner] | None = None) -> T: ...
|
|
45
45
|
|
|
46
|
-
def __get__(self, obj: Owner | None, objtype: type[Owner] | None = None) -> T | Column
|
|
46
|
+
def __get__(self, obj: Owner | None, objtype: type[Owner] | None = None) -> T | Column:
|
|
47
47
|
if obj is None:
|
|
48
48
|
return self
|
|
49
49
|
if self.name is None:
|
|
@@ -71,7 +71,7 @@ def text_column(
|
|
|
71
71
|
default: str | None = None,
|
|
72
72
|
strip: bool = True,
|
|
73
73
|
not_null: bool = False,
|
|
74
|
-
):
|
|
74
|
+
) -> Column[str]:
|
|
75
75
|
def parse(raw: Any) -> str:
|
|
76
76
|
if raw is None:
|
|
77
77
|
return ""
|
|
@@ -96,7 +96,7 @@ def int_column(
|
|
|
96
96
|
*,
|
|
97
97
|
default: int | None = None,
|
|
98
98
|
not_null: bool = False,
|
|
99
|
-
):
|
|
99
|
+
) -> Column[int]:
|
|
100
100
|
def parse(raw: Any) -> int:
|
|
101
101
|
if raw is None or raw == "":
|
|
102
102
|
return 0
|
|
@@ -112,7 +112,7 @@ def int_column(
|
|
|
112
112
|
)
|
|
113
113
|
|
|
114
114
|
|
|
115
|
-
def bool_column(header: str | None = None, *, default: bool | None = None):
|
|
115
|
+
def bool_column(header: str | None = None, *, default: bool | None = None) -> Column[bool]:
|
|
116
116
|
def parse(raw: Any) -> bool:
|
|
117
117
|
if raw is None or raw == "":
|
|
118
118
|
return False
|
|
@@ -134,7 +134,7 @@ def bool_column(header: str | None = None, *, default: bool | None = None):
|
|
|
134
134
|
)
|
|
135
135
|
|
|
136
136
|
|
|
137
|
-
def date_column(header: str | None = None, *, default: date | None = None):
|
|
137
|
+
def date_column(header: str | None = None, *, default: date | None = None) -> Column[date]:
|
|
138
138
|
_DATE_FORMATS: tuple[str, ...] = (
|
|
139
139
|
"%d-%b-%Y", # 01-JUN-2025 (your requirement)
|
|
140
140
|
"%d-%b-%y", # 01-JUN-25
|
|
@@ -2,6 +2,7 @@ from datetime import date
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
+
from excel_orm.base import RowBase
|
|
5
6
|
from src.excel_orm import (
|
|
6
7
|
Column,
|
|
7
8
|
ExcelFile,
|
|
@@ -15,12 +16,12 @@ from src.excel_orm import (
|
|
|
15
16
|
|
|
16
17
|
@pytest.fixture
|
|
17
18
|
def models():
|
|
18
|
-
class Car:
|
|
19
|
+
class Car(RowBase):
|
|
19
20
|
make: Column[str] = text_column(header="Make", not_null=True)
|
|
20
21
|
model: Column[str] = text_column(header="Model", not_null=True)
|
|
21
22
|
year: Column[int] = int_column(header="Year", not_null=True)
|
|
22
23
|
|
|
23
|
-
class ManufacturingPlant:
|
|
24
|
+
class ManufacturingPlant(RowBase):
|
|
24
25
|
name: Column[str] = text_column(header="Factory Name", not_null=True)
|
|
25
26
|
location: Column[str] = text_column(header="Location")
|
|
26
27
|
|
|
@@ -53,7 +54,7 @@ def demand_model():
|
|
|
53
54
|
|
|
54
55
|
@pytest.fixture
|
|
55
56
|
def demand_two_pivot():
|
|
56
|
-
class Demand:
|
|
57
|
+
class Demand(RowBase):
|
|
57
58
|
dt: Column[date] = date_column(header="Date")
|
|
58
59
|
region: Column[str] = text_column(header="Region", not_null=True)
|
|
59
60
|
product: Column[str] = text_column(header="Product", not_null=True)
|
|
@@ -4,11 +4,18 @@ from datetime import date, datetime
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from src.excel_orm import
|
|
7
|
+
from src.excel_orm import (
|
|
8
|
+
Column,
|
|
9
|
+
RowBase,
|
|
10
|
+
bool_column,
|
|
11
|
+
date_column,
|
|
12
|
+
int_column,
|
|
13
|
+
text_column,
|
|
14
|
+
)
|
|
8
15
|
|
|
9
16
|
|
|
10
17
|
def test_descriptor_stores_in_values_dict():
|
|
11
|
-
class Foo:
|
|
18
|
+
class Foo(RowBase):
|
|
12
19
|
a: Column[str] = text_column(header="A")
|
|
13
20
|
|
|
14
21
|
f = Foo()
|
|
@@ -20,14 +27,14 @@ def test_descriptor_stores_in_values_dict():
|
|
|
20
27
|
|
|
21
28
|
|
|
22
29
|
def test_descriptor_class_level_access_returns_column():
|
|
23
|
-
class Foo:
|
|
30
|
+
class Foo(RowBase):
|
|
24
31
|
a: Column[str] = text_column(header="A")
|
|
25
32
|
|
|
26
33
|
assert isinstance(Foo.a, Column)
|
|
27
34
|
|
|
28
35
|
|
|
29
36
|
def test_not_null_validation_raises_on_none_or_empty():
|
|
30
|
-
class Foo:
|
|
37
|
+
class Foo(RowBase):
|
|
31
38
|
a: Column[str] = text_column(header="A", not_null=True)
|
|
32
39
|
|
|
33
40
|
f = Foo()
|
|
@@ -108,3 +115,4 @@ def test_date_column_invalid_raises():
|
|
|
108
115
|
col = date_column(header="D")
|
|
109
116
|
with pytest.raises(ValueError):
|
|
110
117
|
col.parse_cell("not-a-date")
|
|
118
|
+
col.parse_cell(" ")
|
|
@@ -5,7 +5,7 @@ from datetime import date, datetime
|
|
|
5
5
|
import pytest
|
|
6
6
|
from openpyxl import load_workbook
|
|
7
7
|
|
|
8
|
-
from excel_orm import Column, ExcelFile, PivotSheetSpec, text_column
|
|
8
|
+
from excel_orm import Column, ExcelFile, PivotSheetSpec, RowBase, text_column
|
|
9
9
|
from excel_orm.orm import SheetSpec
|
|
10
10
|
|
|
11
11
|
|
|
@@ -311,7 +311,7 @@ def test_pivot_two_row_fields_template_and_parse_end_to_end(tmp_path, demand_two
|
|
|
311
311
|
def test_duplicate_header_names_do_not_clash(tmp_path, models):
|
|
312
312
|
Car, _ = models
|
|
313
313
|
|
|
314
|
-
class ManufacturingPlant:
|
|
314
|
+
class ManufacturingPlant(RowBase):
|
|
315
315
|
name: Column[str] = text_column(header="Make", not_null=True)
|
|
316
316
|
location: Column[str] = text_column(header="location")
|
|
317
317
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from excel_orm import RowBase
|
|
3
4
|
from src.excel_orm import Column, int_column, text_column
|
|
4
5
|
from src.excel_orm.orm import (
|
|
5
6
|
_camel_to_snake,
|
|
@@ -31,7 +32,7 @@ def test_repo_and_display_name():
|
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
def test_get_model_columns_respects_annotation_order():
|
|
34
|
-
class A:
|
|
35
|
+
class A(RowBase):
|
|
35
36
|
x: Column[str] = text_column(header="X")
|
|
36
37
|
y: Column[int] = int_column(header="Y")
|
|
37
38
|
|
|
@@ -40,7 +41,7 @@ def test_get_model_columns_respects_annotation_order():
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
def test_instantiate_model_sets_defaults_and_requires_set_name():
|
|
43
|
-
class A:
|
|
44
|
+
class A(RowBase):
|
|
44
45
|
x: Column[str] = text_column(header="X", default="dflt")
|
|
45
46
|
|
|
46
47
|
a = _instantiate_model(A)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|