python-general-be-lib 0.8.1__tar.gz → 0.8.2__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 (45) hide show
  1. {python_general_be_lib-0.8.1/python_general_be_lib.egg-info → python_general_be_lib-0.8.2}/PKG-INFO +1 -1
  2. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/base/declarative_base.py +9 -5
  3. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/crud_repository.py +4 -2
  4. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/pyproject.toml +1 -1
  5. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2/python_general_be_lib.egg-info}/PKG-INFO +1 -1
  6. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/setup.py +1 -1
  7. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/tests/test_caricamento_parziale.py +48 -0
  8. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/LICENSE +0 -0
  9. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/README.md +0 -0
  10. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/__init__.py +0 -0
  11. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/exception/__init__.py +0 -0
  12. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/exception/access_exceptions.py +0 -0
  13. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/exception/crud_exceptions.py +0 -0
  14. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/exception/exception_interface.py +0 -0
  15. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/base/__init__.py +0 -0
  16. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/base/base_model.py +0 -0
  17. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/metadata/__init__.py +0 -0
  18. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/metadata/crud_metadata.py +0 -0
  19. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/metadata/geom_metadata.py +0 -0
  20. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/__init__.py +0 -0
  21. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/geometry_repository.py +0 -0
  22. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/handler/__init__.py +0 -0
  23. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/handler/base_handler.py +0 -0
  24. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/handler/ilike_handler.py +0 -0
  25. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/handler/interval_handler.py +0 -0
  26. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/handler/json_handler.py +0 -0
  27. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/handler/json_ilike_handler.py +0 -0
  28. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/handler/or_handler.py +0 -0
  29. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/handler/statement_manager.py +0 -0
  30. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/many_to_many_repository.py +0 -0
  31. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/interface/repository/view_repository.py +0 -0
  32. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/logger.py +0 -0
  33. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/general/paginator_dto.py +0 -0
  34. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/python_general_be_lib.egg-info/SOURCES.txt +0 -0
  35. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/python_general_be_lib.egg-info/dependency_links.txt +0 -0
  36. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/python_general_be_lib.egg-info/requires.txt +0 -0
  37. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/python_general_be_lib.egg-info/top_level.txt +0 -0
  38. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/setup.cfg +0 -0
  39. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/tests/test_compiled_cache.py +0 -0
  40. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/tests/test_logger.py +0 -0
  41. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/tests/test_pipeline_handler.py +0 -0
  42. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/tests/test_query_count.py +0 -0
  43. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/tests/test_smoke_crud.py +0 -0
  44. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/tests/test_sql_snapshot.py +0 -0
  45. {python_general_be_lib-0.8.1 → python_general_be_lib-0.8.2}/tests/test_view_repository.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-general-be-lib
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Summary: General purpose backend library — SQLAlchemy CRUD/Geometry repositories, FastAPI exceptions, Pydantic base models, logger utilities.
5
5
  Author-email: Andrea Di Placido <a.diplacido@arpes.it>, "Arpes S.r.l." <it.admin@arpes.it>
6
6
  License: MIT
@@ -45,15 +45,19 @@ class Base:
45
45
  default_values = dict()
46
46
  mapper: Mapper = inspect(cls)
47
47
 
48
- column_properties = [prop for prop in mapper.column_attrs if include_only is None or prop.key in include_only]
48
+ # ``mapper.columns`` è indicizzato per **nome di attributo** e non forza la
49
+ # configurazione dei mapper: ``column_attrs``/``attrs``/``relationships`` la
50
+ # forzerebbero, e chiamare ``to_pydantic`` a import-time — il pattern
51
+ # documentato — romperebbe le relationship con forward-ref non ancora
52
+ # risolvibili (stessa ragione per cui ``relationship_attributes`` è lazy).
53
+ selected = [(name, column) for name, column in mapper.columns.items()
54
+ if include_only is None or name in include_only]
49
55
 
50
56
  model_name = f"{cls.__name__}Model" if not model_name else model_name
51
57
  fields = dict()
52
- field_values = {prop.key: None if full_optional else ... for prop in column_properties}
58
+ field_values = {name: None if full_optional else ... for name, _ in selected}
53
59
  field_values.update(default_values)
54
- for prop in column_properties:
55
- col = prop.columns[0]
56
- field_name = prop.key
60
+ for field_name, col in selected:
57
61
  if isinstance(col.type, Geometry):
58
62
  field_type = Optional[WKBElement] if col.nullable or full_optional else WKBElement
59
63
  else:
