ok-serial 0.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.
ok_serial-0.1/PKG-INFO ADDED
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.3
2
+ Name: ok-serial
3
+ Version: 0.1
4
+ Summary: Serial port library (PySerial wrapper) with more features
5
+ Author: Dan Egnor
6
+ Author-email: Dan Egnor <egnor@ofb.net>
7
+ Requires-Dist: beartype>=0.22.9
8
+ Requires-Dist: natsort>=8.4.0
9
+ Requires-Dist: ok-logging-setup>=0.11
10
+ Requires-Dist: pyserial>=3.5
11
+ Requires-Dist: types-pyserial>=3.5.0.20251001
12
+ Requires-Python: >=3.10
13
+ Project-URL: Homepage, https://github.com/egnor/ok-py-serial#readme
14
+ Project-URL: Repository, https://github.com/egnor/ok-py-serial.git
15
+ Description-Content-Type: text/markdown
16
+
17
+ # ok-serial for Python &nbsp; ๐Ÿ”Œใ€กใ€‡ใ€กใ€‡ใ€ก๐Ÿ
18
+
19
+ Python serial I/O library [based on PySerial](https://www.pyserial.com/) with
20
+ improved discovery and interface semantics.
21
+
22
+ Think twice before using this library! Consider something more established:
23
+
24
+ - [good old PySerial](https://www.pyserial.com/) - it is _very_ well established
25
+ - [pyserial-asyncio](https://github.com/pyserial/pyserial-asyncio) - official
26
+ and "proper" [asyncio](https://docs.python.org/3/library/asyncio.html)
27
+ support for PySerial
28
+ - [pyserial-asyncio-fast](https://github.com/home-assistant-libs/pyserial-asyncio-fast)
29
+ \- a fork of pyserial-asyncio designed for more efficient writes
30
+ - [aioserial](https://github.com/mrjohannchang/aioserial.py) - alternative
31
+ asyncio wrapper designed for ease of use
32
+
33
+ ## Purpose
34
+
35
+ Since 2001, [PySerial](https://www.pyserial.com/) has been the
36
+ workhorse [serial port](https://en.wikipedia.org/wiki/Serial_port)
37
+ ([UART](https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter))
38
+ library for Python. It runs on most Python platforms and abstracts
39
+ lots of gnarly system details. However, some problems keep coming up:
40
+
41
+ - Most serial ports are USB, and USB serial ports get assigned cryptic
42
+ temporary names like `/dev/ttyACM3` or `COM4`. Using
43
+ [`serial.tools.list_ports.grep(...)`](https://pythonhosted.org/pyserial/tools.html#serial.tools.list_ports.grep)
44
+ is a clumsy multi step process; linux allows
45
+ [udev rules](https://dev.to/enbis/how-udev-rules-can-help-us-to-recognize-a-usb-to-serial-device-over-dev-tty-interface-pbk)
46
+ but they're not exactly user friendly.
47
+
48
+ - Nonblocking or concurrent I/O with PySerial is perilous and often
49
+ [broken](https://github.com/pyserial/pyserial/issues/281)
50
+ [entirely](https://github.com/pyserial/pyserial/issues/280).
51
+
52
+ - Buffer sizes are finite and unspecified; overruns cause lost data
53
+ and/or blocking.
54
+
55
+ - Port locking is off by default in PySerial; even if enabled, it only
56
+ uses one advisory locking method. Bad things happen if multiple programs
57
+ try to use the same port.
58
+
59
+ The `ok-serial` library uses PySerial internally but has its own consistent
60
+ interface to fix these problems and be generally smoove:
61
+
62
+ - Ports are referenced by
63
+ [string expressions](#identifying-ports) that can match attributes
64
+ with wildcard support, eg. `desc:Arduino*` or `2e43:0226` or `*RP2040*`.
65
+ (You can also specify exact device path if desired.)
66
+
67
+ - I/O operations are thread safe and can be blocking, non-blocking,
68
+ timeout, or async. All blocking operations can be interrupted.
69
+ Semantics are well described, including concurrent access, partial
70
+ reads/writes, errors, and other edge cases.
71
+
72
+ - I/O buffers are unlimited except for system memory; writes
73
+ never block. (You can use a blocking drain operation to wait for
74
+ output completion if desired.)
75
+
76
+ - Includes [multiple port locking modes](#sharing-modes) with exclusive locking
77
+ as the default. Employs _all_ of
78
+ [`/var/lock/LCK..*` files](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s09.html),
79
+ [`flock(...)`](https://linux.die.net/man/2/flock) (like PySerial),
80
+ and [`TIOCEXCL`](https://man7.org/linux/man-pages/man2/TIOCEXCL.2const.html)
81
+ (as available) to avoid contention.
82
+
83
+ - Includes a `SerialTracker` helper to wait for a device of interest to
84
+ appear, rescanning as needed after disconnection, to handle devices
85
+ that might get plugged and unplugged.
86
+
87
+ ## Installation
88
+
89
+ ```bash
90
+ pip install ok-serial
91
+ ```
92
+
93
+ (or `uv add ok-serial`, etc.)
94
+
95
+ ## Identifying ports
96
+
97
+ Device names like `/dev/ttyUSB3` or `COM4` aren't very useful for USB
98
+ serial ports, so `ok-serial` uses **port match expressions** which
99
+ are strings that identify ports of interest by attributes such as the
100
+ device manufacturer (eg. `Adafruit`), product name (eg.
101
+ `CP2102 USB to UART Bridge Controller`), USB vendor/product ID (eg.
102
+ `239a:812d`), serial number, or other properties.
103
+
104
+ To see port attributes, install `ok-serial` and run
105
+ `ok_scan_serial --verbose` to list available ports like this:
106
+
107
+ ```text
108
+ Serial port: /dev/ttyACM3
109
+ device: '/dev/ttyACM3'
110
+ name: 'ttyACM3'
111
+ description: 'Feather RP2040 RFM - Pico Serial'
112
+ hwid: 'USB VID:PID=239A:812D SER=DF62585783553434 LOCATION=3-2.1:1.0'
113
+ vid: '9114'
114
+ pid: '33069'
115
+ serial_number: 'DF62585783553434'
116
+ location: '3-2.1:1.0'
117
+ manufacturer: 'Adafruit'
118
+ product: 'Feather RP2040 RFM'
119
+ interface: 'Pico Serial'
120
+ usb_device_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1'
121
+ device_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1/3-2.1:1.0'
122
+ subsystem: 'usb'
123
+ usb_interface_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1/3-2.1:1.0'
124
+ ```
125
+
126
+ Attribute names and value formats are inherited from PySerial and
127
+ the underlying OS and can vary, but
128
+ `device`, `name`, `description`, `hwid`, and (for USB) `vid`, `pid`,
129
+ `serial_number`, `location`, `manufacturer`, `product` and `interface`
130
+ are semi-standardized.
131
+
132
+ Match expressions can be a simple value, selecting any port with a
133
+ matching value (case-insensitive whole-string match):
134
+
135
+ ```text
136
+ Pico Serial
137
+ ```
138
+
139
+ Match values can include `*` and `?` wildcards:
140
+
141
+ ```text
142
+ *RP2040*
143
+ ```
144
+
145
+ Expressions can include a field selector (prefix abbreviation is OK):
146
+
147
+ ```text
148
+ subsys:usb
149
+ ```
150
+
151
+ Values containing colons, quotes, or special characters
152
+ should be quoted using Python/C/JS string escaping:
153
+
154
+ ```text
155
+ location:"3-2.1:1.0"
156
+ ```
157
+
158
+ Multiple constraints can be combined; all must match:
159
+
160
+ ```text
161
+ manufacturer:Adafruit serial:DF625*
162
+ ```
163
+
164
+ To experiment, pass a match expression to `ok_scan_serial` on the
165
+ command line; set `$OK_LOGGING_LEVEL=debug` to see the parse result:
166
+
167
+ ```text
168
+ % OK_LOGGING_LEVEL=debug ok_scan_serial -v 'manufacturer:Adafruit serial:DF625*'
169
+ ๐Ÿ•ธ ok_serial.scanning: Parsed 'manufacturer:Adafruit serial:DF625*':
170
+ manufacturer: /(?s:Adafruit)\Z/
171
+ serial: /(?s:DF625.*)\Z/
172
+ ๐Ÿ•ธ ok_serial.scanning: Found 36 ports
173
+ 36 serial ports found, 1 matches 'manufacturer:Adafruit serial:DF625*'
174
+ Serial port: /dev/ttyACM3
175
+ device: '/dev/ttyACM3'
176
+ name: 'ttyACM3'
177
+ description: 'Feather RP2040 RFM - Pico Serial'
178
+ hwid: 'USB VID:PID=239A:812D SER=DF62585783553434 LOCATION=3-2.1:1.0'
179
+ vid: '9114'
180
+ pid: '33069'
181
+ serial_number: 'DF62585783553434'
182
+ location: '3-2.1:1.0'
183
+ manufacturer: 'Adafruit'
184
+ product: 'Feather RP2040 RFM'
185
+ interface: 'Pico Serial'
186
+ usb_device_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1'
187
+ device_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1/3-2.1:1.0'
188
+ subsystem: 'usb'
189
+ usb_interface_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1/3-2.1:1.0'
190
+ ```
191
+
192
+ ## Sharing modes
193
+
194
+ When opening a port, `ok-serial` offers a choice of four sharing modes:
195
+
196
+ - `oblivious` - no locking is done and advisory locks are ignored. If
197
+ multiple programs open the port, they will all send and receive data
198
+ to the same device. This mode is not recommended.
199
+
200
+ - `polite` - locking is checked at open, and if the port is in use the
201
+ open fails. Once opened, no locks are held except for a shared lock
202
+ to discourage other `polite` users from opening the port. If a
203
+ less polite program opens the port later there will be conflict.
204
+ (In the future, this mode will attempt to notice such conflicts
205
+ and close out the port, deferring to the less-polite program.)
206
+
207
+ - `exclusive` (the default mode) - locking is checked at open, and if the
208
+ port is in use the open fails. Once opened, several means of locking
209
+ are employed to prevent or discourage others from opening the port.
210
+
211
+ - `stomp` (use with care!) - locking is checked at open, and if the port
212
+ is in use, _the program using the port is killed_ if permissions
213
+ allow. The port is opened regardless of any other users and all
214
+ available locks are taken.
215
+
216
+ The library's ability to implement these modes can be limited by
217
+ operating system capabilities, process permissions, and the variously
218
+ questionable historical conventions for port usage coordination.
@@ -0,0 +1,202 @@
1
+ # ok-serial for Python &nbsp; ๐Ÿ”Œใ€กใ€‡ใ€กใ€‡ใ€ก๐Ÿ
2
+
3
+ Python serial I/O library [based on PySerial](https://www.pyserial.com/) with
4
+ improved discovery and interface semantics.
5
+
6
+ Think twice before using this library! Consider something more established:
7
+
8
+ - [good old PySerial](https://www.pyserial.com/) - it is _very_ well established
9
+ - [pyserial-asyncio](https://github.com/pyserial/pyserial-asyncio) - official
10
+ and "proper" [asyncio](https://docs.python.org/3/library/asyncio.html)
11
+ support for PySerial
12
+ - [pyserial-asyncio-fast](https://github.com/home-assistant-libs/pyserial-asyncio-fast)
13
+ \- a fork of pyserial-asyncio designed for more efficient writes
14
+ - [aioserial](https://github.com/mrjohannchang/aioserial.py) - alternative
15
+ asyncio wrapper designed for ease of use
16
+
17
+ ## Purpose
18
+
19
+ Since 2001, [PySerial](https://www.pyserial.com/) has been the
20
+ workhorse [serial port](https://en.wikipedia.org/wiki/Serial_port)
21
+ ([UART](https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter))
22
+ library for Python. It runs on most Python platforms and abstracts
23
+ lots of gnarly system details. However, some problems keep coming up:
24
+
25
+ - Most serial ports are USB, and USB serial ports get assigned cryptic
26
+ temporary names like `/dev/ttyACM3` or `COM4`. Using
27
+ [`serial.tools.list_ports.grep(...)`](https://pythonhosted.org/pyserial/tools.html#serial.tools.list_ports.grep)
28
+ is a clumsy multi step process; linux allows
29
+ [udev rules](https://dev.to/enbis/how-udev-rules-can-help-us-to-recognize-a-usb-to-serial-device-over-dev-tty-interface-pbk)
30
+ but they're not exactly user friendly.
31
+
32
+ - Nonblocking or concurrent I/O with PySerial is perilous and often
33
+ [broken](https://github.com/pyserial/pyserial/issues/281)
34
+ [entirely](https://github.com/pyserial/pyserial/issues/280).
35
+
36
+ - Buffer sizes are finite and unspecified; overruns cause lost data
37
+ and/or blocking.
38
+
39
+ - Port locking is off by default in PySerial; even if enabled, it only
40
+ uses one advisory locking method. Bad things happen if multiple programs
41
+ try to use the same port.
42
+
43
+ The `ok-serial` library uses PySerial internally but has its own consistent
44
+ interface to fix these problems and be generally smoove:
45
+
46
+ - Ports are referenced by
47
+ [string expressions](#identifying-ports) that can match attributes
48
+ with wildcard support, eg. `desc:Arduino*` or `2e43:0226` or `*RP2040*`.
49
+ (You can also specify exact device path if desired.)
50
+
51
+ - I/O operations are thread safe and can be blocking, non-blocking,
52
+ timeout, or async. All blocking operations can be interrupted.
53
+ Semantics are well described, including concurrent access, partial
54
+ reads/writes, errors, and other edge cases.
55
+
56
+ - I/O buffers are unlimited except for system memory; writes
57
+ never block. (You can use a blocking drain operation to wait for
58
+ output completion if desired.)
59
+
60
+ - Includes [multiple port locking modes](#sharing-modes) with exclusive locking
61
+ as the default. Employs _all_ of
62
+ [`/var/lock/LCK..*` files](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s09.html),
63
+ [`flock(...)`](https://linux.die.net/man/2/flock) (like PySerial),
64
+ and [`TIOCEXCL`](https://man7.org/linux/man-pages/man2/TIOCEXCL.2const.html)
65
+ (as available) to avoid contention.
66
+
67
+ - Includes a `SerialTracker` helper to wait for a device of interest to
68
+ appear, rescanning as needed after disconnection, to handle devices
69
+ that might get plugged and unplugged.
70
+
71
+ ## Installation
72
+
73
+ ```bash
74
+ pip install ok-serial
75
+ ```
76
+
77
+ (or `uv add ok-serial`, etc.)
78
+
79
+ ## Identifying ports
80
+
81
+ Device names like `/dev/ttyUSB3` or `COM4` aren't very useful for USB
82
+ serial ports, so `ok-serial` uses **port match expressions** which
83
+ are strings that identify ports of interest by attributes such as the
84
+ device manufacturer (eg. `Adafruit`), product name (eg.
85
+ `CP2102 USB to UART Bridge Controller`), USB vendor/product ID (eg.
86
+ `239a:812d`), serial number, or other properties.
87
+
88
+ To see port attributes, install `ok-serial` and run
89
+ `ok_scan_serial --verbose` to list available ports like this:
90
+
91
+ ```text
92
+ Serial port: /dev/ttyACM3
93
+ device: '/dev/ttyACM3'
94
+ name: 'ttyACM3'
95
+ description: 'Feather RP2040 RFM - Pico Serial'
96
+ hwid: 'USB VID:PID=239A:812D SER=DF62585783553434 LOCATION=3-2.1:1.0'
97
+ vid: '9114'
98
+ pid: '33069'
99
+ serial_number: 'DF62585783553434'
100
+ location: '3-2.1:1.0'
101
+ manufacturer: 'Adafruit'
102
+ product: 'Feather RP2040 RFM'
103
+ interface: 'Pico Serial'
104
+ usb_device_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1'
105
+ device_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1/3-2.1:1.0'
106
+ subsystem: 'usb'
107
+ usb_interface_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1/3-2.1:1.0'
108
+ ```
109
+
110
+ Attribute names and value formats are inherited from PySerial and
111
+ the underlying OS and can vary, but
112
+ `device`, `name`, `description`, `hwid`, and (for USB) `vid`, `pid`,
113
+ `serial_number`, `location`, `manufacturer`, `product` and `interface`
114
+ are semi-standardized.
115
+
116
+ Match expressions can be a simple value, selecting any port with a
117
+ matching value (case-insensitive whole-string match):
118
+
119
+ ```text
120
+ Pico Serial
121
+ ```
122
+
123
+ Match values can include `*` and `?` wildcards:
124
+
125
+ ```text
126
+ *RP2040*
127
+ ```
128
+
129
+ Expressions can include a field selector (prefix abbreviation is OK):
130
+
131
+ ```text
132
+ subsys:usb
133
+ ```
134
+
135
+ Values containing colons, quotes, or special characters
136
+ should be quoted using Python/C/JS string escaping:
137
+
138
+ ```text
139
+ location:"3-2.1:1.0"
140
+ ```
141
+
142
+ Multiple constraints can be combined; all must match:
143
+
144
+ ```text
145
+ manufacturer:Adafruit serial:DF625*
146
+ ```
147
+
148
+ To experiment, pass a match expression to `ok_scan_serial` on the
149
+ command line; set `$OK_LOGGING_LEVEL=debug` to see the parse result:
150
+
151
+ ```text
152
+ % OK_LOGGING_LEVEL=debug ok_scan_serial -v 'manufacturer:Adafruit serial:DF625*'
153
+ ๐Ÿ•ธ ok_serial.scanning: Parsed 'manufacturer:Adafruit serial:DF625*':
154
+ manufacturer: /(?s:Adafruit)\Z/
155
+ serial: /(?s:DF625.*)\Z/
156
+ ๐Ÿ•ธ ok_serial.scanning: Found 36 ports
157
+ 36 serial ports found, 1 matches 'manufacturer:Adafruit serial:DF625*'
158
+ Serial port: /dev/ttyACM3
159
+ device: '/dev/ttyACM3'
160
+ name: 'ttyACM3'
161
+ description: 'Feather RP2040 RFM - Pico Serial'
162
+ hwid: 'USB VID:PID=239A:812D SER=DF62585783553434 LOCATION=3-2.1:1.0'
163
+ vid: '9114'
164
+ pid: '33069'
165
+ serial_number: 'DF62585783553434'
166
+ location: '3-2.1:1.0'
167
+ manufacturer: 'Adafruit'
168
+ product: 'Feather RP2040 RFM'
169
+ interface: 'Pico Serial'
170
+ usb_device_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1'
171
+ device_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1/3-2.1:1.0'
172
+ subsystem: 'usb'
173
+ usb_interface_path: '/sys/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.1/3-2.1:1.0'
174
+ ```
175
+
176
+ ## Sharing modes
177
+
178
+ When opening a port, `ok-serial` offers a choice of four sharing modes:
179
+
180
+ - `oblivious` - no locking is done and advisory locks are ignored. If
181
+ multiple programs open the port, they will all send and receive data
182
+ to the same device. This mode is not recommended.
183
+
184
+ - `polite` - locking is checked at open, and if the port is in use the
185
+ open fails. Once opened, no locks are held except for a shared lock
186
+ to discourage other `polite` users from opening the port. If a
187
+ less polite program opens the port later there will be conflict.
188
+ (In the future, this mode will attempt to notice such conflicts
189
+ and close out the port, deferring to the less-polite program.)
190
+
191
+ - `exclusive` (the default mode) - locking is checked at open, and if the
192
+ port is in use the open fails. Once opened, several means of locking
193
+ are employed to prevent or discourage others from opening the port.
194
+
195
+ - `stomp` (use with care!) - locking is checked at open, and if the port
196
+ is in use, _the program using the port is killed_ if permissions
197
+ allow. The port is opened regardless of any other users and all
198
+ available locks are taken.
199
+
200
+ The library's ability to implement these modes can be limited by
201
+ operating system capabilities, process permissions, and the variously
202
+ questionable historical conventions for port usage coordination.
@@ -0,0 +1,35 @@
1
+ """
2
+ Serial port library (PySerial wrapper) with improved discovery,
3
+ port sharing semantics, and interface.
4
+ """
5
+
6
+ from ok_serial._connection import (
7
+ SerialConnection,
8
+ SerialOptions,
9
+ SerialSignals,
10
+ )
11
+
12
+ from ok_serial._exceptions import (
13
+ OkSerialException,
14
+ SerialIoClosed,
15
+ SerialIoException,
16
+ SerialMatcherInvalid,
17
+ SerialOpenBusy,
18
+ SerialOpenException,
19
+ SerialScanException,
20
+ )
21
+ from ok_serial._locking import SerialSharingType
22
+
23
+ from ok_serial._scanning import (
24
+ SerialPort,
25
+ SerialPortMatcher,
26
+ scan_serial_ports,
27
+ )
28
+
29
+ from ok_serial._tracker import SerialTracker, TrackerOptions
30
+
31
+ from beartype.claw import beartype_this_package as _beartype_me
32
+
33
+ _beartype_me()
34
+
35
+ __all__ = [n for n in dir() if not n.startswith("_")]