risclog.sqlalchemy 6.2__tar.gz → 7.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.
Files changed (43) hide show
  1. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/CHANGES.rst +17 -2
  2. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/PKG-INFO +18 -3
  3. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/setup.py +1 -1
  4. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/db.py +62 -8
  5. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/serializer.py +0 -1
  6. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog.sqlalchemy.egg-info/PKG-INFO +18 -3
  7. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/COPYRIGHT.rst +0 -0
  8. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/HACKING.rst +0 -0
  9. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/LICENSE.txt +0 -0
  10. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/README.rst +0 -0
  11. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/doc/about.txt +0 -0
  12. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/doc/api.txt +0 -0
  13. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/doc/changes.txt +0 -0
  14. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/doc/conf.py +0 -0
  15. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/doc/index.txt +0 -0
  16. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/pyproject.toml +0 -0
  17. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/requirements.txt +0 -0
  18. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/setup.cfg +0 -0
  19. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/__init__.py +0 -0
  20. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/__init__.py +0 -0
  21. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/cache.py +0 -0
  22. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/fixtures.py +0 -0
  23. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/functions.py +0 -0
  24. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/interfaces.py +0 -0
  25. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/model.py +0 -0
  26. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/testing.py +0 -0
  27. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/testlayer.py +0 -0
  28. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/__init__.py +0 -0
  29. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/conftest.py +0 -0
  30. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/fixtures/alembic/env.py +0 -0
  31. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/fixtures/alembic/script.py.mako +0 -0
  32. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/fixtures/alembic/versions/3f735078d5a8_foo.py +0 -0
  33. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/test_cache.py +0 -0
  34. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/test_db.py +0 -0
  35. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/test_fixtures.py +0 -0
  36. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/test_model.py +0 -0
  37. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog/sqlalchemy/tests/test_serializer.py +0 -0
  38. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog.sqlalchemy.egg-info/SOURCES.txt +0 -0
  39. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog.sqlalchemy.egg-info/dependency_links.txt +0 -0
  40. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog.sqlalchemy.egg-info/namespace_packages.txt +0 -0
  41. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog.sqlalchemy.egg-info/not-zip-safe +0 -0
  42. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog.sqlalchemy.egg-info/requires.txt +0 -0
  43. {risclog.sqlalchemy-6.2 → risclog.sqlalchemy-7.1}/src/risclog.sqlalchemy.egg-info/top_level.txt +0 -0
@@ -2,10 +2,25 @@
2
2
  Change log for risclog.sqlalchemy
3
3
  =================================
4
4
 
5
- 6.2 (2024-04-19)
5
+ 7.1 (2024-03-22)
6
6
  ================
7
7
 
8
- - Add json encoder for `datetime.time` objects.
8
+ - Fix `sqlalchemy` 1.3 compatibility in `Database.empty()`.
9
+
10
+
11
+ 7.0 (2024-03-21)
12
+ ================
13
+
14
+ - Introduce new `RoutingSession` method `execute_with_bind()` to execute
15
+ statements with a specific bind.
16
+ - `RoutingSession.using_bind()` is now deprecated and kept for backward
17
+ compatibility.
18
+ Instead of a new session instance, it now returns a shim that maps a
19
+ minimal subset of session methods to the original session instance.
20
+ This change was necessary because the previous implementation of creating
21
+ a new session instance and copying all the original session's state
22
+ proved unstable when used with SQLAlchemy 1.4. (One reason being the new
23
+ autobegin feature of sessions changing interactions with `zope.sqlalchemy`.)
9
24
 
10
25
 
11
26
  6.1 (2024-03-12)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: risclog.sqlalchemy
3
- Version: 6.2
3
+ Version: 7.1
4
4
  Summary: Encapsulate sqlalchemy modelling infrastructure.
5
5
  Home-page: https://github.com/gocept/risclog.sqlalchemy
6
6
  Author: gocept <mail@gocept.com>
