diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 6972225..ebf27fc 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -21,7 +21,7 @@ jobs: - name: install run: pip install .[dev,ci] - name: test - run: python -m pytest --reruns 5 tests/ --cov oscpy/ --cov-branch + run: python -m pytest --reruns 2 tests/ --cov oscpy/ --cov-branch - name: coveralls run: python -m coveralls env: diff --git a/oscpy/cli.py b/oscpy/cli.py index 011ef4a..99b55b1 100644 --- a/oscpy/cli.py +++ b/oscpy/cli.py @@ -15,7 +15,7 @@ def _send(options): def _parse(s): try: return literal_eval(s) - except: + except (ValueError, SyntaxError): return s stats = Stats() @@ -59,7 +59,7 @@ def dump(address, *values): return osc -def _dump(options): # pragma: no cover +def _dump(options): # pragma: no cover osc = __dump(options) try: while True: @@ -82,7 +82,8 @@ def init_parser(): help='port to send message to.') send.add_argument('--encoding', '-e', action='store', default='utf-8', help='how to encode the strings') - send.add_argument('--encoding_errors', '-E', action='store', default='replace', + send.add_argument('--encoding_errors', '-E', action='store', + default='replace', help='how to treat string encoding issues') send.add_argument('--safer', '-s', action='store_true', help='wait a little after sending message') @@ -92,9 +93,10 @@ def init_parser(): send.add_argument('address', action='store', help='OSC address to send the message to.') send.add_argument('message', nargs='*', - help='content of the message, separated by spaces.') + help='content of the message, separated by spaces.') - dump = subparser.add_parser('dump', help='listen for messages and print them') + dump = subparser.add_parser('dump', + help='listen for messages and print them') dump.set_defaults(func=_dump) dump.add_argument('--host', '-H', action='store', default='localhost', help='host (ip or name) to send message to.') @@ -102,14 +104,17 @@ def init_parser(): help='port to send message to.') dump.add_argument('--encoding', '-e', action='store', default='utf-8', help='how to encode the strings') - dump.add_argument('--encoding_errors', '-E', action='store', default='replace', + dump.add_argument('--encoding_errors', '-E', action='store', + default='replace', help='how to treat string encoding issues') - # bridge = parser.add_parser('bridge', help='listen for messages and redirect them to a server') + # TODO + # bridge = parser.add_parser('bridge', help='listen for messages and + # redirect them to a server') return parser -def main(): # pragma: no cover +def main(): # pragma: no cover parser = init_parser() options = parser.parse_args() exit(options.func(options)) diff --git a/oscpy/parser.py b/oscpy/parser.py index 4817e83..e58544e 100644 --- a/oscpy/parser.py +++ b/oscpy/parser.py @@ -46,6 +46,7 @@ MidiTuple = namedtuple('MidiTuple', 'port_id status_byte data1 data2') + def padded(l, n=4): """Return the size to pad a thing to. @@ -237,7 +238,9 @@ def format_message(address, values, encoding='', encoding_errors='strict'): if cls_or_value == UNICODE: if not encoding: - raise TypeError(u"Can't format unicode string without encoding") + raise TypeError( + u"Can't format unicode string without encoding" + ) cls_or_value = bytes value = ( @@ -274,7 +277,8 @@ def format_message(address, values, encoding='', encoding_errors='strict'): tags, *( ( - encode_cache.get(v) + NULL if isinstance(v, UNICODE) and encoding + encode_cache.get(v) + NULL + if isinstance(v, UNICODE) and encoding else (v + NULL) if t in (b's', b'b') else format_midi(v) if isinstance(v, MidiTuple) else v diff --git a/oscpy/server.py b/oscpy/server.py index 25104cb..23536e7 100644 --- a/oscpy/server.py +++ b/oscpy/server.py @@ -19,6 +19,8 @@ from oscpy.client import send_bundle, send_message from oscpy.stats import Stats +UDP_MAX_SIZE = 65535 + def ServerClass(cls): """Decorate classes with for methods implementing OSC endpoints. @@ -62,7 +64,7 @@ def __init__( """Create an OSCThreadServer. - `timeout` is a number of seconds used as a time limit for - select() calls in the listening thread, optiomal, defaults to + select() calls in the listening thread, optional, defaults to 0.01. - `drop_late_bundles` instruct the server not to dispatch calls from bundles that arrived after their timetag value. @@ -327,14 +329,14 @@ def _listen(self): continue else: try: - read, write, error = select(self.sockets, [], [], self.timeout) + read, _, _ = select(self.sockets, [], [], self.timeout) except (ValueError, socket.error): continue for sender_socket in read: try: - data, sender = sender_socket.recvfrom(65535) - except ConnectionResetError: + data, sender = sender_socket.recvfrom(UDP_MAX_SIZE) + except (OSError, ConnectionResetError): continue for address, tags, values, offset in read_packet( @@ -441,7 +443,8 @@ def send_bundle( return stats def get_sender(self): - """Return the socket, ip and port of the message that is currently being managed. + """Return the socket, ip and port of the message that is + currently being managed. Warning:: this method should only be called from inside the handling @@ -568,8 +571,9 @@ def bind_meta_routes(self, sock=None): """ self.bind(b'/_oscpy/version', self._get_version, sock=sock) self.bind(b'/_oscpy/routes', self._get_routes, sock=sock) - self.bind(b'/_oscpy/stats/received', self._get_stats_received, sock=sock) self.bind(b'/_oscpy/stats/sent', self._get_stats_sent, sock=sock) + self.bind(b'/_oscpy/stats/received', + self._get_stats_received, sock=sock) def _get_version(self, port, *args): self.answer( diff --git a/tests/test_server.py b/tests/test_server.py index cb111e0..fae5d54 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -927,3 +927,22 @@ def callback_3000(*values): assert checklist == ['a', 'b', 'c'] server_3000.stop() # clean up + + +def test_close_receiving(): + from threading import Thread + + osc = OSCThreadServer(encoding='utf8') + osc.listen(default=True) + port = osc.getaddress()[1] + + def send_messages(): + for i in range(500): + send_message(b'/flood', [b't' * 60000], 'localhost', port) + + thread = Thread(target=send_messages) + thread.start() + + # surprise, let's stop it asap! + osc.stop_all() + thread.join()