eventsourcing 9.3.0__py3-none-any.whl → 9.3.0b1__py3-none-any.whl

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.

Potentially problematic release.


This version of eventsourcing might be problematic. Click here for more details.

@@ -915,16 +915,6 @@ class AggregateNotFoundError(EventSourcingError):
915
915
  """
916
916
 
917
917
 
918
- class AggregateNotFound(AggregateNotFoundError): # noqa: N818
919
- def __init__(self, *args: Any, **kwargs: Any) -> None:
920
- warn(
921
- "AggregateNotFound is deprecated, use AggregateNotFoundError instead",
922
- DeprecationWarning,
923
- stacklevel=2,
924
- )
925
- super().__init__(*args, **kwargs)
926
-
927
-
928
918
  class EventSourcedLog(Generic[TDomainEvent]):
929
919
  """
930
920
  Constructs a sequence of domain events, like an aggregate.
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
4
+ from collections import defaultdict
3
5
  from dataclasses import dataclass
4
6
  from datetime import datetime, timezone
5
- from typing import Any, Iterable, List, Type, TypeVar, cast
7
+ from typing import Any, ClassVar, Dict, Iterable, List, Type, TypeVar, cast
6
8
  from uuid import UUID, uuid4
7
9
 
8
10
  from eventsourcing.dispatch import singledispatchmethod
@@ -27,7 +29,6 @@ class Aggregate:
27
29
  id: UUID
28
30
  version: int
29
31
  created_on: datetime
30
- _pending_events: List[DomainEvent]
31
32
 
32
33
  def __init__(self, event: DomainEvent):
33
34
  self.id = event.originator_id
@@ -46,15 +47,15 @@ class Aggregate:
46
47
  timestamp=event_class.create_timestamp(),
47
48
  )
48
49
  new_event = event_class(**kwargs)
49
- self._apply(new_event)
50
- self._pending_events.append(new_event)
50
+ self.apply(new_event)
51
+ self.pending_events.append(new_event)
51
52
 
52
53
  @singledispatchmethod
53
- def _apply(self, event: DomainEvent) -> None:
54
+ def apply(self, event: DomainEvent) -> None:
54
55
  """Applies event to aggregate."""
55
56
 
56
57
  def collect_events(self) -> List[DomainEvent]:
57
- events, self._pending_events = self._pending_events, []
58
+ events, self.pending_events = self.pending_events, []
58
59
  return events
59
60
 
60
61
  @classmethod
@@ -63,12 +64,25 @@ class Aggregate:
63
64
  _: TAggregate | None,
64
65
  events: Iterable[DomainEvent],
65
66
  ) -> TAggregate | None:
66
- aggregate: TAggregate = object.__new__(cls)
67
- aggregate._pending_events = []
67
+ aggregate = object.__new__(cls)
68
68
  for event in events:
69
- aggregate._apply(event)
69
+ aggregate.apply(event)
70
70
  return aggregate
71
71
 
72
+ @property
73
+ def pending_events(self) -> List[DomainEvent]:
74
+ return type(self).__pending_events[id(self)]
75
+
76
+ @pending_events.setter
77
+ def pending_events(self, pending_events: List[DomainEvent]) -> None:
78
+ type(self).__pending_events[id(self)] = pending_events
79
+
80
+ __pending_events: ClassVar[Dict[int, List[DomainEvent]]] = defaultdict(list)
81
+
82
+ def __del__(self) -> None:
83
+ with contextlib.suppress(KeyError):
84
+ type(self).__pending_events.pop(id(self))
85
+
72
86
 
73
87
  class Dog(Aggregate):
74
88
  @dataclass(frozen=True)
@@ -88,27 +102,27 @@ class Dog(Aggregate):
88
102
  name=name,
89
103
  )
90
104
  dog = cast(Dog, cls.projector(None, [event]))
91
- dog._pending_events.append(event)
105
+ dog.pending_events.append(event)
92
106
  return dog
93
107
 
94
108
  def add_trick(self, trick: str) -> None:
95
109
  self.trigger_event(self.TrickAdded, trick=trick)
96
110
 
97
111
  @singledispatchmethod
