wexample-orm 0.0.6__tar.gz → 1.0.0__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.
Files changed (21) hide show
  1. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/PKG-INFO +6 -4
  2. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/README.md +3 -2
  3. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/pyproject.toml +3 -2
  4. wexample_orm-1.0.0/src/wexample_orm/entity/abstract_entity.py +41 -0
  5. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/tests/test_abstract.py +19 -0
  6. wexample_orm-0.0.6/src/wexample_orm/entity/abstract_entity.py +0 -31
  7. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/__init__.py +0 -0
  8. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/common/__init__.py +0 -0
  9. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/common/abstract_repositories_manager.py +0 -0
  10. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/entity/__init__.py +0 -0
  11. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/exception/__init__.py +0 -0
  12. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/exception/repository_session_missing_exception.py +0 -0
  13. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/exception/unknown_repository_exception.py +0 -0
  14. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/py.typed +0 -0
  15. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/repository/__init__.py +0 -0
  16. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/repository/abstract_repository.py +0 -0
  17. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/session/__init__.py +0 -0
  18. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/session/abstract_session_factory.py +0 -0
  19. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/testing/__init__.py +0 -0
  20. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/src/wexample_orm/testing/sqlite.py +0 -0
  21. {wexample_orm-0.0.6 → wexample_orm-1.0.0}/tests/__init__.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wexample-orm
3
- Version: 0.0.6
4
- Summary: Generic ORM abstractions on top of SQLAlchemy.
3
+ Version: 1.0.0
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,6 +10,7 @@ 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
15
  Requires-Dist: wexample-helpers>=13.0.0
15
16
  Provides-Extra: dev
@@ -19,9 +20,9 @@ Description-Content-Type: text/markdown
19
20
 
20
21
  # orm
21
22
 
22
- Version: 0.0.6
23
+ Version: 1.0.0
23
24
 
24
- Generic ORM abstractions on top of SQLAlchemy.
25
+ ORM abstractions on top of SQLAlchemy, PostgreSQL-first (psycopg bundled).
25
26
 
26
27
  ## Table of Contents
27
28
 
@@ -100,6 +101,7 @@ 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
106
  - wexample-helpers: >=13.0.0
105
107
 
@@ -1,8 +1,8 @@
1
1
  # orm
2
2
 
3
- Version: 0.0.6
3
+ Version: 1.0.0
4
4
 
5
- Generic ORM abstractions on top of SQLAlchemy.
5
+ ORM abstractions on top of SQLAlchemy, PostgreSQL-first (psycopg bundled).
6
6
 
7
7
  ## Table of Contents
8
8
 
@@ -81,6 +81,7 @@ 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
86
  - wexample-helpers: >=13.0.0
86
87
 
@@ -6,8 +6,8 @@ build-backend = "pdm.backend"
6
6
 
7
7
  [project]
8
8
  name = "wexample-orm"
9
- version = "0.0.6"
10
- description = "Generic ORM abstractions on top of SQLAlchemy."
9
+ version = "1.0.0"
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,6 +19,7 @@ classifiers = [
19
19
  ]
20
20
  dependencies = [
21
21
  "attrs>=23.1.0",
22
+ "psycopg[binary]>=3.2",
22
23
  "sqlalchemy>=2,<3",
23
24
  "wexample-helpers>=13.0.0",
24
25
  ]
@@ -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
- )