moat-lib-ring 0.1.0__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.
@@ -0,0 +1,14 @@
1
+ The code in this repository, and all MoaT submodules it refers to,
2
+ is part of the MoaT project.
3
+
4
+ Unless a submodule's LICENSE.txt states otherwise, all included files are
5
+ licensed under the LGPL V3, as published by the FSF at
6
+ https://www.gnu.org/licenses/lgpl-3.0.html .
7
+
8
+ In addition to the LGPL's terms, the author(s) respectfully ask all users of
9
+ this code to contribute any bug fixes or enhancements. Also, please link back to
10
+ https://M-o-a-T.org.
11
+
12
+ Thank you.
13
+
14
+ Copyright © 2021 ff.: the MoaT contributor(s), as per the git changelog(s).
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/make -f
2
+
3
+ PACKAGE = moat-lib-ring
4
+ MAKEINCL ?= $(shell python3 -mmoat src path)/make/py
5
+
6
+ ifneq ($(wildcard $(MAKEINCL)),)
7
+ include $(MAKEINCL)
8
+ # availabe via http://github.com/smurfix/sourcemgr
9
+
10
+ else
11
+ %:
12
+ @echo "Please fix 'python3 -mmoat src path'."
13
+ @exit 1
14
+ endif
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: moat-lib-ring
3
+ Version: 0.1.0
4
+ Summary: A character-based ring buffer
5
+ Maintainer-email: Matthias Urlichs <matthias@urlichs.de>
6
+ Project-URL: homepage, https://m-o-a-t.org
7
+ Project-URL: repository, https://github.com/M-o-a-T/moat
8
+ Keywords: MoaT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Framework :: AnyIO
11
+ Classifier: Framework :: Trio
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Intended Audience :: Developers
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE.txt
18
+ Requires-Dist: anyio~=4.0
19
+ Dynamic: license-file
20
+
21
+
22
+ # moat-lib-ring
23
+ A simple opinionated character-based ring buffer.
24
+
25
+ This buffer, well, buffers. It's async compatible and can handle writes
26
+ overruns either by delaying the writer or by only keeping the newest data.
27
+
28
+ Usage is very simple:
29
+
30
+ ```python
31
+ from moat.lib.ring import RingBuffer
32
+
33
+ # Create buffer
34
+ ring = RingBuffer(200)
35
+
36
+ # write to it
37
+ nbytes = ring.write(b'Hello', drop=True)
38
+ assert nbytes == 5
39
+
40
+ # read from it
41
+ buf = bytearray(2)
42
+ assert ring.readinto(buf) == 2
43
+ assert buf == 'He'
44
+
45
+ ```
46
+
47
+ ## Overflow handling
48
+
49
+ If you set `drop=True`, which is the default, then writing to the buffer
50
+ will always succeed and return the length of the bytestring.
51
+ The last bufsize-1 bytes will be preserved. The first byte you read will be
52
+ a null byte, to signal that data was lost.
53
+
54
+ `drop=False` means that writing will stall instead of destroying data: `write`
55
+ will return a smaller length than requested. The caller is responsible for
56
+ waiting until there is free space.
57
+
58
+
59
+ ## Locking
60
+
61
+ This code doesn't know if it's called from a thread or not.
62
+ If required, please add your own.
63
+
64
+
65
+ ## Async operation
66
+
67
+ `moat.lib.ring.aio` provides an async version of this code.
68
+
69
+ In the async version, writing always waits until all bytes have been
70
+ delivered to the buffer.
71
+
72
+
73
+ # Threaded operation
74
+
75
+ TODO. Requires a lock and thread-based events (or conditions).
@@ -0,0 +1,55 @@
1
+
2
+ # moat-lib-ring
3
+ A simple opinionated character-based ring buffer.
4
+
5
+ This buffer, well, buffers. It's async compatible and can handle writes
6
+ overruns either by delaying the writer or by only keeping the newest data.
7
+
8
+ Usage is very simple:
9
+
10
+ ```python
11
+ from moat.lib.ring import RingBuffer
12
+
13
+ # Create buffer
14
+ ring = RingBuffer(200)
15
+
16
+ # write to it
17
+ nbytes = ring.write(b'Hello', drop=True)
18
+ assert nbytes == 5
19
+
20
+ # read from it
21
+ buf = bytearray(2)
22
+ assert ring.readinto(buf) == 2
23
+ assert buf == 'He'
24
+
25
+ ```
26
+
27
+ ## Overflow handling
28
+
29
+ If you set `drop=True`, which is the default, then writing to the buffer
30
+ will always succeed and return the length of the bytestring.
31
+ The last bufsize-1 bytes will be preserved. The first byte you read will be
32
+ a null byte, to signal that data was lost.
33
+
34
+ `drop=False` means that writing will stall instead of destroying data: `write`
35
+ will return a smaller length than requested. The caller is responsible for
36
+ waiting until there is free space.
37
+
38
+
39
+ ## Locking
40
+
41
+ This code doesn't know if it's called from a thread or not.
42
+ If required, please add your own.
43
+
44
+
45
+ ## Async operation
46
+
47
+ `moat.lib.ring.aio` provides an async version of this code.
48
+
49
+ In the async version, writing always waits until all bytes have been
50
+ delivered to the buffer.
51
+
52
+
53
+ # Threaded operation
54
+
55
+ TODO. Requires a lock and thread-based events (or conditions).
@@ -0,0 +1,7 @@
1
+ /files
2
+ /*.log
3
+ /*.debhelper
4
+ /*.debhelper-build-stamp
5
+ /*.substvars
6
+ /debhelper-build-stamp
7
+ /python3-moat-lib-ring
@@ -0,0 +1,11 @@
1
+ moat-lib-ring (0.1.0-4) unstable; urgency=medium
2
+
3
+ * New release for 25.6.6
4
+
5
+ -- Matthias Urlichs <matthias@urlichs.de> Wed, 10 Dec 2025 11:29:56 +0100
6
+
7
+ moat-lib-ring (0.1.0-1) unstable; urgency=medium
8
+
9
+ * Initial release.
10
+
11
+ -- Matthias Urlichs <matthias@urlichs.de> Mon, 01 Dec 2025 13:18:41 +0100
@@ -0,0 +1,18 @@
1
+ Source: moat-lib-ring
2
+ Maintainer: "Matthias Urlichs" <matthias@urlichs.de>
3
+ Section: python
4
+ Priority: optional
5
+ Build-Depends: dh-python, python3-all, debhelper (>= 13),
6
+ python3-setuptools,
7
+ python3-wheel,
8
+ Standards-Version: 3.9.6
9
+ Homepage: https://m-o-a-t.org
10
+ X-DH-Compat: 13
11
+
12
+ Package: python3-moat-lib-ring
13
+ Architecture: all
14
+ Depends: ${misc:Depends}, ${python3:Depends},
15
+ python3-anyio (>= 3.0),
16
+ Description: A simple ring buffer
17
+ This is an efficient character-based ring buffer. It can either stall when the buffer
18
+ is full, or keep only the last n-1 bytes (adding a null byte to signal overflow).
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/make -f
2
+
3
+ # This file was automatically generated by toml2deb
4
+ # Thu, 16 Jul 2020 13:15:17 +0200
5
+ export PYBUILD_NAME=moat-lib-ring
6
+ %:
7
+ dh $@ --with python3 --buildsystem=pybuild
8
+
9
+ override_dh_usrlocal:
10
+ if test -d debian/python3-moat-lib-ring/usr/local/bin/ ; then \
11
+ mkdir -p debian/python3-moat-lib-ring/usr/bin; \
12
+ mv debian/python3-moat-lib-ring/usr/local/bin/* debian/python3-moat-lib-ring/usr/bin; \
13
+ fi
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ build-backend = "setuptools.build_meta"
3
+ requires = ["wheel","setuptools"]
4
+
5
+ [project]
6
+ classifiers = [
7
+ "Development Status :: 4 - Beta",
8
+ "Framework :: AnyIO",
9
+ "Framework :: Trio",
10
+ "Framework :: AsyncIO",
11
+ "Programming Language :: Python :: 3",
12
+ "Intended Audience :: Developers",
13
+ ]
14
+ dependencies = [
15
+ "anyio ~= 4.0",
16
+ ]
17
+ keywords = ["MoaT"]
18
+ requires-python = ">=3.8"
19
+ name = "moat-lib-ring"
20
+ maintainers = [{email = "matthias@urlichs.de",name = "Matthias Urlichs"}]
21
+ description='A character-based ring buffer'
22
+ readme = "README.md"
23
+ version = "0.1.0"
24
+
25
+ [project.urls]
26
+ homepage = "https://m-o-a-t.org"
27
+ repository = "https://github.com/M-o-a-T/moat"
28
+
29
+ [tool.setuptools]
30
+ [tool.setuptools.packages.find]
31
+ where = ["src"]
32
+
33
+ [tool.setuptools.package-data]
34
+ "*" = ["*.yaml"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ # noqa:D104
2
+ from __future__ import annotations
3
+
4
+ from .ring import RingBuffer as RingBuffer
@@ -0,0 +1,66 @@
1
+ """
2
+ Async front-end for the characer-based ring buffer.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from moat.lib.compat import Event
8
+
9
+ from .ring import RingBuffer as _RingBuf
10
+
11
+
12
+ class RingBuffer(_RingBuf):
13
+ """
14
+ This ring buffer can hold a predetermined number of bytes.
15
+
16
+ This is the async version.
17
+ """
18
+
19
+ _w_evt: Event | None = None
20
+ _r_evt: Event | None = None
21
+
22
+ def __init__(self, length: int):
23
+ super().__init__(length)
24
+
25
+ async def write(self, buf: bytes) -> int:
26
+ """
27
+ Adds the bytes in `buf` to the end of the buffer.
28
+ """
29
+ if not buf:
30
+ return 0
31
+
32
+ buf_len = len(buf)
33
+ n = 0
34
+
35
+ while True:
36
+ nb = super().write(buf, drop=False)
37
+ n += nb
38
+ if self._r_evt is not None:
39
+ self._r_evt.set()
40
+ self._r_evt = None
41
+ if n == buf_len:
42
+ return buf_len
43
+ buf = memoryview(buf)[nb:]
44
+ if self._w_evt is None:
45
+ self._w_evt = Event()
46
+ await self._w_evt.wait()
47
+
48
+ async def readinto(self, buf: bytearray) -> int:
49
+ """
50
+ Copies as many bytes as will fit (or are available, whichever is
51
+ smaller) into the buffer and advance the read counter.
52
+ """
53
+ if len(buf) == 0:
54
+ return 0
55
+ n = super().readinto(buf)
56
+ while n == 0:
57
+ if self._r_evt is None:
58
+ self._r_evt = Event()
59
+ await self._r_evt.wait()
60
+ n = super().readinto(buf)
61
+
62
+ if self._w_evt is not None:
63
+ self._w_evt.set()
64
+ self._w_evt = None
65
+
66
+ return n
@@ -0,0 +1,108 @@
1
+ """
2
+ Characer-based ring buffer.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+
8
+ class RingBuffer:
9
+ """
10
+ This ring buffer can hold a predetermined number of bytes.
11
+
12
+ Old data are overwritten when the reader doesn't keep up.
13
+ """
14
+
15
+ def __init__(self, length: int):
16
+ """
17
+ Sets up a ring buffer of size `len` (i.e. holds zero to len-1 bytes).
18
+ """
19
+ self._buf = bytearray(length)
20
+ self._read_pos = 0 # Position to read from
21
+ self._count = 0 # Number of bytes available
22
+
23
+ def n_free(self):
24
+ "free space in buffer"
25
+ return len(self._buf) - self._count
26
+
27
+ def n_avail(self):
28
+ "bytes in buffer"
29
+ return self._count
30
+
31
+ __len__ = n_avail
32
+
33
+ def __repr__(self):
34
+ return f"<Ring:{self._count}/{len(self._buf)}>"
35
+
36
+ def write(self, buf: bytes, drop: bool = True) -> int:
37
+ """
38
+ Adds the bytes in `buf` to the end of the buffer.
39
+
40
+ If the buffer is full and `drop` is set (the default), discards as
41
+ many old bytes as necessary and replaces the first byte with a
42
+ 0x00. Otherwise only write as much as will fit.
43
+ """
44
+ if not buf:
45
+ return 0
46
+
47
+ buf_len = len(self._buf)
48
+ res = write_len = len(buf)
49
+
50
+ # If writing more than buffer can hold, only write last buf_len bytes
51
+ if drop and write_len > buf_len:
52
+ buf = memoryview(buf)[write_len - buf_len :]
53
+ write_len = buf_len
54
+
55
+ # Check if we'll overflow
56
+ overflow = max(0, self._count + write_len - buf_len)
57
+ if overflow > 0:
58
+ # Advance read position to discard old bytes
59
+ if drop:
60
+ self._read_pos = (self._read_pos + overflow) % buf_len
61
+ self._count -= overflow
62
+ else:
63
+ res = write_len = buf_len - self._count
64
+ overflow = 0
65
+ buf = memoryview(buf)[:write_len]
66
+
67
+ # Write data, handling wraparound
68
+ write_pos = (self._read_pos + self._count) % buf_len
69
+ space_to_end = buf_len - write_pos
70
+ if write_len <= space_to_end:
71
+ # Simple case: no wraparound
72
+ self._buf[write_pos : write_pos + write_len] = buf
73
+ else:
74
+ # Wraparound case: split the write
75
+ self._buf[write_pos:buf_len] = buf[:space_to_end]
76
+ self._buf[0 : write_len - space_to_end] = buf[space_to_end:]
77
+
78
+ self._count += write_len
79
+
80
+ # Replace the oldest byte with 0x00 if we had overflow
81
+ if overflow > 0:
82
+ self._buf[self._read_pos] = 0x00
83
+
84
+ return res
85
+
86
+ def readinto(self, buf: bytearray) -> int:
87
+ """
88
+ Copies as many bytes as will fit (or are available, whichever is
89
+ smaller) into the buffer and advance the read counter.
90
+ """
91
+ read_len = min(len(buf), self._count)
92
+ if read_len == 0:
93
+ return 0
94
+
95
+ buf_len = len(self._buf)
96
+ space_to_end = buf_len - self._read_pos
97
+
98
+ if read_len <= space_to_end:
99
+ # Simple case: no wraparound
100
+ buf[:read_len] = self._buf[self._read_pos : self._read_pos + read_len]
101
+ else:
102
+ # Wraparound case: split the read
103
+ buf[:space_to_end] = self._buf[self._read_pos : buf_len]
104
+ buf[space_to_end:read_len] = self._buf[0 : read_len - space_to_end]
105
+
106
+ self._read_pos = (self._read_pos + read_len) % buf_len
107
+ self._count -= read_len
108
+ return read_len
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: moat-lib-ring
3
+ Version: 0.1.0
4
+ Summary: A character-based ring buffer
5
+ Maintainer-email: Matthias Urlichs <matthias@urlichs.de>
6
+ Project-URL: homepage, https://m-o-a-t.org
7
+ Project-URL: repository, https://github.com/M-o-a-T/moat
8
+ Keywords: MoaT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Framework :: AnyIO
11
+ Classifier: Framework :: Trio
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Intended Audience :: Developers
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE.txt
18
+ Requires-Dist: anyio~=4.0
19
+ Dynamic: license-file
20
+
21
+
22
+ # moat-lib-ring
23
+ A simple opinionated character-based ring buffer.
24
+
25
+ This buffer, well, buffers. It's async compatible and can handle writes
26
+ overruns either by delaying the writer or by only keeping the newest data.
27
+
28
+ Usage is very simple:
29
+
30
+ ```python
31
+ from moat.lib.ring import RingBuffer
32
+
33
+ # Create buffer
34
+ ring = RingBuffer(200)
35
+
36
+ # write to it
37
+ nbytes = ring.write(b'Hello', drop=True)
38
+ assert nbytes == 5
39
+
40
+ # read from it
41
+ buf = bytearray(2)
42
+ assert ring.readinto(buf) == 2
43
+ assert buf == 'He'
44
+
45
+ ```
46
+
47
+ ## Overflow handling
48
+
49
+ If you set `drop=True`, which is the default, then writing to the buffer
50
+ will always succeed and return the length of the bytestring.
51
+ The last bufsize-1 bytes will be preserved. The first byte you read will be
52
+ a null byte, to signal that data was lost.
53
+
54
+ `drop=False` means that writing will stall instead of destroying data: `write`
55
+ will return a smaller length than requested. The caller is responsible for
56
+ waiting until there is free space.
57
+
58
+
59
+ ## Locking
60
+
61
+ This code doesn't know if it's called from a thread or not.
62
+ If required, please add your own.
63
+
64
+
65
+ ## Async operation
66
+
67
+ `moat.lib.ring.aio` provides an async version of this code.
68
+
69
+ In the async version, writing always waits until all bytes have been
70
+ delivered to the buffer.
71
+
72
+
73
+ # Threaded operation
74
+
75
+ TODO. Requires a lock and thread-based events (or conditions).
@@ -0,0 +1,16 @@
1
+ LICENSE.txt
2
+ Makefile
3
+ README.md
4
+ pyproject.toml
5
+ debian/.gitignore
6
+ debian/changelog
7
+ debian/control
8
+ debian/rules
9
+ src/moat/lib/ring/__init__.py
10
+ src/moat/lib/ring/aio.py
11
+ src/moat/lib/ring/ring.py
12
+ src/moat_lib_ring.egg-info/PKG-INFO
13
+ src/moat_lib_ring.egg-info/SOURCES.txt
14
+ src/moat_lib_ring.egg-info/dependency_links.txt
15
+ src/moat_lib_ring.egg-info/requires.txt
16
+ src/moat_lib_ring.egg-info/top_level.txt