98
- def _apply(self, event: DomainEvent) -> None:
112
+ def apply(self, event: DomainEvent) -> None:
99
113
  """Applies event to aggregate."""
100
114
 
101
- @_apply.register(Registered)
115
+ @apply.register(Registered)
102
116
  def _(self, event: Registered) -> None:
103
117
  super().__init__(event)
104
118
  self.name = event.name
105
119
  self.tricks: List[str] = []
106
120
 
107
- @_apply.register(TrickAdded)
121
+ @apply.register(TrickAdded)
108
122
  def _(self, event: TrickAdded) -> None:
109
123
  self.tricks.append(event.trick)
110
124
  self.version = event.originator_version
111
125
 
112
- @_apply.register(Snapshot)
126
+ @apply.register(Snapshot)
113
127
  def _(self, event: Snapshot) -> None:
114
128
  self.__dict__.update(event.state)
@@ -23,119 +23,112 @@ class ContentManagementSystemTestCase(TestCase):
23
23
  env: ClassVar[Dict[str, str]] = {}
24
24
 
25
25
  def test_system(self) -> None:
26
- with SingleThreadedRunner(
27
- system=ContentManagementSystem(), env=self.env
28
- ) as runner:
29
-
30
- content_management_app = runner.get(ContentManagementApplication)
31
- search_index_app = runner.get(SearchIndexApplication)
32
-
33
- # Set user_id context variable.
34
- user_id = uuid4()
35
- user_id_cvar.set(user_id)
36
-
37
- # Create empty pages.
38
- content_management_app.create_page(title="Animals", slug="animals")
39
- content_management_app.create_page(title="Plants", slug="plants")
40
- content_management_app.create_page(title="Minerals", slug="minerals")
41
-
42
- # Search, expect no results.
43
- self.assertEqual(0, len(search_index_app.search("cat")))
44
- self.assertEqual(0, len(search_index_app.search("rose")))
45
- self.assertEqual(0, len(search_index_app.search("calcium")))
46
-
47
- # Update the pages.
48
- content_management_app.update_body(slug="animals", body="cat")
49
- content_management_app.update_body(slug="plants", body="rose")
50
- content_management_app.update_body(slug="minerals", body="calcium")
51
-
52
- # Search for single words.
53
- page_ids = search_index_app.search("cat")
54
- self.assertEqual(1, len(page_ids))
55
- page = content_management_app.get_page_by_id(page_ids[0])
56
- self.assertEqual(page["slug"], "animals")
57
- self.assertEqual(page["body"], "cat")
58
-
59
- page_ids = search_index_app.search("rose")
60
- self.assertEqual(1, len(page_ids))
61
- page = content_management_app.get_page_by_id(page_ids[0])
62
- self.assertEqual(page["slug"], "plants")
63
- self.assertEqual(page["body"], "rose")
64
-
65
- page_ids = search_index_app.search("calcium")
66
- self.assertEqual(1, len(page_ids))
67
- page = content_management_app.get_page_by_id(page_ids[0])
68
- self.assertEqual(page["slug"], "minerals")
69
- self.assertEqual(page["body"], "calcium")
70
-
71
- self.assertEqual(len(search_index_app.search("dog")), 0)
72
- self.assertEqual(len(search_index_app.search("bluebell")), 0)
73
- self.assertEqual(len(search_index_app.search("zinc")), 0)
74
-
75
- # Update the pages again.
76
- content_management_app.update_body(slug="animals", body="cat dog zebra")
77
- content_management_app.update_body(
78
- slug="plants", body="bluebell rose jasmine"
79
- )
80
- content_management_app.update_body(
81
- slug="minerals", body="iron zinc calcium"
82
- )
83
-
84
- # Search for single words.
85
- page_ids = search_index_app.search("cat")
86
- self.assertEqual(1, len(page_ids))
87
- page = content_management_app.get_page_by_id(page_ids[0])
88
- self.assertEqual(page["slug"], "animals")
89
- self.assertEqual(page["body"], "cat dog zebra")
90
-
91
- page_ids = search_index_app.search("rose")
92
- self.assertEqual(1, len(page_ids))
93
- page = content_management_app.get_page_by_id(page_ids[0])
94
- self.assertEqual(page["slug"], "plants")
95
- self.assertEqual(page["body"], "bluebell rose jasmine")
96
-
97
- page_ids = search_index_app.search("calcium")
98
- self.assertEqual(1, len(page_ids))
99
- page = content_management_app.get_page_by_id(page_ids[0])
100
- self.assertEqual(page["slug"], "minerals")
101
- self.assertEqual(page["body"], "iron zinc calcium")
102
-
103
- page_ids = search_index_app.search("dog")
104
- self.assertEqual(1, len(page_ids))
105
- page = content_management_app.get_page_by_id(page_ids[0])
106
- self.assertEqual(page["slug"], "animals")
107
- self.assertEqual(page["body"], "cat dog zebra")
108
-
109
- page_ids = search_index_app.search("bluebell")
110
- self.assertEqual(1, len(page_ids))
111
- page = content_management_app.get_page_by_id(page_ids[0])
112
- self.assertEqual(page["slug"], "plants")
113
- self.assertEqual(page["body"], "bluebell rose jasmine")
114
-
115
- page_ids = search_index_app.search("zinc")
116
- self.assertEqual(1, len(page_ids))
117
- page = content_management_app.get_page_by_id(page_ids[0])
118
- self.assertEqual(page["slug"], "minerals")
119
- self.assertEqual(page["body"], "iron zinc calcium")
120
-
121
- # Search for multiple words in same page.
122
- page_ids = search_index_app.search("dog cat")
123
- self.assertEqual(1, len(page_ids))
124
- page = content_management_app.get_page_by_id(page_ids[0])
125
- self.assertEqual(page["slug"], "animals")
126
- self.assertEqual(page["body"], "cat dog zebra")
127
-
128
- # Search for multiple words in same page, expect no results.
129
- page_ids = search_index_app.search("rose zebra")
130
- self.assertEqual(0, len(page_ids))
131
-
132
- # Search for alternative words, expect two results.
133
- page_ids = search_index_app.search("rose OR zebra")
134
- pages = [
135
- content_management_app.get_page_by_id(page_id) for page_id in page_ids
136
- ]
137
- self.assertEqual(2, len(pages))
138
- self.assertEqual(["animals", "plants"], sorted(p["slug"] for p in pages))
26
+ runner = SingleThreadedRunner(system=ContentManagementSystem(), env=self.env)
27
+ runner.start()
28
+
29
+ content_management_app = runner.get(ContentManagementApplication)
30
+ search_index_app = runner.get(SearchIndexApplication)
31
+
32
+ # Set user_id context variable.
33
+ user_id = uuid4()
34
+ user_id_cvar.set(user_id)
35
+
36
+ # Create empty pages.
37
+ content_management_app.create_page(title="Animals", slug="animals")
38
+ content_management_app.create_page(title="Plants", slug="plants")
39
+ content_management_app.create_page(title="Minerals", slug="minerals")
40
+
41
+ # Search, expect no results.
42
+ self.assertEqual(0, len(search_index_app.search("cat")))
43
+ self.assertEqual(0, len(search_index_app.search("rose")))
44
+ self.assertEqual(0, len(search_index_app.search("calcium")))
45
+
46
+ # Update the pages.
47
+ content_management_app.update_body(slug="animals", body="cat")
48
+ content_management_app.update_body(slug="plants", body="rose")
49
+ content_management_app.update_body(slug="minerals", body="calcium")
50
+
51
+ # Search for single words.
52
+ page_ids = search_index_app.search("cat")
53
+ self.assertEqual(1, len(page_ids))
54
+ page = content_management_app.get_page_by_id(page_ids[0])
55
+ self.assertEqual(page["slug"], "animals")
56
+ self.assertEqual(page["body"], "cat")
57
+
58
+ page_ids = search_index_app.search("rose")
59
+ self.assertEqual(1, len(page_ids))
60
+ page = content_management_app.get_page_by_id(page_ids[0])
61
+ self.assertEqual(page["slug"], "plants")
62
+ self.assertEqual(page["body"], "rose")
63
+
64
+ page_ids = search_index_app.search("calcium")
65
+ self.assertEqual(1, len(page_ids))
66
+ page = content_management_app.get_page_by_id(page_ids[0])
67
+ self.assertEqual(page["slug"], "minerals")
68
+ self.assertEqual(page["body"], "calcium")
69
+
70
+ self.assertEqual(len(search_index_app.search("dog")), 0)
71
+ self.assertEqual(len(search_index_app.search("bluebell")), 0)
72
+ self.assertEqual(len(search_index_app.search("zinc")), 0)
73
+
74
+ # Update the pages again.
75
+ content_management_app.update_body(slug="animals", body="cat dog zebra")
76
+ content_management_app.update_body(slug="plants", body="bluebell rose jasmine")
77
+ content_management_app.update_body(slug="minerals", body="iron zinc calcium")
78
+
79
+ # Search for single words.
80
+ page_ids = search_index_app.search("cat")
81
+ self.assertEqual(1, len(page_ids))
82
+ page = content_management_app.get_page_by_id(page_ids[0])
83
+ self.assertEqual(page["slug"], "animals")
84
+ self.assertEqual(page["body"], "cat dog zebra")
85
+
86
+ page_ids = search_index_app.search("rose")
87
+ self.assertEqual(1, len(page_ids))
88
+ page = content_management_app.get_page_by_id(page_ids[0])
89
+ self.assertEqual(page["slug"], "plants")
90
+ self.assertEqual(page["body"], "bluebell rose jasmine")
91
+
92
+ page_ids = search_index_app.search("calcium")
93
+ self.assertEqual(1, len(page_ids))
94
+ page = content_management_app.get_page_by_id(page_ids[0])
95
+ self.assertEqual(page["slug"], "minerals")
96
+ self.assertEqual(page["body"], "iron zinc calcium")
97
+
98
+ page_ids = search_index_app.search("dog")
99
+ self.assertEqual(1, len(page_ids))
100
+ page = content_management_app.get_page_by_id(page_ids[0])
101
+ self.assertEqual(page["slug"], "animals")
102
+ self.assertEqual(page["body"], "cat dog zebra")
103
+
104
+ page_ids = search_index_app.search("bluebell")
105
+ self.assertEqual(1, len(page_ids))
106
+ page = content_management_app.get_page_by_id(page_ids[0])
107
+ self.assertEqual(page["slug"], "plants")
108
+ self.assertEqual(page["body"], "bluebell rose jasmine")
109
+
110
+ page_ids = search_index_app.search("zinc")
111
+ self.assertEqual(1, len(page_ids))
112
+ page = content_management_app.get_page_by_id(page_ids[0])
113
+ self.assertEqual(page["slug"], "minerals")
114
+ self.assertEqual(page["body"], "iron zinc calcium")
115
+
116
+ # Search for multiple words in same page.
117
+ page_ids = search_index_app.search("dog cat")
118
+ self.assertEqual(1, len(page_ids))
119
+ page = content_management_app.get_page_by_id(page_ids[0])
120
+ self.assertEqual(page["slug"], "animals")
121
+ self.assertEqual(page["body"], "cat dog zebra")
122
+
123
+ # Search for multiple words in same page, expect no results.
124
+ page_ids = search_index_app.search("rose zebra")
125
+ self.assertEqual(0, len(page_ids))
126
+
127
+ # Search for alternative words, expect two results.
128
+ page_ids = search_index_app.search("rose OR zebra")
129
+ pages = [content_management_app.get_page_by_id(page_id) for page_id in page_ids]
130
+ self.assertEqual(2, len(pages))
131
+ self.assertEqual(["animals", "plants"], sorted(p["slug"] for p in pages))
139
132
 
