Skip to content

Commit ebd97fd

Browse files
committed
Adapt RangeDistributorAdapter for AOP providers and FP factories
1 parent 295fcc8 commit ebd97fd

1 file changed

Lines changed: 33 additions & 21 deletions

File tree

ascetic_ddd/faker/domain/distributors/o2m/range_distributor_adapter.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import asyncio
12
import typing
23

34
from ascetic_ddd.faker.domain.distributors.m2o.interfaces import IM2ODistributor, IM2ODistributorFactory
@@ -42,7 +43,9 @@ class RangeDistributorAdapter(IM2ODistributor[T], typing.Generic[T]):
4243
"""
4344
_distributor: IO2MDistributor
4445
_values: dict[int, T]
46+
_next_position: int
4547
_provider_name: str | None
48+
_lock: asyncio.Lock
4649

4750
def __init__(self, distributor: IO2MDistributor):
4851
"""
@@ -51,7 +54,9 @@ def __init__(self, distributor: IO2MDistributor):
5154
"""
5255
self._distributor = distributor
5356
self._values = {}
57+
self._next_position = 0
5458
self._provider_name = None
59+
self._lock = asyncio.Lock()
5560

5661
async def next(
5762
self,
@@ -61,41 +66,48 @@ async def next(
6166
"""
6267
Returns a value from the dictionary by a random number.
6368
69+
If the value is not found, acquires lock and raises Cursor.
70+
The lock is held until cursor.append() is called, serializing
71+
the creation path to guarantee value ordering.
72+
Portable to Go (mutex.Lock in Next, mutex.Unlock in Append).
73+
6474
Raises:
6575
Cursor(): if there is no value in the dictionary for the slot.
66-
cursor.position -- the slot number.
67-
cursor.append(session, value) -- add a value.
76+
cursor.append(session, value) -- add a value and release lock.
6877
"""
6978
num = self._distributor.distribute()
7079

71-
if num not in self._values:
72-
raise Cursor(
73-
position=num,
74-
callback=self._append,
75-
)
76-
77-
value = self._values[num]
78-
79-
# Check specification
80-
if not await specification.is_satisfied_by(session, value):
81-
# Value does not satisfy the specification -- try again
82-
return await self.next(session, specification)
83-
84-
return Some(value)
80+
if num in self._values:
81+
value = self._values[num]
82+
# Check specification
83+
if not await specification.is_satisfied_by(session, value):
84+
# Value does not satisfy the specification -- try again
85+
return await self.next(session, specification)
86+
return Some(value)
87+
88+
# Creation path — acquire lock, hold until _locked_append
89+
await self._lock.acquire()
90+
raise Cursor(
91+
position=len(self._values),
92+
callback=self._locked_append,
93+
)
8594

86-
async def _append(self, session: ISession, value: T, position: int):
95+
async def _locked_append(self, session: ISession, value: T, position: int):
8796
"""
88-
Adds a value to the dictionary.
97+
Adds a value to the dictionary and releases the lock.
8998
9099
Args:
91100
session: Session
92101
value: Value to add
93-
position: Slot number (key in the dictionary).
102+
position: Sequential position (key in the dictionary).
94103
"""
95-
self._values[position] = value
104+
try:
105+
self._values[position] = value
106+
finally:
107+
self._lock.release()
96108

97109
async def append(self, session: ISession, value: T):
98-
await self._append(session, value, -1)
110+
self._values[len(self._values)] = value
99111

100112
@property
101113
def provider_name(self) -> str | None:

0 commit comments

Comments
 (0)