@@ -41,7 +41,9 @@ def _reduced_model(entity: Type[Base], columns: frozenset[str]) -> Type[BaseMode
41
41
  ricostruire il TypeAdapter — mancherebbe il bersaglio a ogni richiesta, e il suo
42
42
  `maxsize=None` tratterebbe una voce per classe generata.
43
43
  """
44
- ordinate = [prop.key for prop in inspect(entity).column_attrs if prop.key in columns]
44
+ # ``mapper.columns`` è indicizzato per nome di attributo e non forza la
45
+ # configurazione dei mapper (a differenza di ``column_attrs``/``attrs``).
46
+ ordinate = [name for name in inspect(entity).columns.keys() if name in columns]
45
47
  digest = hashlib.sha1("|".join(ordinate).encode()).hexdigest()[:8]
46
48
  return entity.to_pydantic(
47
49
  model_name=f"{entity.__name__}_{digest}",
@@ -131,7 +133,7 @@ class CrudRepository[Entity: Base | Type[Base]]:
131
133
 
132
134
 
133
135
  def _retrieve_parsing_model(self, columns=None, exclude_columns=None):
134
- selected_columns = set(columns) if columns else set(self.entity.__mapper__.attrs.keys()) # o columns_names corretto
136
+ selected_columns = set(columns) if columns else set(inspect(self.entity).columns.keys())
135
137
  if exclude_columns:
136
138
  selected_columns -= set(exclude_columns)
137
139
  if columns is None and not exclude_columns:
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends.legacy:build"
4
4
 
5
5
  [project]
6
6
  name = "python-general-be-lib"
7
- version = "0.8.1"
7
+ version = "0.8.2"
8
8
  description = "General purpose backend library — SQLAlchemy CRUD/Geometry repositories, FastAPI exceptions, Pydantic base models, logger utilities."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-general-be-lib
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Summary: General purpose backend library — SQLAlchemy CRUD/Geometry repositories, FastAPI exceptions, Pydantic base models, logger utilities.
5
5
  Author-email: Andrea Di Placido <a.diplacido@arpes.it>, "Arpes S.r.l." <it.admin@arpes.it>
6
6
  License: MIT
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="python-general-be-lib",
5
- version="0.8.1",
5
+ version="0.8.2",
6
6
  packages=find_packages(include=["general*"]),
7
7
  python_requires=">=3.12",
8
8
  install_requires=[
@@ -234,3 +234,51 @@ def test_paging_find_usa_lo_stesso_modello_ridotto(engine: Engine, repo: CrudRep
234
234
  pagina = repo.paging_find(columns=["nome"], limit=20)
235
235
 
236
236
  assert set(pagina.data[0].model_dump()) == {"nome"}
237
+
238
+
239
+ # ------------------------------------------- configurazione dei mapper a import-time
240
+
241
+
242
+ def test_to_pydantic_non_forza_la_configurazione_dei_mapper() -> None:
243
+ """``to_pydantic`` deve funzionare su un'entità con forward-ref non ancora risolvibili.
244
+
245
+ Il pattern documentato è generare i modelli a import-time
246
+ (``UserModel = User.to_pydantic(...)``), quando le entità referenziate per nome
247
+ dalle ``relationship`` possono non essere ancora state importate. Toccare
248
+ ``mapper.column_attrs``, ``mapper.attrs`` o ``mapper.relationships`` innesca
249
+ ``configure_mappers()`` su **tutto** il registry e fa fallire l'import con::
250
+
251
+ InvalidRequestError: When initializing mapper Mapper[Rel(...)],
252
+ expression 'Azienda' failed to locate a name
253
+
254
+ ``mapper.columns`` invece non la forza, ed è già indicizzato per nome di attributo.
255
+ È la stessa ragione per cui ``CrudRepository.relationship_attributes`` è una
256
+ property lazy.
257
+ """
258
+ from sqlalchemy import ForeignKey
259
+ from sqlalchemy.orm import relationship
260
+
261
+ class RelAziendaUtente(Base):
262
+ __tablename__ = "rel_azienda_utente_probe"
263
+
264
+ id = Column(Integer, primary_key=True)
265
+ id_azienda = Column(Integer, ForeignKey("azienda_probe.id"))
266
+ # riferimento per nome a una classe definita più avanti: irrisolvibile adesso
267
+ azienda = relationship("AziendaProbe")
268
+
269
+ modello = RelAziendaUtente.to_pydantic(full_optional=True)
270
+ assert set(modello.model_fields) == {"id", "id_azienda"}
271
+
272
+ ridotto = RelAziendaUtente.to_pydantic(full_optional=True, include_only=["id_azienda"])
273
+ assert set(ridotto.model_fields) == {"id_azienda"}
274
+
275
+ # la classe mancante arriva dopo: da qui in poi la configurazione può avvenire
276
+ class AziendaProbe(Base):
277
+ __tablename__ = "azienda_probe"
278
+
279
+ id = Column(Integer, primary_key=True)
280
+
281
+ from sqlalchemy.orm import configure_mappers
282
+
283
+ configure_mappers()
284
+ assert RelAziendaUtente.azienda.property.mapper.class_ is AziendaProbe