140
133
 
141
134
  class TestWithSQLite(ContentManagementSystemTestCase):
@@ -164,17 +157,18 @@ class TestWithPostgres(ContentManagementSystemTestCase):
164
157
  super().tearDown()
165
158
 
166
159
  def drop_tables(self) -> None:
167
- with PostgresDatastore(
160
+ db = PostgresDatastore(
168
161
  self.env["POSTGRES_DBNAME"],
169
162
  self.env["POSTGRES_HOST"],
170
163
  self.env["POSTGRES_PORT"],
171
164
  self.env["POSTGRES_USER"],
172
165
  self.env["POSTGRES_PASSWORD"],
173
- ) as datastore:
174
- drop_postgres_table(datastore, "public.contentmanagementapplication_events")
175
- drop_postgres_table(datastore, "public.pages_projection_example")
176
- drop_postgres_table(datastore, "public.searchindexapplication_events")
177
- drop_postgres_table(datastore, "public.searchindexapplication_tracking")
166
+ )
167
+ drop_postgres_table(db, "public.contentmanagementapplication_events")
168
+ drop_postgres_table(db, "public.pages_projection_example")
169
+ drop_postgres_table(db, "public.searchindexapplication_events")
170
+ drop_postgres_table(db, "public.searchindexapplication_tracking")
171
+ db.close()
178
172
 
