python-osc 1.9.2__tar.gz → 1.10.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.
- {python_osc-1.9.2/python_osc.egg-info → python_osc-1.10.0}/PKG-INFO +28 -29
- {python_osc-1.9.2 → python_osc-1.10.0}/pyproject.toml +16 -3
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/dispatcher.py +51 -18
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/osc_bundle_builder.py +4 -2
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/osc_message.py +6 -2
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/osc_message_builder.py +5 -5
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/osc_packet.py +1 -2
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/osc_server.py +1 -2
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/osc_tcp_server.py +4 -4
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/parsing/osc_types.py +12 -18
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/tcp_client.py +19 -0
- python_osc-1.10.0/pythonosc/test/parsing/__init__.py +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/parsing/test_osc_types.py +5 -5
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_osc_bundle.py +4 -10
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_osc_message.py +4 -12
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_osc_message_builder.py +1 -1
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_osc_packet.py +1 -1
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_osc_server.py +1 -1
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_osc_tcp_server.py +9 -9
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_udp_client.py +16 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/udp_client.py +12 -1
- python_osc-1.9.2/MANIFEST.in +0 -3
- python_osc-1.9.2/PKG-INFO +0 -198
- python_osc-1.9.2/python_osc.egg-info/SOURCES.txt +0 -37
- python_osc-1.9.2/python_osc.egg-info/dependency_links.txt +0 -1
- python_osc-1.9.2/python_osc.egg-info/top_level.txt +0 -1
- python_osc-1.9.2/setup.cfg +0 -4
- {python_osc-1.9.2 → python_osc-1.10.0}/LICENSE.txt +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/README.rst +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/__init__.py +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/osc_bundle.py +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/parsing/__init__.py +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/parsing/ntp.py +0 -0
- /python_osc-1.9.2/pythonosc/test/__init__.py → /python_osc-1.10.0/pythonosc/py.typed +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/slip.py +0 -0
- {python_osc-1.9.2/pythonosc/test/parsing → python_osc-1.10.0/pythonosc/test}/__init__.py +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/parsing/test_ntp.py +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_dispatcher.py +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_osc_bundle_builder.py +0 -0
- {python_osc-1.9.2 → python_osc-1.10.0}/pythonosc/test/test_tcp_client.py +0 -0
|
@@ -1,35 +1,34 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: python-osc
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.0
|
|
4
4
|
Summary: Open Sound Control server and client implementations in pure Python
|
|
5
|
+
Keywords: osc,sound,midi,music
|
|
6
|
+
Author: attwad
|
|
5
7
|
Author-email: attwad <tmusoft@gmail.com>
|
|
6
8
|
License: This is free and unencumbered software released into the public domain.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Project-URL: Repository, https://github.com/attwad/python-osc
|
|
32
|
-
Keywords: osc,sound,midi,music
|
|
9
|
+
|
|
10
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
11
|
+
distribute this software, either in source code form or as a compiled
|
|
12
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
13
|
+
means.
|
|
14
|
+
|
|
15
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
16
|
+
of this software dedicate any and all copyright interest in the
|
|
17
|
+
software to the public domain. We make this dedication for the benefit
|
|
18
|
+
of the public at large and to the detriment of our heirs and
|
|
19
|
+
successors. We intend this dedication to be an overt act of
|
|
20
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
21
|
+
software under copyright law.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
24
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
25
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
26
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
27
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
28
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
29
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
30
|
+
|
|
31
|
+
For more information, please refer to <http://unlicense.org/>
|
|
33
32
|
Classifier: Development Status :: 5 - Production/Stable
|
|
34
33
|
Classifier: Intended Audience :: Developers
|
|
35
34
|
Classifier: License :: Freely Distributable
|
|
@@ -37,8 +36,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
37
36
|
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
38
37
|
Classifier: Topic :: System :: Networking
|
|
39
38
|
Requires-Python: >=3.10
|
|
39
|
+
Project-URL: Repository, https://github.com/attwad/python-osc
|
|
40
40
|
Description-Content-Type: text/x-rst
|
|
41
|
-
License-File: LICENSE.txt
|
|
42
41
|
|
|
43
42
|
==========
|
|
44
43
|
python-osc
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["
|
|
3
|
-
build-backend = "
|
|
2
|
+
requires = ["uv-build"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-osc"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.10.0"
|
|
8
8
|
description = "Open Sound Control server and client implementations in pure Python"
|
|
9
9
|
readme = "README.rst"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -21,10 +21,23 @@ classifiers = [
|
|
|
21
21
|
"Topic :: Multimedia :: Sound/Audio",
|
|
22
22
|
"Topic :: System :: Networking",
|
|
23
23
|
]
|
|
24
|
+
dependencies = []
|
|
24
25
|
|
|
25
26
|
[project.urls]
|
|
26
27
|
Repository = "https://github.com/attwad/python-osc"
|
|
27
28
|
|
|
29
|
+
[dependency-groups]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest",
|
|
32
|
+
"mypy",
|
|
33
|
+
"ruff",
|
|
34
|
+
"pytest-cov",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.uv.build-backend]
|
|
38
|
+
module-name = "pythonosc"
|
|
39
|
+
module-root = "."
|
|
40
|
+
|
|
28
41
|
[tool.mypy]
|
|
29
42
|
# Would be great to turn this on, however there's too many cases it would break
|
|
30
43
|
# right now.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""Maps OSC addresses to handler functions
|
|
2
|
-
"""
|
|
1
|
+
"""Maps OSC addresses to handler functions"""
|
|
3
2
|
|
|
4
3
|
import collections
|
|
5
4
|
import inspect
|
|
@@ -183,28 +182,62 @@ class Dispatcher(object):
|
|
|
183
182
|
) -> Generator[Handler, None, None]:
|
|
184
183
|
"""Yields handlers matching an address
|
|
185
184
|
|
|
186
|
-
|
|
187
185
|
Args:
|
|
188
186
|
address_pattern: Address to match
|
|
189
187
|
|
|
190
188
|
Returns:
|
|
191
189
|
Generator yielding Handlers matching address_pattern
|
|
192
190
|
"""
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
191
|
+
# Convert OSC Address Pattern to a Python regular expression.
|
|
192
|
+
# Spec: https://opensoundcontrol.stanford.edu/spec-1_0.html#osc-address-patterns
|
|
193
|
+
|
|
194
|
+
pattern = "^"
|
|
195
|
+
i = 0
|
|
196
|
+
while i < len(address_pattern):
|
|
197
|
+
c = address_pattern[i]
|
|
198
|
+
if c == "*":
|
|
199
|
+
pattern += "[^/]*"
|
|
200
|
+
elif c == "?":
|
|
201
|
+
pattern += "[^/]"
|
|
202
|
+
elif c == "[":
|
|
203
|
+
pattern += "["
|
|
204
|
+
i += 1
|
|
205
|
+
if i < len(address_pattern) and address_pattern[i] == "!":
|
|
206
|
+
pattern += "^"
|
|
207
|
+
i += 1
|
|
208
|
+
while i < len(address_pattern) and address_pattern[i] != "]":
|
|
209
|
+
if address_pattern[i] in r"\^$.|()+*?":
|
|
210
|
+
pattern += "\\"
|
|
211
|
+
pattern += address_pattern[i]
|
|
212
|
+
i += 1
|
|
213
|
+
pattern += "]"
|
|
214
|
+
elif c == "{":
|
|
215
|
+
pattern += "("
|
|
216
|
+
i += 1
|
|
217
|
+
while i < len(address_pattern) and address_pattern[i] != "}":
|
|
218
|
+
char = address_pattern[i]
|
|
219
|
+
if char == ",":
|
|
220
|
+
pattern += "|"
|
|
221
|
+
elif char in r"\^$.|()[]+*?":
|
|
222
|
+
pattern += "\\" + char
|
|
223
|
+
else:
|
|
224
|
+
pattern += char
|
|
225
|
+
i += 1
|
|
226
|
+
pattern += ")"
|
|
227
|
+
elif c in r"\^$.|()[]+?":
|
|
228
|
+
pattern += "\\" + c
|
|
229
|
+
else:
|
|
230
|
+
pattern += c
|
|
231
|
+
i += 1
|
|
232
|
+
pattern += "$"
|
|
207
233
|
|
|
234
|
+
try:
|
|
235
|
+
patterncompiled = re.compile(pattern)
|
|
236
|
+
except re.error:
|
|
237
|
+
# If the pattern is invalid, it won't match anything.
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
matched = False
|
|
208
241
|
for addr, handlers in self._map.items():
|
|
209
242
|
if patterncompiled.match(addr) or (
|
|
210
243
|
("*" in addr)
|
|
@@ -317,7 +350,7 @@ class Dispatcher(object):
|
|
|
317
350
|
)
|
|
318
351
|
if result:
|
|
319
352
|
results.append(result)
|
|
320
|
-
except osc_packet.ParseError
|
|
353
|
+
except osc_packet.ParseError:
|
|
321
354
|
pass
|
|
322
355
|
return results
|
|
323
356
|
|
|
@@ -25,9 +25,11 @@ class OscBundleBuilder(object):
|
|
|
25
25
|
seconds since the epoch in UTC or IMMEDIATELY.
|
|
26
26
|
"""
|
|
27
27
|
self._timestamp = timestamp
|
|
28
|
-
self._contents: List[osc_bundle.OscBundle] = []
|
|
28
|
+
self._contents: List[osc_bundle.OscBundle | osc_message.OscMessage] = []
|
|
29
29
|
|
|
30
|
-
def add_content(
|
|
30
|
+
def add_content(
|
|
31
|
+
self, content: osc_bundle.OscBundle | osc_message.OscMessage
|
|
32
|
+
) -> None:
|
|
31
33
|
"""Add a new content to this bundle.
|
|
32
34
|
|
|
33
35
|
Args:
|
|
@@ -34,8 +34,12 @@ class OscMessage(object):
|
|
|
34
34
|
|
|
35
35
|
# Get the parameters types.
|
|
36
36
|
type_tag, index = osc_types.get_string(self._dgram, index)
|
|
37
|
-
if type_tag.startswith(","):
|
|
38
|
-
|
|
37
|
+
if not type_tag.startswith(","):
|
|
38
|
+
raise ParseError(
|
|
39
|
+
f"OSC Type Tag String must start with a comma, got: {type_tag}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
type_tag = type_tag[1:]
|
|
39
43
|
|
|
40
44
|
params = [] # type: List[Any]
|
|
41
45
|
param_stack = [params]
|
|
@@ -5,7 +5,7 @@ from typing import Any, Iterable, List, Optional, Tuple, Union
|
|
|
5
5
|
from pythonosc import osc_message
|
|
6
6
|
from pythonosc.parsing import osc_types
|
|
7
7
|
|
|
8
|
-
ArgValue = Union[str, bytes, bool, int, float, osc_types.MidiPacket, list]
|
|
8
|
+
ArgValue = Union[str, bytes, bool, int, float, osc_types.MidiPacket, list, None]
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class BuildError(Exception):
|
|
@@ -96,7 +96,7 @@ class OscMessageBuilder(object):
|
|
|
96
96
|
arg_type = self._get_arg_type(arg_value)
|
|
97
97
|
if isinstance(arg_type, list):
|
|
98
98
|
self._args.append((self.ARG_TYPE_ARRAY_START, None))
|
|
99
|
-
for v, t in zip(arg_value, arg_type): # type: ignore[
|
|
99
|
+
for v, t in zip(arg_value, arg_type): # type: ignore[arg-type]
|
|
100
100
|
self.add_arg(v, t)
|
|
101
101
|
self._args.append((self.ARG_TYPE_ARRAY_STOP, None))
|
|
102
102
|
else:
|
|
@@ -121,7 +121,7 @@ class OscMessageBuilder(object):
|
|
|
121
121
|
elif arg_value is False:
|
|
122
122
|
arg_type = self.ARG_TYPE_FALSE
|
|
123
123
|
elif isinstance(arg_value, int):
|
|
124
|
-
if arg_value.bit_length() >
|
|
124
|
+
if arg_value.bit_length() > 31:
|
|
125
125
|
arg_type = self.ARG_TYPE_INT64
|
|
126
126
|
else:
|
|
127
127
|
arg_type = self.ARG_TYPE_INT
|
|
@@ -134,7 +134,7 @@ class OscMessageBuilder(object):
|
|
|
134
134
|
elif arg_value is None:
|
|
135
135
|
arg_type = self.ARG_TYPE_NIL
|
|
136
136
|
else:
|
|
137
|
-
raise ValueError("
|
|
137
|
+
raise ValueError("Inferred arg_value type is not supported")
|
|
138
138
|
return arg_type
|
|
139
139
|
|
|
140
140
|
def build(self) -> osc_message.OscMessage:
|
|
@@ -195,7 +195,7 @@ class OscMessageBuilder(object):
|
|
|
195
195
|
|
|
196
196
|
def build_msg(address: str, value: ArgValue = "") -> osc_message.OscMessage:
|
|
197
197
|
builder = OscMessageBuilder(address=address)
|
|
198
|
-
values:
|
|
198
|
+
values: Iterable[Any]
|
|
199
199
|
if value == "":
|
|
200
200
|
values = []
|
|
201
201
|
elif not isinstance(value, Iterable) or isinstance(value, (str, bytes)):
|
|
@@ -72,8 +72,7 @@ class OscPacket(object):
|
|
|
72
72
|
else:
|
|
73
73
|
# Empty packet, should not happen as per the spec but heh, UDP...
|
|
74
74
|
raise ParseError(
|
|
75
|
-
"OSC Packet should at least contain an OscMessage or an "
|
|
76
|
-
"OscBundle."
|
|
75
|
+
"OSC Packet should at least contain an OscMessage or an OscBundle."
|
|
77
76
|
)
|
|
78
77
|
except (osc_bundle.ParseError, osc_message.ParseError) as pe:
|
|
79
78
|
raise ParseError(f"Could not parse packet {pe}")
|
|
@@ -75,7 +75,7 @@ class _TCPHandler1_0(socketserver.BaseRequestHandler):
|
|
|
75
75
|
)
|
|
76
76
|
# resp = _call_handlers_for_packet(data, self.server.dispatcher)
|
|
77
77
|
for r in resp:
|
|
78
|
-
if not isinstance(r,
|
|
78
|
+
if not isinstance(r, tuple):
|
|
79
79
|
r = [r]
|
|
80
80
|
msg = osc_message_builder.build_msg(r[0], r[1:])
|
|
81
81
|
b = struct.pack("!I", len(msg.dgram))
|
|
@@ -117,7 +117,7 @@ class _TCPHandler1_1(socketserver.BaseRequestHandler):
|
|
|
117
117
|
p, self.client_address
|
|
118
118
|
)
|
|
119
119
|
for r in resp:
|
|
120
|
-
if not isinstance(r,
|
|
120
|
+
if not isinstance(r, tuple):
|
|
121
121
|
r = [r]
|
|
122
122
|
msg = osc_message_builder.build_msg(r[0], r[1:])
|
|
123
123
|
self.request.sendall(slip.encode(msg.dgram))
|
|
@@ -284,7 +284,7 @@ class AsyncOSCTCPServer:
|
|
|
284
284
|
buf, client_address
|
|
285
285
|
)
|
|
286
286
|
for r in result:
|
|
287
|
-
if not isinstance(r,
|
|
287
|
+
if not isinstance(r, tuple):
|
|
288
288
|
r = [r]
|
|
289
289
|
msg = osc_message_builder.build_msg(r[0], r[1:])
|
|
290
290
|
b = struct.pack("!I", len(msg.dgram))
|
|
@@ -319,7 +319,7 @@ class AsyncOSCTCPServer:
|
|
|
319
319
|
p, client_address
|
|
320
320
|
)
|
|
321
321
|
for r in result:
|
|
322
|
-
if not isinstance(r,
|
|
322
|
+
if not isinstance(r, tuple):
|
|
323
323
|
r = [r]
|
|
324
324
|
msg = osc_message_builder.build_msg(r[0], r[1:])
|
|
325
325
|
writer.write(slip.encode(msg.dgram))
|
|
@@ -71,24 +71,21 @@ def get_string(dgram: bytes, start_index: int) -> Tuple[str, int]:
|
|
|
71
71
|
raise ParseError("start_index < 0")
|
|
72
72
|
offset = 0
|
|
73
73
|
try:
|
|
74
|
-
if (
|
|
75
|
-
len(dgram) > start_index + _STRING_DGRAM_PAD
|
|
76
|
-
and dgram[start_index + _STRING_DGRAM_PAD] == _EMPTY_STR_DGRAM
|
|
77
|
-
):
|
|
78
|
-
return "", start_index + _STRING_DGRAM_PAD
|
|
79
74
|
while dgram[start_index + offset] != 0:
|
|
80
75
|
offset += 1
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
76
|
+
|
|
77
|
+
# OSC spec: "followed by a null, followed by 0-3 additional null characters
|
|
78
|
+
# to make the total number of bits a multiple of 32"
|
|
79
|
+
# This means the total length (including the first null) must be a multiple of 4.
|
|
80
|
+
total_len = offset + 1
|
|
81
|
+
if total_len % 4 != 0:
|
|
82
|
+
total_len += 4 - (total_len % 4)
|
|
83
|
+
|
|
84
|
+
if start_index + total_len > len(dgram):
|
|
89
85
|
raise ParseError("Datagram is too short")
|
|
86
|
+
|
|
90
87
|
data_str = dgram[start_index : start_index + offset]
|
|
91
|
-
return data_str.
|
|
88
|
+
return data_str.decode("utf-8"), start_index + total_len
|
|
92
89
|
except IndexError as ie:
|
|
93
90
|
raise ParseError(f"Could not parse datagram {ie}")
|
|
94
91
|
except TypeError as te:
|
|
@@ -214,11 +211,8 @@ def get_timetag(dgram: bytes, start_index: int) -> Tuple[Tuple[datetime, int], i
|
|
|
214
211
|
timetag, _ = get_uint64(dgram, start_index)
|
|
215
212
|
seconds, fraction = ntp.parse_timestamp(timetag)
|
|
216
213
|
|
|
217
|
-
hours, seconds = seconds // 3600, seconds % 3600
|
|
218
|
-
minutes, seconds = seconds // 60, seconds % 60
|
|
219
|
-
|
|
220
214
|
utc = datetime.combine(ntp._NTP_EPOCH, datetime.min.time()) + timedelta(
|
|
221
|
-
|
|
215
|
+
seconds=seconds
|
|
222
216
|
)
|
|
223
217
|
|
|
224
218
|
return (utc, fraction), start_index + _TIMETAG_DGRAM_LEN
|
|
@@ -122,6 +122,25 @@ class SimpleTCPClient(TCPClient):
|
|
|
122
122
|
r = self.receive(timeout)
|
|
123
123
|
|
|
124
124
|
|
|
125
|
+
class TCPDispatchClient(SimpleTCPClient):
|
|
126
|
+
"""OSC TCP Client that includes a :class:`Dispatcher` for handling responses and other messages from the server"""
|
|
127
|
+
|
|
128
|
+
dispatcher = Dispatcher()
|
|
129
|
+
|
|
130
|
+
def handle_messages(self, timeout_sec: int = 30) -> None:
|
|
131
|
+
"""Wait :int:`timeout` seconds for a message from the server and process each message with the registered
|
|
132
|
+
handlers. Continue until a timeout occurs.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
timeout: Time in seconds to wait for a message
|
|
136
|
+
"""
|
|
137
|
+
r = self.receive(timeout_sec)
|
|
138
|
+
while r:
|
|
139
|
+
for m in r:
|
|
140
|
+
self.dispatcher.call_handlers_for_packet(m, (self.address, self.port))
|
|
141
|
+
r = self.receive(timeout_sec)
|
|
142
|
+
|
|
143
|
+
|
|
125
144
|
class AsyncTCPClient:
|
|
126
145
|
"""Async OSC client to send :class:`OscMessage` or :class:`OscBundle` via TCP"""
|
|
127
146
|
|
|
File without changes
|
|
@@ -85,10 +85,10 @@ class TestRGBA(unittest.TestCase):
|
|
|
85
85
|
b"\x00\x00\x00\x01": (1, 4),
|
|
86
86
|
b"\x00\x00\x00\x02": (2, 4),
|
|
87
87
|
b"\x00\x00\x00\x03": (3, 4),
|
|
88
|
-
b"\
|
|
89
|
-
b"\x00\
|
|
90
|
-
b"\x00\x00\
|
|
91
|
-
b"\x00\x00\x00\
|
|
88
|
+
b"\xff\x00\x00\x00": (4278190080, 4),
|
|
89
|
+
b"\x00\xff\x00\x00": (16711680, 4),
|
|
90
|
+
b"\x00\x00\xff\x00": (65280, 4),
|
|
91
|
+
b"\x00\x00\x00\xff": (255, 4),
|
|
92
92
|
b"\x00\x00\x00\x01GARBAGE": (1, 4),
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -164,7 +164,7 @@ class TestDate(unittest.TestCase):
|
|
|
164
164
|
(datetime(1900, 1, 1, 0, 0, 0), 0),
|
|
165
165
|
8,
|
|
166
166
|
),
|
|
167
|
-
b"\x83\xaa\
|
|
167
|
+
b"\x83\xaa\x7e\x80\x0a\x00\xb0\x0c": (
|
|
168
168
|
(datetime(1970, 1, 1, 0, 0, 0), 167817228),
|
|
169
169
|
8,
|
|
170
170
|
),
|
|
@@ -46,7 +46,7 @@ _DGRAM_TWO_MESSAGES_IN_BUNDLE = (
|
|
|
46
46
|
b"?\x00\x00\x00"
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
-
_DGRAM_EMPTY_BUNDLE = b"#bundle\x00
|
|
49
|
+
_DGRAM_EMPTY_BUNDLE = b"#bundle\x00\x00\x00\x00\x00\x00\x00\x00\x01"
|
|
50
50
|
|
|
51
51
|
_DGRAM_BUNDLE_IN_BUNDLE = (
|
|
52
52
|
b"#bundle\x00"
|
|
@@ -60,20 +60,14 @@ _DGRAM_BUNDLE_IN_BUNDLE = (
|
|
|
60
60
|
b"?\x00\x00\x00"
|
|
61
61
|
)
|
|
62
62
|
|
|
63
|
-
_DGRAM_INVALID = b"#bundle\x00
|
|
63
|
+
_DGRAM_INVALID = b"#bundle\x00\x00\x00\x00"
|
|
64
64
|
|
|
65
65
|
_DGRAM_INVALID_INDEX = (
|
|
66
|
-
b"#bundle\x00"
|
|
67
|
-
b"\x00\x00\x00\x00\x00\x00\x00\x01"
|
|
68
|
-
b"\x00\x00\x00\x20"
|
|
69
|
-
b"/SYNC\x00\x00\x00\x00"
|
|
66
|
+
b"#bundle\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x20/SYNC\x00\x00\x00\x00"
|
|
70
67
|
)
|
|
71
68
|
|
|
72
69
|
_DGRAM_UNKNOWN_TYPE = (
|
|
73
|
-
b"#bundle\x00"
|
|
74
|
-
b"\x00\x00\x00\x00\x00\x00\x00\x01"
|
|
75
|
-
b"\x00\x00\x00\x10"
|
|
76
|
-
b"iamnotaslash"
|
|
70
|
+
b"#bundle\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x10iamnotaslash"
|
|
77
71
|
)
|
|
78
72
|
|
|
79
73
|
|
|
@@ -5,11 +5,11 @@ from pythonosc import osc_message
|
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
|
|
7
7
|
# Datagrams sent by Reaktor 5.8 by Native Instruments (c).
|
|
8
|
-
_DGRAM_KNOB_ROTATES = b"/FB\x00
|
|
8
|
+
_DGRAM_KNOB_ROTATES = b"/FB\x00,f\x00\x00>xca=q"
|
|
9
9
|
|
|
10
|
-
_DGRAM_SWITCH_GOES_OFF = b"/SYNC\x00\x00\x00
|
|
10
|
+
_DGRAM_SWITCH_GOES_OFF = b"/SYNC\x00\x00\x00,f\x00\x00\x00\x00\x00\x00"
|
|
11
11
|
|
|
12
|
-
_DGRAM_SWITCH_GOES_ON = b"/SYNC\x00\x00\x00
|
|
12
|
+
_DGRAM_SWITCH_GOES_ON = b"/SYNC\x00\x00\x00,f\x00\x00?\x00\x00\x00"
|
|
13
13
|
|
|
14
14
|
_DGRAM_NO_PARAMS = b"/SYNC\x00\x00\x00"
|
|
15
15
|
|
|
@@ -22,15 +22,7 @@ _DGRAM_ALL_STANDARD_TYPES_OF_PARAMS = (
|
|
|
22
22
|
b"\x00\x00\x00\x08stuff\x00\x00\x00"
|
|
23
23
|
) # b"stuff\x00\x00\x00"
|
|
24
24
|
|
|
25
|
-
_DGRAM_ALL_NON_STANDARD_TYPES_OF_PARAMS =
|
|
26
|
-
b"/SYNC\x00\x00\x00"
|
|
27
|
-
b"T" # True
|
|
28
|
-
b"F" # False
|
|
29
|
-
b"N" # Nil
|
|
30
|
-
b"[]th\x00" # Empty array
|
|
31
|
-
b"\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
32
|
-
b"\x00\x00\x00\xe8\xd4\xa5\x10\x00" # 1000000000000
|
|
33
|
-
)
|
|
25
|
+
_DGRAM_ALL_NON_STANDARD_TYPES_OF_PARAMS = b"/SYNC\x00\x00\x00,TFN[]th\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe8\xd4\xa5\x10\x00"
|
|
34
26
|
|
|
35
27
|
_DGRAM_COMPLEX_ARRAY_PARAMS = (
|
|
36
28
|
b"/SYNC\x00\x00\x00"
|
|
@@ -19,7 +19,7 @@ class TestOscMessageBuilder(unittest.TestCase):
|
|
|
19
19
|
builder = osc_message_builder.OscMessageBuilder("")
|
|
20
20
|
self.assertRaises(ValueError, builder.add_arg, "what?", 1)
|
|
21
21
|
|
|
22
|
-
def
|
|
22
|
+
def test_add_arg_invalid_inferred_type(self):
|
|
23
23
|
builder = osc_message_builder.OscMessageBuilder("")
|
|
24
24
|
self.assertRaises(ValueError, builder.add_arg, {"name": "John"})
|
|
25
25
|
|
|
@@ -3,7 +3,7 @@ import unittest.mock
|
|
|
3
3
|
|
|
4
4
|
from pythonosc import dispatcher, osc_server
|
|
5
5
|
|
|
6
|
-
_SIMPLE_PARAM_INT_MSG = b"/SYNC\x00\x00\x00
|
|
6
|
+
_SIMPLE_PARAM_INT_MSG = b"/SYNC\x00\x00\x00,i\x00\x00\x00\x00\x00\x04"
|
|
7
7
|
|
|
8
8
|
# Regression test for a datagram that should NOT be stripped, ever...
|
|
9
9
|
_SIMPLE_PARAM_INT_9 = b"/debug\x00\x00,i\x00\x00\x00\x00\x00\t"
|
|
@@ -5,7 +5,7 @@ import unittest.mock as mock
|
|
|
5
5
|
from pythonosc import dispatcher, osc_tcp_server
|
|
6
6
|
from pythonosc.slip import END
|
|
7
7
|
|
|
8
|
-
_SIMPLE_PARAM_INT_MSG = b"/SYNC\x00\x00\x00
|
|
8
|
+
_SIMPLE_PARAM_INT_MSG = b"/SYNC\x00\x00\x00,i\x00\x00\x00\x00\x00\x04"
|
|
9
9
|
|
|
10
10
|
LEN_SIMPLE_PARAM_INT_MSG = struct.pack("!I", len(_SIMPLE_PARAM_INT_MSG))
|
|
11
11
|
_SIMPLE_PARAM_INT_MSG_1_1 = END + _SIMPLE_PARAM_INT_MSG + END
|
|
@@ -95,12 +95,12 @@ class TestTCP_1_1_Handler(unittest.TestCase):
|
|
|
95
95
|
|
|
96
96
|
def test_response_with_args(self):
|
|
97
97
|
def respond(*args, **kwargs):
|
|
98
|
-
return
|
|
98
|
+
return (
|
|
99
99
|
"/SYNC",
|
|
100
100
|
1,
|
|
101
101
|
"2",
|
|
102
102
|
3.0,
|
|
103
|
-
|
|
103
|
+
)
|
|
104
104
|
|
|
105
105
|
self.dispatcher.map("/SYNC", respond)
|
|
106
106
|
mock_sock = mock.Mock()
|
|
@@ -208,12 +208,12 @@ class TestTCP_1_0_Handler(unittest.TestCase):
|
|
|
208
208
|
|
|
209
209
|
def test_response_with_args(self):
|
|
210
210
|
def respond(*args, **kwargs):
|
|
211
|
-
return
|
|
211
|
+
return (
|
|
212
212
|
"/SYNC",
|
|
213
213
|
1,
|
|
214
214
|
"2",
|
|
215
215
|
3.0,
|
|
216
|
-
|
|
216
|
+
)
|
|
217
217
|
|
|
218
218
|
self.dispatcher.map("/SYNC", respond)
|
|
219
219
|
mock_sock = mock.Mock()
|
|
@@ -314,12 +314,12 @@ class TestAsync1_1Handler(unittest.IsolatedAsyncioTestCase):
|
|
|
314
314
|
|
|
315
315
|
async def test_response_with_args(self):
|
|
316
316
|
def respond(*args, **kwargs):
|
|
317
|
-
return
|
|
317
|
+
return (
|
|
318
318
|
"/SYNC",
|
|
319
319
|
1,
|
|
320
320
|
"2",
|
|
321
321
|
3.0,
|
|
322
|
-
|
|
322
|
+
)
|
|
323
323
|
|
|
324
324
|
self.dispatcher.map("/SYNC", respond)
|
|
325
325
|
self.mock_reader.read.side_effect = [_SIMPLE_MSG_NO_PARAMS_1_1, b""]
|
|
@@ -332,12 +332,12 @@ class TestAsync1_1Handler(unittest.IsolatedAsyncioTestCase):
|
|
|
332
332
|
|
|
333
333
|
async def test_async_response_with_args(self):
|
|
334
334
|
async def respond(*args, **kwargs):
|
|
335
|
-
return
|
|
335
|
+
return (
|
|
336
336
|
"/SYNC",
|
|
337
337
|
1,
|
|
338
338
|
"2",
|
|
339
339
|
3.0,
|
|
340
|
-
|
|
340
|
+
)
|
|
341
341
|
|
|
342
342
|
self.dispatcher.map("/SYNC", respond)
|
|
343
343
|
self.mock_reader.read.side_effect = [_SIMPLE_MSG_NO_PARAMS_1_1, b""]
|
|
@@ -48,5 +48,21 @@ class TestSimpleUdpClient(unittest.TestCase):
|
|
|
48
48
|
self.assertEqual(self.builder.add_arg.call_count, 3)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
class TestUdpClientClose(unittest.TestCase):
|
|
52
|
+
@mock.patch("socket.socket")
|
|
53
|
+
def test_close(self, mock_socket_ctor):
|
|
54
|
+
mock_socket = mock_socket_ctor.return_value
|
|
55
|
+
client = udp_client.UDPClient("::1", 31337)
|
|
56
|
+
client.close()
|
|
57
|
+
self.assertTrue(mock_socket.close.called)
|
|
58
|
+
|
|
59
|
+
@mock.patch("socket.socket")
|
|
60
|
+
def test_context_manager(self, mock_socket_ctor):
|
|
61
|
+
mock_socket = mock_socket_ctor.return_value
|
|
62
|
+
with udp_client.UDPClient("::1", 31337) as client:
|
|
63
|
+
self.assertIsInstance(client, udp_client.UDPClient)
|
|
64
|
+
self.assertTrue(mock_socket.close.called)
|
|
65
|
+
|
|
66
|
+
|
|
51
67
|
if __name__ == "__main__":
|
|
52
68
|
unittest.main()
|
|
@@ -55,6 +55,18 @@ class UDPClient(object):
|
|
|
55
55
|
self._address = address
|
|
56
56
|
self._port = port
|
|
57
57
|
|
|
58
|
+
def __enter__(self) -> "UDPClient":
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
def __exit__(
|
|
62
|
+
self, exc_type: type | None, exc_val: Exception | None, exc_tb: object | None
|
|
63
|
+
) -> None:
|
|
64
|
+
self.close()
|
|
65
|
+
|
|
66
|
+
def close(self) -> None:
|
|
67
|
+
"""Close the socket"""
|
|
68
|
+
self._sock.close()
|
|
69
|
+
|
|
58
70
|
def send(self, content: Union[OscMessage, OscBundle]) -> None:
|
|
59
71
|
"""Sends an :class:`OscMessage` or :class:`OscBundle` via UDP
|
|
60
72
|
|
|
@@ -87,7 +99,6 @@ class SimpleUDPClient(UDPClient):
|
|
|
87
99
|
value: One or more arguments to be added to the message
|
|
88
100
|
"""
|
|
89
101
|
builder = OscMessageBuilder(address=address)
|
|
90
|
-
values: ArgValue
|
|
91
102
|
if value is None:
|
|
92
103
|
pass
|
|
93
104
|
elif not isinstance(value, Iterable) or isinstance(value, (str, bytes)):
|
python_osc-1.9.2/MANIFEST.in
DELETED
python_osc-1.9.2/PKG-INFO
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: python-osc
|
|
3
|
-
Version: 1.9.2
|
|
4
|
-
Summary: Open Sound Control server and client implementations in pure Python
|
|
5
|
-
Author-email: attwad <tmusoft@gmail.com>
|
|
6
|
-
License: This is free and unencumbered software released into the public domain.
|
|
7
|
-
|
|
8
|
-
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
9
|
-
distribute this software, either in source code form or as a compiled
|
|
10
|
-
binary, for any purpose, commercial or non-commercial, and by any
|
|
11
|
-
means.
|
|
12
|
-
|
|
13
|
-
In jurisdictions that recognize copyright laws, the author or authors
|
|
14
|
-
of this software dedicate any and all copyright interest in the
|
|
15
|
-
software to the public domain. We make this dedication for the benefit
|
|
16
|
-
of the public at large and to the detriment of our heirs and
|
|
17
|
-
successors. We intend this dedication to be an overt act of
|
|
18
|
-
relinquishment in perpetuity of all present and future rights to this
|
|
19
|
-
software under copyright law.
|
|
20
|
-
|
|
21
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
22
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
23
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
24
|
-
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
25
|
-
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
26
|
-
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
27
|
-
OTHER DEALINGS IN THE SOFTWARE.
|
|
28
|
-
|
|
29
|
-
For more information, please refer to <http://unlicense.org/>
|
|
30
|
-
|
|
31
|
-
Project-URL: Repository, https://github.com/attwad/python-osc
|
|
32
|
-
Keywords: osc,sound,midi,music
|
|
33
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
34
|
-
Classifier: Intended Audience :: Developers
|
|
35
|
-
Classifier: License :: Freely Distributable
|
|
36
|
-
Classifier: Programming Language :: Python :: 3
|
|
37
|
-
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
38
|
-
Classifier: Topic :: System :: Networking
|
|
39
|
-
Requires-Python: >=3.10
|
|
40
|
-
Description-Content-Type: text/x-rst
|
|
41
|
-
License-File: LICENSE.txt
|
|
42
|
-
|
|
43
|
-
==========
|
|
44
|
-
python-osc
|
|
45
|
-
==========
|
|
46
|
-
|
|
47
|
-
Open Sound Control server and client implementations in **pure python**.
|
|
48
|
-
|
|
49
|
-
.. image:: https://github.com/attwad/python-osc/actions/workflows/python-test.yml/badge.svg
|
|
50
|
-
:target: https://github.com/attwad/python-osc/actions/workflows/python-test.yml
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Current status
|
|
54
|
-
==============
|
|
55
|
-
|
|
56
|
-
This library was developed following the
|
|
57
|
-
`OpenSoundControl Specification 1.0 <https://opensoundcontrol.stanford.edu/spec-1_0.html>`_
|
|
58
|
-
and is currently in a stable state.
|
|
59
|
-
|
|
60
|
-
Features
|
|
61
|
-
========
|
|
62
|
-
|
|
63
|
-
* UDP and TCP blocking/threading/forking/asyncio server implementations
|
|
64
|
-
* UDP and TCP clients, including asyncio support
|
|
65
|
-
* TCP support for 1.0 and 1.1 protocol formats
|
|
66
|
-
* int, int64, float, string, double, MIDI, timestamps, blob, nil OSC arguments
|
|
67
|
-
* simple OSC address<->callback matching system
|
|
68
|
-
* support for sending responses from callback handlers in client and server
|
|
69
|
-
* extensive unit test coverage
|
|
70
|
-
* basic client and server examples
|
|
71
|
-
|
|
72
|
-
Documentation
|
|
73
|
-
=============
|
|
74
|
-
|
|
75
|
-
Available at https://python-osc.readthedocs.io/.
|
|
76
|
-
|
|
77
|
-
Installation
|
|
78
|
-
============
|
|
79
|
-
|
|
80
|
-
python-osc is a pure python library that has no external dependencies,
|
|
81
|
-
to install it just use pip (prefered):
|
|
82
|
-
|
|
83
|
-
.. image:: https://img.shields.io/pypi/v/python-osc.svg
|
|
84
|
-
:target: https://pypi.python.org/pypi/python-osc
|
|
85
|
-
|
|
86
|
-
.. code-block:: bash
|
|
87
|
-
|
|
88
|
-
$ pip install python-osc
|
|
89
|
-
|
|
90
|
-
Examples
|
|
91
|
-
========
|
|
92
|
-
|
|
93
|
-
Simple client
|
|
94
|
-
-------------
|
|
95
|
-
|
|
96
|
-
.. code-block:: python
|
|
97
|
-
|
|
98
|
-
"""Small example OSC client
|
|
99
|
-
|
|
100
|
-
This program sends 10 random values between 0.0 and 1.0 to the /filter address,
|
|
101
|
-
waiting for 1 seconds between each value.
|
|
102
|
-
"""
|
|
103
|
-
import argparse
|
|
104
|
-
import random
|
|
105
|
-
import time
|
|
106
|
-
|
|
107
|
-
from pythonosc import udp_client
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if __name__ == "__main__":
|
|
111
|
-
parser = argparse.ArgumentParser()
|
|
112
|
-
parser.add_argument("--ip", default="127.0.0.1",
|
|
113
|
-
help="The ip of the OSC server")
|
|
114
|
-
parser.add_argument("--port", type=int, default=5005,
|
|
115
|
-
help="The port the OSC server is listening on")
|
|
116
|
-
args = parser.parse_args()
|
|
117
|
-
|
|
118
|
-
client = udp_client.SimpleUDPClient(args.ip, args.port)
|
|
119
|
-
|
|
120
|
-
for x in range(10):
|
|
121
|
-
client.send_message("/filter", random.random())
|
|
122
|
-
time.sleep(1)
|
|
123
|
-
|
|
124
|
-
Simple server
|
|
125
|
-
-------------
|
|
126
|
-
|
|
127
|
-
.. code-block:: python
|
|
128
|
-
|
|
129
|
-
"""Small example OSC server
|
|
130
|
-
|
|
131
|
-
This program listens to several addresses, and prints some information about
|
|
132
|
-
received packets.
|
|
133
|
-
"""
|
|
134
|
-
import argparse
|
|
135
|
-
import math
|
|
136
|
-
|
|
137
|
-
from pythonosc.dispatcher import Dispatcher
|
|
138
|
-
from pythonosc import osc_server
|
|
139
|
-
|
|
140
|
-
def print_volume_handler(unused_addr, args, volume):
|
|
141
|
-
print("[{0}] ~ {1}".format(args[0], volume))
|
|
142
|
-
|
|
143
|
-
def print_compute_handler(unused_addr, args, volume):
|
|
144
|
-
try:
|
|
145
|
-
print("[{0}] ~ {1}".format(args[0], args[1](volume)))
|
|
146
|
-
except ValueError: pass
|
|
147
|
-
|
|
148
|
-
if __name__ == "__main__":
|
|
149
|
-
parser = argparse.ArgumentParser()
|
|
150
|
-
parser.add_argument("--ip",
|
|
151
|
-
default="127.0.0.1", help="The ip to listen on")
|
|
152
|
-
parser.add_argument("--port",
|
|
153
|
-
type=int, default=5005, help="The port to listen on")
|
|
154
|
-
args = parser.parse_args()
|
|
155
|
-
|
|
156
|
-
dispatcher = Dispatcher()
|
|
157
|
-
dispatcher.map("/filter", print)
|
|
158
|
-
dispatcher.map("/volume", print_volume_handler, "Volume")
|
|
159
|
-
dispatcher.map("/logvolume", print_compute_handler, "Log volume", math.log)
|
|
160
|
-
|
|
161
|
-
server = osc_server.ThreadingOSCUDPServer(
|
|
162
|
-
(args.ip, args.port), dispatcher)
|
|
163
|
-
print("Serving on {}".format(server.server_address))
|
|
164
|
-
server.serve_forever()
|
|
165
|
-
|
|
166
|
-
Building bundles
|
|
167
|
-
----------------
|
|
168
|
-
|
|
169
|
-
.. code-block:: python
|
|
170
|
-
|
|
171
|
-
from pythonosc import osc_bundle_builder
|
|
172
|
-
from pythonosc import osc_message_builder
|
|
173
|
-
|
|
174
|
-
bundle = osc_bundle_builder.OscBundleBuilder(
|
|
175
|
-
osc_bundle_builder.IMMEDIATELY)
|
|
176
|
-
msg = osc_message_builder.OscMessageBuilder(address="/SYNC")
|
|
177
|
-
msg.add_arg(4.0)
|
|
178
|
-
# Add 4 messages in the bundle, each with more arguments.
|
|
179
|
-
bundle.add_content(msg.build())
|
|
180
|
-
msg.add_arg(2)
|
|
181
|
-
bundle.add_content(msg.build())
|
|
182
|
-
msg.add_arg("value")
|
|
183
|
-
bundle.add_content(msg.build())
|
|
184
|
-
msg.add_arg(b"\x01\x02\x03")
|
|
185
|
-
bundle.add_content(msg.build())
|
|
186
|
-
|
|
187
|
-
sub_bundle = bundle.build()
|
|
188
|
-
# Now add the same bundle inside itself.
|
|
189
|
-
bundle.add_content(sub_bundle)
|
|
190
|
-
# The bundle has 5 elements in total now.
|
|
191
|
-
|
|
192
|
-
bundle = bundle.build()
|
|
193
|
-
# You can now send it via a client with the `.send()` method:
|
|
194
|
-
client.send(bundle)
|
|
195
|
-
|
|
196
|
-
License?
|
|
197
|
-
========
|
|
198
|
-
Unlicensed, do what you want with it. (http://unlicense.org)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
LICENSE.txt
|
|
2
|
-
MANIFEST.in
|
|
3
|
-
README.rst
|
|
4
|
-
pyproject.toml
|
|
5
|
-
python_osc.egg-info/PKG-INFO
|
|
6
|
-
python_osc.egg-info/SOURCES.txt
|
|
7
|
-
python_osc.egg-info/dependency_links.txt
|
|
8
|
-
python_osc.egg-info/top_level.txt
|
|
9
|
-
pythonosc/__init__.py
|
|
10
|
-
pythonosc/dispatcher.py
|
|
11
|
-
pythonosc/osc_bundle.py
|
|
12
|
-
pythonosc/osc_bundle_builder.py
|
|
13
|
-
pythonosc/osc_message.py
|
|
14
|
-
pythonosc/osc_message_builder.py
|
|
15
|
-
pythonosc/osc_packet.py
|
|
16
|
-
pythonosc/osc_server.py
|
|
17
|
-
pythonosc/osc_tcp_server.py
|
|
18
|
-
pythonosc/slip.py
|
|
19
|
-
pythonosc/tcp_client.py
|
|
20
|
-
pythonosc/udp_client.py
|
|
21
|
-
pythonosc/parsing/__init__.py
|
|
22
|
-
pythonosc/parsing/ntp.py
|
|
23
|
-
pythonosc/parsing/osc_types.py
|
|
24
|
-
pythonosc/test/__init__.py
|
|
25
|
-
pythonosc/test/test_dispatcher.py
|
|
26
|
-
pythonosc/test/test_osc_bundle.py
|
|
27
|
-
pythonosc/test/test_osc_bundle_builder.py
|
|
28
|
-
pythonosc/test/test_osc_message.py
|
|
29
|
-
pythonosc/test/test_osc_message_builder.py
|
|
30
|
-
pythonosc/test/test_osc_packet.py
|
|
31
|
-
pythonosc/test/test_osc_server.py
|
|
32
|
-
pythonosc/test/test_osc_tcp_server.py
|
|
33
|
-
pythonosc/test/test_tcp_client.py
|
|
34
|
-
pythonosc/test/test_udp_client.py
|
|
35
|
-
pythonosc/test/parsing/__init__.py
|
|
36
|
-
pythonosc/test/parsing/test_ntp.py
|
|
37
|
-
pythonosc/test/parsing/test_osc_types.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pythonosc
|
python_osc-1.9.2/setup.cfg
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|