@@ -106,10 +106,25 @@ python, where ``sphinx-pypi-upload`` is installed::
106
106
  Change log for risclog.sqlalchemy
107
107
  =================================
108
108
 
109
- 6.2 (2024-04-19)
109
+ 7.1 (2024-03-22)
110
110
  ================
111
111
 
112
- - Add json encoder for `datetime.time` objects.
112
+ - Fix `sqlalchemy` 1.3 compatibility in `Database.empty()`.
113
+
114
+
115
+ 7.0 (2024-03-21)
116
+ ================
117
+
118
+ - Introduce new `RoutingSession` method `execute_with_bind()` to execute
119
+ statements with a specific bind.
120
+ - `RoutingSession.using_bind()` is now deprecated and kept for backward
121
+ compatibility.
122
+ Instead of a new session instance, it now returns a shim that maps a
123
+ minimal subset of session methods to the original session instance.
124
+ This change was necessary because the previous implementation of creating
125
+ a new session instance and copying all the original session's state
126
+ proved unstable when used with SQLAlchemy 1.4. (One reason being the new
127
+ autobegin feature of sessions changing interactions with `zope.sqlalchemy`.)
113
128
 
114
129
 
115
130
  6.1 (2024-03-12)
@@ -9,7 +9,7 @@ from setuptools import find_packages, setup
9
9
 