179
173
 
180
174
  del ContentManagementSystemTestCase
@@ -96,15 +96,16 @@ class TestWithPostgres(SearchableContentApplicationTestCase):
96
96
  super().tearDown()
97
97
 
98
98
  def drop_tables(self) -> None:
99
- with PostgresDatastore(
99
+ db = PostgresDatastore(
100
100
  os.environ["POSTGRES_DBNAME"],
101
101
  os.environ["POSTGRES_HOST"],
102
102
  os.environ["POSTGRES_PORT"],
103
103
  os.environ["POSTGRES_USER"],
104
104
  os.environ["POSTGRES_PASSWORD"],
105
- ) as datastore:
106
- drop_postgres_table(datastore, "public.searchablecontentapplication_events")
107
- drop_postgres_table(datastore, "public.pages_projection_example")
105
+ )
106
+ drop_postgres_table(db, "public.searchablecontentapplication_events")
107
+ drop_postgres_table(db, "public.pages_projection_example")
108
+ db.close()
108
109
 
109
110
 
110
111
  del SearchableContentApplicationTestCase
@@ -54,15 +54,16 @@ class TestWithPostgres(SearchableContentRecorderTestCase):
54
54
  super().tearDown()
55
55
 
56
56
  def drop_tables(self) -> None:
