wexample-orm 0.0.6__tar.gz → 1.0.1__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.
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/PKG-INFO +8 -6
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/README.md +4 -3
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/pyproject.toml +4 -3
- wexample_orm-1.0.1/src/wexample_orm/entity/abstract_entity.py +41 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/tests/test_abstract.py +19 -0
- wexample_orm-0.0.6/src/wexample_orm/entity/abstract_entity.py +0 -31
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/__init__.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/common/__init__.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/common/abstract_repositories_manager.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/entity/__init__.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/exception/__init__.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/exception/repository_session_missing_exception.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/exception/unknown_repository_exception.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/py.typed +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/repository/__init__.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/repository/abstract_repository.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/session/__init__.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/session/abstract_session_factory.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/testing/__init__.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/testing/sqlite.py +0 -0
- {wexample_orm-0.0.6 → wexample_orm-1.0.1}/tests/__init__.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wexample-orm
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: ORM abstractions on top of SQLAlchemy, PostgreSQL-first (psycopg bundled).
|
|
5
5
|
Author-Email: weeger <contact@wexample.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -10,8 +10,9 @@ Classifier: Operating System :: OS Independent
|
|
|
10
10
|
Project-URL: homepage, https://github.com/wexample/python-orm
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
12
|
Requires-Dist: attrs>=23.1.0
|
|
13
|
+
Requires-Dist: psycopg[binary]>=3.2
|
|
13
14
|
Requires-Dist: sqlalchemy<3,>=2
|
|
14
|
-
Requires-Dist: wexample-helpers>=
|
|
15
|
+
Requires-Dist: wexample-helpers>=14.2.0
|
|
15
16
|
Provides-Extra: dev
|
|
16
17
|
Requires-Dist: pytest; extra == "dev"
|
|
17
18
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -19,9 +20,9 @@ Description-Content-Type: text/markdown
|
|
|
19
20
|
|
|
20
21
|
# orm
|
|
21
22
|
|
|
22
|
-
Version:
|
|
23
|
+
Version: 1.0.1
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
ORM abstractions on top of SQLAlchemy, PostgreSQL-first (psycopg bundled).
|
|
25
26
|
|
|
26
27
|
## Table of Contents
|
|
27
28
|
|
|
@@ -100,8 +101,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
100
101
|
## Dependencies
|
|
101
102
|
|
|
102
103
|
- attrs: >=23.1.0
|
|
104
|
+
- psycopg: >=3.2
|
|
103
105
|
- sqlalchemy: <3,>=2
|
|
104
|
-
- wexample-helpers: >=
|
|
106
|
+
- wexample-helpers: >=14.2.0
|
|
105
107
|
|
|
106
108
|
## Versioning & Compatibility Policy
|
|
107
109
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# orm
|
|
2
2
|
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
ORM abstractions on top of SQLAlchemy, PostgreSQL-first (psycopg bundled).
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
@@ -81,8 +81,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
81
81
|
## Dependencies
|
|
82
82
|
|
|
83
83
|
- attrs: >=23.1.0
|
|
84
|
+
- psycopg: >=3.2
|
|
84
85
|
- sqlalchemy: <3,>=2
|
|
85
|
-
- wexample-helpers: >=
|
|
86
|
+
- wexample-helpers: >=14.2.0
|
|
86
87
|
|
|
87
88
|
## Versioning & Compatibility Policy
|
|
88
89
|
|
|
@@ -6,8 +6,8 @@ build-backend = "pdm.backend"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "wexample-orm"
|
|
9
|
-
version = "
|
|
10
|
-
description = "
|
|
9
|
+
version = "1.0.1"
|
|
10
|
+
description = "ORM abstractions on top of SQLAlchemy, PostgreSQL-first (psycopg bundled)."
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "weeger", email = "contact@wexample.com" },
|
|
13
13
|
]
|
|
@@ -19,8 +19,9 @@ classifiers = [
|
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
21
|
"attrs>=23.1.0",
|
|
22
|
+
"psycopg[binary]>=3.2",
|
|
22
23
|
"sqlalchemy>=2,<3",
|
|
23
|
-
"wexample-helpers>=
|
|
24
|
+
"wexample-helpers>=14.2.0",
|
|
24
25
|
]
|
|
25
26
|
|
|
26
27
|
[project.readme]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import BigInteger, Integer
|
|
4
|
+
from sqlalchemy.orm import Mapped, as_declarative, declared_attr, mapped_column
|
|
5
|
+
from wexample_helpers.helpers.string import string_to_snake_case
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@as_declarative()
|
|
9
|
+
class AbstractEntity:
|
|
10
|
+
"""Common ancestor for every wex-managed SQLAlchemy entity.
|
|
11
|
+
|
|
12
|
+
Imposes a single convention for the primary key across all projects:
|
|
13
|
+
|
|
14
|
+
``id BIGINT GENERATED BY DEFAULT AS IDENTITY`` on PostgreSQL — modern
|
|
15
|
+
SQL standard (PG ≥ 10) and Doctrine's default when it owns the
|
|
16
|
+
schema, so the same table works seamlessly from PHP and Python.
|
|
17
|
+
|
|
18
|
+
A ``with_variant(Integer, "sqlite")`` is bolted on for tests: SQLite
|
|
19
|
+
only auto-increments a column declared as ``INTEGER PRIMARY KEY``
|
|
20
|
+
(ROWID alias), not ``BIGINT``. Production stays BIGINT, in-memory
|
|
21
|
+
sessions used by ``wexample_orm.testing.in_memory_session`` keep
|
|
22
|
+
working without per-project gymnastics.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__abstract__ = True
|
|
26
|
+
|
|
27
|
+
@declared_attr
|
|
28
|
+
def __tablename__(cls) -> str:
|
|
29
|
+
return cls.get_entity_name()
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def get_entity_name(cls) -> str:
|
|
33
|
+
return string_to_snake_case(cls.__name__)
|
|
34
|
+
|
|
35
|
+
@declared_attr
|
|
36
|
+
def id(cls) -> Mapped[int]:
|
|
37
|
+
return mapped_column(
|
|
38
|
+
BigInteger().with_variant(Integer(), "sqlite"),
|
|
39
|
+
primary_key=True,
|
|
40
|
+
autoincrement=True,
|
|
41
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from sqlalchemy import BigInteger
|
|
4
5
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
5
6
|
|
|
6
7
|
from wexample_orm.common.abstract_repositories_manager import (
|
|
@@ -22,6 +23,24 @@ def test_entity_name_is_snake_case_of_class_name() -> None:
|
|
|
22
23
|
assert User.__tablename__ == "user"
|
|
23
24
|
|
|
24
25
|
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# PK convention — single, framework-wide.
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_id_is_bigint_identity_style() -> None:
|
|
32
|
+
"""Every entity gets a BIGINT autoincrement PK with no Sequence.
|
|
33
|
+
|
|
34
|
+
Production schema (PG) uses ``GENERATED BY DEFAULT AS IDENTITY`` — the
|
|
35
|
+
DB owns id allocation, SQLAlchemy doesn't carry a Sequence default.
|
|
36
|
+
"""
|
|
37
|
+
id_col = User.__table__.c.id
|
|
38
|
+
assert isinstance(id_col.type, BigInteger)
|
|
39
|
+
assert id_col.default is None # no SQLAlchemy-managed Sequence
|
|
40
|
+
assert id_col.autoincrement is True
|
|
41
|
+
assert id_col.primary_key is True
|
|
42
|
+
|
|
43
|
+
|
|
25
44
|
def test_manager_caches_repository_instances() -> None:
|
|
26
45
|
with in_memory_session(AbstractEntity) as session:
|
|
27
46
|
manager = ProjectRepositoriesManager(
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from sqlalchemy import Integer, Sequence
|
|
4
|
-
from sqlalchemy.orm import Mapped, as_declarative, declared_attr, mapped_column
|
|
5
|
-
from wexample_helpers.helpers.string import string_to_snake_case
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@as_declarative()
|
|
9
|
-
class AbstractEntity:
|
|
10
|
-
__abstract__ = True
|
|
11
|
-
|
|
12
|
-
@declared_attr
|
|
13
|
-
def __tablename__(cls) -> str:
|
|
14
|
-
return cls.get_entity_name()
|
|
15
|
-
|
|
16
|
-
@classmethod
|
|
17
|
-
def get_entity_name(cls) -> str:
|
|
18
|
-
return string_to_snake_case(cls.__name__)
|
|
19
|
-
|
|
20
|
-
@classmethod
|
|
21
|
-
def get_sequence(cls) -> Sequence:
|
|
22
|
-
return Sequence(f"{cls.get_entity_name()}_id_seq")
|
|
23
|
-
|
|
24
|
-
@declared_attr
|
|
25
|
-
def id(cls) -> Mapped[int]:
|
|
26
|
-
return mapped_column(
|
|
27
|
-
Integer,
|
|
28
|
-
cls.get_sequence(),
|
|
29
|
-
primary_key=True,
|
|
30
|
-
autoincrement=True,
|
|
31
|
-
)
|
|
File without changes
|
|
File without changes
|
{wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/common/abstract_repositories_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/exception/unknown_repository_exception.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/repository/abstract_repository.py
RENAMED
|
File without changes
|
|
File without changes
|
{wexample_orm-0.0.6 → wexample_orm-1.0.1}/src/wexample_orm/session/abstract_session_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|