10
10
  setup(
11
11
  name='risclog.sqlalchemy',
12
- version='6.2',
12
+ version='7.1',
13
13
  python_requires='>=3.7',
14
14
  install_requires=[
15
15
  'SQLAlchemy >= 1.0, < 2',
@@ -12,10 +12,14 @@ import transaction
12
12
  import zope.component
13
13
  import zope.interface
14
14
  import zope.sqlalchemy
15
+ from packaging.version import Version as parse_version
16
+ from sqlalchemy import __version__ as sqlalchemy_version
15
17
 
16
18
  # Mapping engine name registered using Database.register_engine --> base class
17
19
  _ENGINE_CLASS_MAPPING = {}
18
20
 
21
+ SA_GE_14 = parse_version(sqlalchemy_version) >= parse_version('1.4.0')
22
+
19
23
 
20
24
  def assert_engine_not_registered(name, mapping):
21
25
  """Ensure a consistent error message."""
@@ -72,12 +76,60 @@ class RoutingSession(sqlalchemy.orm.Session):
72
76
 
73
77
  raise RuntimeError(f'Did not find an engine for {mapper.class_}')
74
78
 
79
+ def _bound_execute(self, bind, *args, **kwargs):
80
+ if SA_GE_14:
81
+ return self.execute(*args, bind_arguments={'bind': bind}, **kwargs)
82
+ else:
83
+ return self.execute(*args, bind=bind, **kwargs)
84
+
85
+ def execute_with_bind(self, engine_name, *args, **kwargs):
86
+ """
87
+ Execute a statement using a specific engine, indicated by its name.
88
+ """
89
+ db_util = zope.component.getUtility(
90
+ risclog.sqlalchemy.interfaces.IDatabase
91
+ )
92
+ bind = db_util.get_engine(engine_name)
93
+ return self._bound_execute(bind, *args, **kwargs)
94
+
75
95
  def using_bind(self, name):
76
- """Select an engine name if not using mappers."""
77
- session = RoutingSession()
78
- vars(session).update(vars(self))
79
- session._name = name
80
- return session
96
+ """
97
+ Return session-like object that limits its action to the given engine.
98
+
99
+ This method is deprecated and is kept for backward compatibility.
100
+ If you need to execute a statement using a specific engine, use
101
+ `execute_with_bind` instead.
102
+ """
103
+
104
+ class BoundSession:
105
+ """
106
+ Shim that maps a subset of commonly used SQLAlchemy session methods
107
+ to the original session while forcing the bind.
108
+ """
109
+
110
+ def __init__(self, session, engine_name):
111
+ self.session = session
112
+ db_util = zope.component.getUtility(
113
+ risclog.sqlalchemy.interfaces.IDatabase
114
+ )
115
+ self.bind = db_util.get_engine(engine_name)
116
+
117
+ def execute(self, *args, **kwargs):
118
+ return self.session._bound_execute(self.bind, *args, **kwargs)
119
+
120
+ def query(self, *args, **kwargs):
121
+ # Associate query with this shim-session, to make it use its
122
+ # `execute` method.
123
+ query = self.session.query(*args, **kwargs).with_session(self)
124
+ # Used to introspect if a query is bound to a specific engine.
125
+ query._bind = self.bind
126
+ return query
127
+
128
+ def connection(self):
129
+ bind = self.session.get_bind(bind=self.bind)
130
+ return self.session._connection_for_bind(bind)
131
+
132
+ return BoundSession(self, name)
81
133
 
82
134
 
83
135
  def get_database(testing=False, keep_session=False, expire_on_commit=False):
@@ -186,8 +238,10 @@ class Database:
186
238
  def _verify_engine(self, engine):
187
239
  # Step 1: Try to identify a testing table
188
240
  conn = engine.connect()
241
+ from sqlalchemy import text
242
+
189
243
  try:
190
- conn.execute('SELECT * FROM tmp_functest')
244
+ conn.execute(text('SELECT * FROM tmp_functest'))
191
245
  except sqlalchemy.exc.DatabaseError:
192
246
  db_is_testing = False
193
247
  else:
@@ -280,13 +334,13 @@ class Database:
280
334
  if not table_names:
281
335
  return
282
336
  tables = ', '.join('"%s"' % x for x in table_names)
283
- self.session.execute(
337
+ self.session._bound_execute(
338
+ engine,
284
339
  'TRUNCATE {} {} {}'.format(
285
340
  tables,
286
341
  'RESTART IDENTITY' if restart_sequences else '',
287
342
  'CASCADE' if cascade else '',
288
343
  ),
289
- bind=engine,
290
344
  )
291
345
  zope.sqlalchemy.mark_changed(self.session)
292
346
  if commit:
@@ -51,7 +51,6 @@ def uuid_encode(o, request=None):
51
51
  ENCODERS = {
52
52
  ObjectBase: sqlalchemy_encode,
53
53
  datetime.date: datetime_encode,
54
- datetime.time: datetime_encode,
55
54
  datetime.datetime: datetime_encode,
56
55
  decimal.Decimal: decimal_encode,
57
56
  uuid.UUID: uuid_encode,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: risclog.sqlalchemy
3
- Version: 6.2
3
+ Version: 7.1
4
4
  Summary: Encapsulate sqlalchemy modelling infrastructure.
5
5
  Home-page: https://github.com/gocept/risclog.sqlalchemy
6
6
  Author: gocept <mail@gocept.com>
@@ -106,10 +106,25 @@ python, where ``sphinx-pypi-upload`` is installed::
106
106
  Change log for risclog.sqlalchemy
107
107
  =================================
108
108
 
109
- 6.2 (2024-04-19)
109
+ 7.1 (2024-03-22)
110
110
  ================
111
111
 
112
- - Add json encoder for `datetime.time` objects.
112
+ - Fix `sqlalchemy` 1.3 compatibility in `Database.empty()`.
113
+
114
+
115
+ 7.0 (2024-03-21)
116
+ ================
117
+
118
+ - Introduce new `RoutingSession` method `execute_with_bind()` to execute
119
+ statements with a specific bind.
120
+ - `RoutingSession.using_bind()` is now deprecated and kept for backward
121
+ compatibility.
122
+ Instead of a new session instance, it now returns a shim that maps a
123
+ minimal subset of session methods to the original session instance.
124
+ This change was necessary because the previous implementation of creating
125
+ a new session instance and copying all the original session's state
126
+ proved unstable when used with SQLAlchemy 1.4. (One reason being the new
127
+ autobegin feature of sessions changing interactions with `zope.sqlalchemy`.)
113
128
 
114
129
 
115
130
  6.1 (2024-03-12)