57
- with PostgresDatastore(
57
+ db = PostgresDatastore(
58
58
  os.environ["POSTGRES_DBNAME"],
59
59
  os.environ["POSTGRES_HOST"],
60
60
  os.environ["POSTGRES_PORT"],
61
61
  os.environ["POSTGRES_USER"],
62
62
  os.environ["POSTGRES_PASSWORD"],
63
- ) as datastore:
64
- drop_postgres_table(datastore, "public.searchablecontentapplication_events")
65
- drop_postgres_table(datastore, "public.pages_projection_example")
63
+ )
64
+ drop_postgres_table(db, "public.searchablecontentapplication_events")
65
+ drop_postgres_table(db, "public.pages_projection_example")
66
+ db.close()
66
67
 
67
68
 
68
69
  del SearchableContentRecorderTestCase
@@ -76,19 +76,16 @@ class WithPostgreSQL(SearchableTimestampsTestCase):
76
76
  super().tearDown()
77
77
 
78
78
  def drop_tables(self) -> None:
79
- with PostgresDatastore(
79
+ db = PostgresDatastore(
80
80
  os.environ["POSTGRES_DBNAME"],
81
81
  os.environ["POSTGRES_HOST"],
82
82
  os.environ["POSTGRES_PORT"],
83
83
  os.environ["POSTGRES_USER"],
84
84
  os.environ["POSTGRES_PASSWORD"],
85
- ) as datastore:
86
- drop_postgres_table(
87
- datastore, "public.searchabletimestampsapplication_events"
88
- )
89
- drop_postgres_table(
90
- datastore, "public.searchabletimestampsapplication_timestamps"
91
- )
85
+ )
86
+ drop_postgres_table(db, "public.searchabletimestampsapplication_events")
87
+ drop_postgres_table(db, "public.searchabletimestampsapplication_timestamps")
88
+ db.close()
92
89
 
93
90
 
94
91
  del SearchableTimestampsTestCase
eventsourcing/postgres.py CHANGED
@@ -2,14 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  import logging
4
4
  from contextlib import contextmanager
5
- from typing import TYPE_CHECKING, Any, Callable, Iterator, List, Sequence
5
+ from typing import TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Sequence
6
6
 
7
7
  import psycopg
8
8
  import psycopg.errors
