[Pkg-mpd-commits] [python-mpd] 50/91: asyncio: Initial support
Simon McVittie
smcv at debian.org
Sat Feb 24 14:55:34 UTC 2018
This is an automated email from the git hooks/post-receive script.
smcv pushed a commit to branch debian/master
in repository python-mpd.
commit dcb2a00f1b5874dc1cebec3e15029c8cc8a80f35
Author: chrysn <chrysn at fsfe.org>
Date: Thu Apr 6 21:57:10 2017 +0200
asyncio: Initial support
---
examples/asyncio_example.py | 31 +++++++++++
mpd/asyncio.py | 124 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 155 insertions(+)
diff --git a/examples/asyncio_example.py b/examples/asyncio_example.py
new file mode 100644
index 0000000..39d985d
--- /dev/null
+++ b/examples/asyncio_example.py
@@ -0,0 +1,31 @@
+import asyncio
+
+from mpd.asyncio import MPDClient
+
+async def main():
+ print("Create MPD client")
+ client = MPDClient()
+ try:
+ await client.connect('localhost', 6600)
+ except Exception as e:
+ print("Connection failed:", e)
+ return
+
+ print("Connected to MPD version", client.mpd_version)
+
+ try:
+ status = await client.status()
+ except Exception as e:
+ print("Status error:", e)
+ return
+ else:
+ print("Status success:", status)
+
+ for x in await client.decoders():
+ print("sync decoder:", x)
+
+ async for x in client.decoders():
+ print("async decoder:", x)
+
+if __name__ == '__main__':
+ asyncio.get_event_loop().run_until_complete(main())
diff --git a/mpd/asyncio.py b/mpd/asyncio.py
new file mode 100644
index 0000000..e5ac2cd
--- /dev/null
+++ b/mpd/asyncio.py
@@ -0,0 +1,124 @@
+import asyncio
+
+from mpd.base import HELLO_PREFIX, ERROR_PREFIX, SUCCESS
+from mpd.base import MPDClientBase
+from mpd.base import MPDClient as SyncMPDClient
+from mpd.base import ProtocolError, ConnectionError
+from mpd.base import mpd_command_provider
+
+ at mpd_command_provider
+class MPDClient(MPDClientBase):
+ __run_task = None # doubles as indicator for being connected
+
+ async def connect(self, host, port=6600, loop=None):
+ self.__loop = loop
+
+ if '/' in host:
+ r, w = await asyncio.open_unic_connection(path, loop=loop)
+ else:
+ r, w = await asyncio.open_connection(host, port, loop=loop)
+ self.__rfile, self.__wfile = r, w
+
+ self.__commandqueue = asyncio.Queue(loop=loop)
+
+ await self.__hello()
+
+ self.__run_task = asyncio.Task(self.__run())
+
+ def disconnect(self):
+ self.__run_task.cancel()
+ self.__rfile = self.__wfile = None
+ self.__run_task = self.__commandqueue = None
+
+ async def __run(self):
+ while True:
+ command, args, callback, result = await self.__commandqueue.get()
+ self._write_command(command, args)
+ responselines = await self.__read_full_output()
+ try:
+ result.set_result(callback(self, responselines))
+ except Exception as e:
+ result.set_error(e)
+
+ # helper methods
+
+ async def __readline(self):
+ """Wrapper around .__rfile.readline that handles encoding"""
+ data = await self.__rfile.readline()
+ try:
+ return data.decode('utf8')
+ except UnicodeDecodeError:
+ self.disconnect()
+ raise ProtocolError("Invalid UTF8 received")
+
+ def __write(self, text):
+ """Wrapper around .__wfile.write that handles encoding."""
+ self.__wfile.write(text.encode('utf8'))
+
+ # copied stuff from base
+
+ async def __hello(self):
+ # not catching the timeout error, it's actually pretty adaequate
+ try:
+ line = await asyncio.wait_for(self.__readline(), timeout=5)
+ except asyncio.TimeoutError:
+ self.disconnect()
+ raise ConnectionError("No response from server while reading MPD hello")
+
+ # FIXME this is copied from base.MPDClient._hello
+ if not line.endswith("\n"):
+ self.disconnect()
+ raise ConnectionError("Connection lost while reading MPD hello")
+ line = line.rstrip("\n")
+ if not line.startswith(HELLO_PREFIX):
+ raise ProtocolError("Got invalid MPD hello: '{}'".format(line))
+ self.mpd_version = line[len(HELLO_PREFIX):].strip()
+
+ # FIXME this is just a wrapper for the below
+ def _write_line(self, text):
+ self.__write(text + "\n")
+ # FIXME this code should be sharable
+ _write_command = SyncMPDClient._write_command
+
+
+ async def __read_output_line(self):
+ """Kind of like SyncMPDClient._read_line"""
+ line = await self.__readline()
+ if not line.endswith("\n"):
+ self.disconnect()
+ raise ConnectionError("Connection lost while reading line")
+ line = line.rstrip("\n")
+ if line.startswith(ERROR_PREFIX):
+ error = line[len(ERROR_PREFIX):].strip()
+ raise CommandError(error)
+ if line == SUCCESS:
+ return None
+ return line
+
+ async def __read_full_output(self):
+ """Kind of like SyncMPDClient._read_lines, but without the iteration"""
+ result = []
+ while True:
+ line = await self.__read_output_line()
+ if line is None:
+ return result
+ else:
+ result.append(line)
+
+ # command provider interface
+
+ @classmethod
+ def add_command(cls, name, callback):
+ if hasattr(cls, name):
+ # twisted silently ignores them; probably, i'll make an
+ # experience that'll make me take the same router at some point.
+ raise AttributeError("Refusing to override the %s command"%name)
+ def f(self, *args):
+ result = asyncio.Future()
+ self.__commandqueue.put_nowait(
+ (name, args, callback, result)
+ )
+ return result
+ escaped_name = name.replace(" ", "_")
+ f.__name__ = escaped_name
+ setattr(cls, escaped_name, f)
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mpd/python-mpd.git
More information about the Pkg-mpd-commits
mailing list