Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Commit messages are consistent with Conventional Commits.
0.11.0 - 2025-12-31
Added
culsans.__version__andculsans.__version_tuple__as a way to retrieve the package version at runtime.culsans.Grouper(initial implementation) as a higher-level synchronization primitive with phase-fair priority policy by default. See x42005e1f/aiologic#19 for details.culsans.RWLock(initial implementation) as a readers-writer lock that is both phase-fair and reentrant. It is currently defined as a subclass ofculsans.Grouper, but this may change in the future.culsans.BaseQueueProxyas a common superclass forculsans.SyncQueueProxyandculsans.AsyncQueueProxy. Added to reduce code duplication, but can also be used separately for other purposes (such as checking that an object is an instance of either proxy type).culsans.GreenQueue,culsans.GreenQueueProxy, and methods prefixed withgreen_, which make the interfacesaiologic-like. They are equivalent to their “sync” alternatives and are preferred for new uses.blockingparameter wherever there isblockparameter, as its alias. This change complements the previous one. Note that none of these parameters have been added to async def methods, as this would complicate the implementation, so there is still no full compatibility withaiologicqueues (the parameters will be added in the future along with async timeouts).sizerparameter, correspondingisize()public methods, and_isizeprotected attribute/method (for overriding). Allows to specify the size for each queue item, which affects the put methods. Solves #9.clearable()methods and relatedculsans.Queue._clearable()protected method (for overriding) analogous to those for peek methods, making implementation of theclear()method optional. Along with this, theclear()method is now also used in the implementation of theshutdown()method, when available.sizeproperty, which returns the cumulative size of items in the queue.green_proxy/async_proxyproperties as a more generic alternative tosync_q/async_q.The proxies can now be weakly referenced. Previously, this was disallowed due to their limited lifetime (since the corresponding properties return new objects on each access). This is now allowed for cases where a proxy is used as a backport to older versions of Python.
A negative queue length is now a valid value and is handled correctly (extends subclassing capabilities).
A negative value of the
unfinished_tasksproperty is now treated as infinity (extends subclassing capabilities).
Changed
The queues now override
__len__()and__bool__(), allowinglen(queue)andbool(queue)to be used in the same sense as for collections. And while this change is not conceptually incompatible, in fact it breaks the not-so-good patternif self.queue: # self.queue is not None.The
maxsizeproperty is now defined as the maximum cumulative size of queue items rather than the maximum queue length. This allows arbitrary queue item sizes to be supported, but may require existing code to be modified to work correctly when passing a custom sizer (as this affects both the put methods and thefull()methods).The underlying lock is now reentrant. This differs from the standard queue approach, but makes it much easier to create subclasses and also makes
culsans.Queuesomewhat compatible withsqlalchemy.util.queue.Queue. However, the main reason is not this, but to make the following change efficiently implemented.The queues now rely on new
aiologicsafety guarantees when using the condition variables. Previously, as withthreading.Condition, aKeyboardInterruptraised during synchronization on the underlying lock after notification led to the lock being over-released and, as a result, to aRuntimeError. Now,aiologic.Conditionis used as a context manager, thereby including additional checks on theaiologicside to ensure that the current thread owns the lock when releasing it.The
unfinished_tasksproperty’s value now changes according to the queue length change (the value returned by the_qsize()method). This allows to create flattened queues without introducing related additional methods and also corrects the behavior for subclasses that may not change the queue length as a result of insertion.The
timeoutparameter’s value is now checked and converted at the beginning of the method call, regardless of theblockparameter’s value. This should prevent cases where an incorrect value is passed.The peekability check is now ensured before each call (after each context switch), allowing the peek methods to be enabled/disabled dynamically.
The package now relies on
aiologic.meta.export()for exports instead of using its own implementation (similar toaiologic==0.15.0), which provides safer behavior. In particular, queue methods now also update their metadata, allowing them to be safely referenced during pickling.The protocols are now inherited from
typing_extensions.Protocolon Python below 3.13, which backports all related fixes and improvements to older versions of Python.The shutdown exceptions are now defined again via backports (on Python below 3.13), but in a type-friendly manner.
Fixed
The sync methods called the checkpoint function regardless of the
blockparameter’s value. Now they do not make the call in the non-blocking case.Notifications could be insufficient or excessive when the queue length changed differently than in the standard behavior. Now such situations are detected and a different notification mechanism is activated when they are detected.
0.10.0 - 2025-11-04
Added
culsans.Queue.waitingas the thirdaiologic-like property. Useful when you need to reliably obtain the total number of waiting ones.Timeout handling has been improved in line with the latest changes in
aiologic(support for very large numbers, additional checking forNaN, use of the clock functions fromaiologic.lowlevel). This is particularly relevant foraiologic>=0.15.0, which implements safe timeouts, but has almost no impact on older versions.The properties of
culsans.Queuenow return proxies instead of protocols for type checkers. This allows, for example, accessing the wrapped queue via the proxy attribute without type errors.The proxies are now represented in a readable format, which should make debugging a little easier.
Changed
The priority queues now use stricter bounds for the type variable: collection elements must be rich comparable (implement
__lt__()or__gt__()method). This corresponds to recent changes intypeshedfor the standard queues (see python/typeshed#14418) and makes them safer.
Fixed
For the underlying lock,
aiologic.lowlevel._threadwas used, which has been declared deprecated in the latest version ofaiologic.With green checkpoints enabled, the end time was recalculated for the timeout after rescheduling, which could lead to doubling the actual wait time (
0.9.0regression).
0.9.0 - 2025-07-16
Changed
Checkpoint functions are now always called before queue operations are performed (previously they were called after). This ensures that all queue methods behave as expected on cancellations, but it may increase the number of explicit context switches.
The build system has been changed from
setuptoolstouv+hatch. It keeps the samepyproject.tomlformat, but has better performance, better logging, and builds cleaner source distributions (withoutsetup.cfg).The version identifier is now generated dynamically and includes the latest commit information for development versions, which simplifies bug reporting. It is also passed to archives generated by GitHub (via
.git_archival.txt) and source distributions (viaPKG-INFO).
Fixed
For asynchronous checkpoints,
aiologic.lowlevel.checkpoint()was used, which has been declared deprecated in the latest version ofaiologic.With checkpoints enabled (
triocase by default), the get methods could lose items on cancellations, and for the put methods, it was impossible to determine whether the put was successful or not on the same cancellations (0.2.1regression).
0.8.0 - 2025-01-19
Added
Python 3.8 support. This makes the list of supported versions consistent with that of
aiologic. Previously, the lowest supported version was 3.9.culsans.MixedQueueas a queue protocol that provides both types of blocking methods via prefixes. Can be used to generalizeculsans.Queueand its derivatives by type checkers, as it does not includejanus.Queue-specific and other foreign methods and properties/attributes.
Changed
Interfaces and type hints have been improved:
Now
aiologic>=0.13.0is used (previously>=0.12.0was used), which provides type annotations. This removes mypy-specific type ignores.All fields are annotated. A side effect is that the queue subclasses now define their own slot for storing data.
The source code tree has been significantly changed for better IDEs support. The same applies to exports, which are now checker-friendly.
0.7.1 - 2024-12-23
Fixed
For
culsans.QueueShutDownon Python below 3.13, backported exceptions were defined, but they caused name conflicts for type checkers. Now, all queue shutdown exceptions reference the same class (on Python below 3.13).
0.7.0 - 2024-12-23
Added
culsans.QueueEmpty,culsans.QueueFull, andculsans.QueueShutDownas exceptions compatible with any interface (they inherit both types). They are now also raised instead of specialized exceptions, allowing any type to be used in try-catch.The remaining non-blocking methods and both types of blocking methods to
culsans.Queuevia prefixes (sync_andasync_). The main reason for this is to move the entire implementation from the proxy classes to the queue classes. However, they can also be used as a simpler, shorter style of working with queues (similar toaiologic, but withsync_instead ofgreen_).culsans.Queue.puttingandculsans.Queue.gettingasaiologic-like properties. They can be used to obtain the number of waiting ones for a given operation type. But note,culsans.Queue.gettingalso includes the number of peekers.culsans.Queuenow inherits fromculsans.BaseQueueinstead ofGeneric. This improves type safety and allows introspection at runtime.Direct access (without the
_prefix) to the synchronization primitives used, to better mimic thequeuemodule.
0.6.0 - 2024-12-14
Changed
The behavior of Janus-specific methods now corresponds to
janus>=2.0.0(instead of1.2.0). The only change is that after callingculsans.Queue.close(),*QueueShutDownis now raised instead ofRuntimeError.
0.5.0 - 2024-12-14
Added
culsans.Queue.aclose()method, which replicates the behavior of the same-named Janus method. This makes the interface compliant with the Janus API version 1.2.0 (instead of 1.1.0).
0.4.0 - 2024-11-17
Added
peekable()methods and relatedculsans.Queue._peekable()protected method (for overriding). They simplify non-peekable subclass creation by providing a unified contract for how to deal with it from the user’s side.culsans.UnsupportedOperationexception, which is raised when attempting to call any of the peek methods for a non-peekable queue.
0.3.0 - 2024-11-08
Added
culsans.Queue.close()andculsans.Queue.wait_closed()methods,culsans.Queue.closedproperty to implement compatibility withjanus>=1.1.0. For the most part, they behave similarly to the originals (including the exception raised after closing), but semantically they are closer to the queue shutdown methods from Python 3.13. This differs from thejanusbehavior, but solves aio-libs/janus#237.peek()andpeek_nowait()methods, relatedculsans.Queue._peek()protected method (for overriding), as a way to retrieve an item from a queue without removing it. In a sense, they implement partial compatibility with thegeventqueues, but peek/front is also a well-known third type of queue operation.clear()method and relatedculsans.Queue._clear()protected method (for overriding). Atomically clears the queue, ensuring that other threads do not affect the clearing process, and updates theunfinished_tasksproperty at the same time. Solves aio-libs/janus#645.
0.2.1 - 2024-11-04
Fixed
Mixed use of both types of methods could lead to deadlocks (due to the use of a shared wait queue for the underlying lock). This could be considered expected behavior if it did not also affect non-blocking methods (due to the blocking use of the condition variables).
0.2.0 - 2024-11-04
Added
culsans.Queue.maxsizecan now be changed dynamically at runtime (growing & shrinking). This allows for more complex logic to be implemented, whereby the maximum queue size is adjusted according to certain external conditions. Related: python/cpython#54319.
Changed
The protocols, and therefore the proxies, no longer include the implicit
__dict__, thereby preventing unknown attributes from being set. This makes them safer.
0.1.0 - 2024-11-02
Added
Janus-like mixed queues and all related API (excluding some Janus-specific methods that seem redundant on Python 3.13, in favor of new methods such as
shutdown()). They use the same patterns as thequeuemodule, but rely on the condition variables fromaiologic. This makes them 4-8 times faster thanjanus.Queuein multi-threaded tests, simplifies usage, and expands the number of supported use cases (multiple event loops,triosupport, etc.).