9
9
  import psycopg_pool
10
10
  from psycopg import Connection, Cursor
11
11
  from psycopg.rows import DictRow, dict_row
12
- from typing_extensions import Self
13
12
 
14
13
  from eventsourcing.persistence import (
15
14
  AggregateRecorder,
@@ -63,11 +62,11 @@ class PostgresDatastore:
63
62
  user: str,
64
63
  password: str,
65
64
  *,
66
- connect_timeout: int = 30,
65
+ connect_timeout: int = 5,
67
66
  idle_in_transaction_session_timeout: int = 0,
68
67
  pool_size: int = 2,
69
68
  max_overflow: int = 2,
70
- max_waiting: int = 0,
69
+ pool_timeout: float = 5.0,
71
70
  conn_max_age: float = 60 * 60.0,
72
71
  pre_ping: bool = False,
73
72
  lock_timeout: int = 0,
@@ -80,6 +79,7 @@ class PostgresDatastore:
80
79
  self.pool_open_timeout = pool_open_timeout
81
80
 
82
81
  check = ConnectionPool.check_connection if pre_ping else None
82
+ kwargs: Dict[str, Any] = {"check": check}
83
83
  self.pool = ConnectionPool(
84
84
  get_password_func=get_password_func,
85
85
  connection_class=Connection[DictRow],
@@ -96,9 +96,9 @@ class PostgresDatastore:
96
96
  open=False,
97
97
  configure=self.after_connect,
98
98
  timeout=connect_timeout,
99
- max_waiting=max_waiting,
99
+ max_waiting=round(pool_timeout),
100
100
  max_lifetime=conn_max_age,
101
- check=check,
101
+ **kwargs, # use the 'check' argument when no longer supporting Python 3.7
102
102
  )
103
103
  self.lock_timeout = lock_timeout
104
104
  self.schema = schema.strip()
@@ -156,12 +156,6 @@ class PostgresDatastore:
156
156
  def __del__(self) -> None:
157
157
  self.close()
158
158
 
159
- def __enter__(self) -> Self:
160
- return self
161
-
162
- def __exit__(self, *args: object, **kwargs: Any) -> None:
163
- self.close()
164
-
165
159
 
166
160
  class PostgresAggregateRecorder(AggregateRecorder):
167
161
  def __init__(
@@ -564,10 +558,10 @@ class Factory(InfrastructureFactory):
564
558
  POSTGRES_CONNECT_TIMEOUT = "POSTGRES_CONNECT_TIMEOUT"
565
559
  POSTGRES_CONN_MAX_AGE = "POSTGRES_CONN_MAX_AGE"
566
560
  POSTGRES_PRE_PING = "POSTGRES_PRE_PING"
567
- POSTGRES_MAX_WAITING = "POSTGRES_MAX_WAITING"
561
+ POSTGRES_POOL_TIMEOUT = "POSTGRES_POOL_TIMEOUT"
568
562
  POSTGRES_LOCK_TIMEOUT = "POSTGRES_LOCK_TIMEOUT"
569
563
  POSTGRES_POOL_SIZE = "POSTGRES_POOL_SIZE"
570
- POSTGRES_MAX_OVERFLOW = "POSTGRES_MAX_OVERFLOW"
564
+ POSTGRES_POOL_MAX_OVERFLOW = "POSTGRES_POOL_MAX_OVERFLOW"
571
565
  POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT = (
572
566
  "POSTGRES_IDLE_IN_TRANSACTION_SESSION_TIMEOUT"
573
567
  )
@@ -624,7 +618,7 @@ class Factory(InfrastructureFactory):
624
618
  get_password_func = resolve_topic(get_password_topic)
625
619
  password = ""
626
620
 
627
- connect_timeout = 30
621
+ connect_timeout = 5
628
622
  connect_timeout_str = self.env.get(self.POSTGRES_CONNECT_TIMEOUT)
629
623
  if connect_timeout_str:
630
624
  try:
@@ -670,30 +664,30 @@ class Factory(InfrastructureFactory):
670
664
  raise OSError(msg) from None
671
665
 
672
666
  pool_max_overflow = 10
673
- pool_max_overflow_str = self.env.get(self.POSTGRES_MAX_OVERFLOW)
667
+ pool_max_overflow_str = self.env.get(self.POSTGRES_POOL_MAX_OVERFLOW)
674
668
  if pool_max_overflow_str:
675
669
  try:
676
670
  pool_max_overflow = int(pool_max_overflow_str)
677
671
  except ValueError:
678
672
  msg = (
679
673
  "Postgres environment value for key "
680
- f"'{self.POSTGRES_MAX_OVERFLOW}' is invalid. "
674
+ f"'{self.POSTGRES_POOL_MAX_OVERFLOW}' is invalid. "
681
675
  "If set, an integer or empty string is expected: "
682
676
  f"'{pool_max_overflow_str}'"
683
677
  )
684
678
  raise OSError(msg) from None
685
679
 
686
- max_waiting = 0
687
- max_waiting_str = self.env.get(self.POSTGRES_MAX_WAITING)
688
- if max_waiting_str:
680
+ pool_timeout = 30.0
681
+ pool_timeout_str = self.env.get(self.POSTGRES_POOL_TIMEOUT)
682
+ if pool_timeout_str:
689
683
  try:
690
- max_waiting = int(max_waiting_str)
684
+ pool_timeout = float(pool_timeout_str)
691
685
  except ValueError:
692
686
  msg = (
693
687
  "Postgres environment value for key "
694
- f"'{self.POSTGRES_MAX_WAITING}' is invalid. "
695
- "If set, an integer or empty string is expected: "
696
- f"'{max_waiting_str}'"
688
+ f"'{self.POSTGRES_POOL_TIMEOUT}' is invalid. "
689
+ "If set, a float or empty string is expected: "
690
+ f"'{pool_timeout_str}'"
697
691
  )
698
692
  raise OSError(msg) from None
699
693
 
@@ -739,16 +733,13 @@ class Factory(InfrastructureFactory):
739
733
  idle_in_transaction_session_timeout=idle_in_transaction_session_timeout,
740
734
  pool_size=pool_size,
741
735
  max_overflow=pool_max_overflow,
742
- max_waiting=max_waiting,
736
+ pool_timeout=pool_timeout,
743
737
  conn_max_age=conn_max_age,
744
738
  pre_ping=pre_ping,
745
739
  lock_timeout=lock_timeout,
746
740
  schema=schema,
747
741
  )
748
742
 
749
- def env_create_table(self) -> bool:
750
- return strtobool(self.env.get(self.CREATE_TABLE) or "yes")
751
-
752
743
  def aggregate_recorder(self, purpose: str = "events") -> AggregateRecorder:
753
744
  prefix = self.env.name.lower() or "stored"
754
745
  events_table_name = prefix + "_" + purpose
@@ -792,6 +783,9 @@ class Factory(InfrastructureFactory):
792
783
  recorder.create_table()
793
784
  return recorder
794
785
 
786
+ def env_create_table(self) -> bool:
787
+ return strtobool(self.env.get(self.CREATE_TABLE) or "yes")
788
+
795
789
  def close(self) -> None:
796
790
  if hasattr(self, "datastore"):
797
791
  self.datastore.close()
eventsourcing/system.py CHANGED
@@ -8,7 +8,6 @@ from queue import Full, Queue
8
8
  from threading import Event, Lock, RLock, Thread
9
9
  from types import FrameType, ModuleType
10
10
  from typing import (
11
- Any,
12
11
  ClassVar,
13
12
  Dict,
14
13
  Iterable,
@@ -22,8 +21,6 @@ from typing import (
22
21
  cast,
23
22
  )
24
23
 
25
- from typing_extensions import Self
26
-
27
24
  from eventsourcing.application import (
28
25
  Application,
29
26
  NotificationLog,
@@ -528,13 +525,6 @@ class SingleThreadedRunner(Runner, RecordingEventReceiver):
528
525
  assert isinstance(app, cls)
529
526
  return app
530
527
 
531
- def __enter__(self) -> Self:
532
- self.start()
533
- return self
534
-
535
- def __exit__(self, *args: object, **kwargs: Any) -> None:
536
- self.stop()
537
-
538
528
 
539
529
  class NewSingleThreadedRunner(Runner, RecordingEventReceiver):
540
530
  """