+++ /dev/null
-import asyncio
-import logging
-import timeit
-
-from mpdaio.client import MPDClient
-from musicpd import MPDClient as MPDClientNAIO
-
-
-async def run_cli():
- cli = MPDClient()
- cli.mpd_timeout = 0.1
- #current = await cli.currentsong()
- #print(current)
-
- #await cli.connect(host='kaliko.me', port='6601')
- cli = MPDClient(host='kaliko.me', port=6601)
- cli.mpd_timeout = 0.1
- print(await cli.currentsong())
- print(await cli.playlistinfo())
- await cli.list('artist')
- #print(await cli.playlistinfo())
- await cli.close()
-
-
-async def gather_calls():
- await asyncio.gather(
- cli.ping(),
- cli.currentsong(),
- cli.playlistinfo(),
- cli.list('artist'),
- cli.listallinfo('The Doors'),
- cli.listallinfo('The Doors'),
- cli.listallinfo('The Doors'),
- cli.listallinfo('AFX'),
- cli.listallinfo('AFX'),
- cli.listallinfo('AFX'),
- cli.find('(MUSICBRAINZ_ARTISTID == "9efff43b-3b29-4082-824e-bc82f646f93d")'),
- )
-
-
-async def aio_warmup():
- # print(len(cli._pool._connections.setdefault(host, [])))
- await gather_calls()
- # print(len(cli._pool._connections.setdefault(host, [])))
-
-
-async def aio():
- # Group tasks together
- try:
- await gather_calls()
- finally:
- # finally close
- await cli.close()
-
-
-def noaio():
- cli.ping()
- cli.currentsong()
- cli.playlistinfo()
- cli.list('artist')
- cli.listallinfo('The Doors')
- cli.listallinfo('The Doors')
- cli.listallinfo('The Doors')
- cli.listallinfo('AFX')
- cli.listallinfo('AFX')
- cli.listallinfo('AFX')
- cli.find('(MUSICBRAINZ_ARTISTID == "9efff43b-3b29-4082-824e-bc82f646f93d")')
-
-
-if __name__ == '__main__':
- # Setup
- n = 5
- host = ('kaliko.me', '6601')
- logging.basicConfig(level=logging.INFO,
- format='%(levelname)-8s %(module)-10s %(message)s')
-
- print('Running aio code')
- cli = MPDClient(*host)
- ev = asyncio.get_event_loop()
- ev.run_until_complete(aio_warmup())
-
- # Time it
- #t = timeit.Timer('ev.run_until_complete(aio_warmup())', globals=globals())
- t = timeit.Timer('ev.run_until_complete(aio_warmup())', globals=globals())
- print('Start timeit')
- print(t.timeit(n))
- print(t.timeit(n))
- ev.run_until_complete(cli.close())
-
- # Setup
- print('Running non aio code')
- cli = MPDClientNAIO()
- cli.mpd_timeout = 0.1
- cli.connect(*host)
- # Time it
- t = timeit.Timer('noaio()', globals=globals())
- print(t.timeit(n))
- print(t.timeit(n))
- cli.disconnect()
+++ /dev/null
-# -*- coding: utf-8 -*-
-#
-# python-musicpd: Python MPD client library
-# Copyright (C) 2014-2015 Kaliko Jack <kaliko@azylum.org>
-#
-# python-musicpdasio is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# python-musicpd is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with python-musicpd. If not, see <http://www.gnu.org/licenses/>.
-
-try:
- import asyncio
-except ImportError:
- import sys
- print('Failed to import asyncio, need python >= 3.4')
- sys.exit(1)
-
-import logging
-
-from os import environ
-if 'DEBUG' in environ or 'PYTHONASYNCIODEBUG' in environ:
- # environ['PYTHONASYNCIODEBUG'] = '1'
- logging.basicConfig(level=logging.DEBUG)
-
-HELLO_PREFIX = "OK MPD "
-ERROR_PREFIX = "ACK "
-SUCCESS = "OK"
-NEXT = "list_OK"
-VERSION = '0.0.1b'
-
-
-class MPDError(Exception):
- pass
-
-class ConnectionError(MPDError):
- pass
-
-class ProtocolError(MPDError):
- pass
-
-class CommandError(MPDError):
- pass
-
-
-class Response:
- def __init__(self):
- self.version = None
- self.resp = ''
-
- def __repr__(self):
- return '"{0}…" ({1})'.format(
- ' '.join(self.resp.split('\n')[:2]),
- self.version)
-
-
-class MPDProto(asyncio.Protocol):
- def __init__(self, future, payload):
- logging.debug('payload: "%s"', payload)
- self.transport = None
- self.future = future
- self.payload = payload
- self.sess = Response()
-
- def connection_made(self, transport):
- self.transport = transport
- self.transport.write(bytes('{}\n'.format(self.payload), 'utf-8'))
-
- def eof_received(self):
- self.transport.close()
- err = ConnectionError('Connection lost while reading line')
- self.future.set_exception(err)
-
- def data_received(self, data):
- #logging.debug(data.decode('utf-8'))
- rcv = self._hello(data.decode('utf-8'))
-
- if rcv.startswith(ERROR_PREFIX):
- err = rcv[len(ERROR_PREFIX):].strip()
- self.future.set_exception(CommandError(err))
-
- self.sess.resp += rcv
- if rcv.endswith(SUCCESS+'\n'):
- # Strip final SUCCESS
- self.sess.resp = self.sess.resp[:len(SUCCESS+'\n')*-1]
- logging.debug('set future result')
- self.transport.close()
- self.future.set_result(self.sess)
-
- def _hello(self, rcv):
- """Consume HELLO_PREFIX"""
- if rcv.startswith(HELLO_PREFIX):
- logging.debug('consumed hello prefix')
- self.sess.version = rcv.split('\n')[0][len(HELLO_PREFIX):]
- return rcv[rcv.find('\n')+1:]
- return rcv
-
-class MPDClient:
- """MPD Client
- :param string host: Server name or IP, default to 'localhost'
- :param integer port: Server port, default to 6600
- :param string passwd: Password, default to ``None``
-
- """
-
- def __init__(self, host='localhost', port=6600, passwd=None):
- self._evloop = asyncio.get_event_loop()
- self.asio = False
- self.futures = []
- self._host = host
- self._port = port
- #self._pwd = passwd # TODO: authentication yet to implement
- self._commands = {
- 'currentsong',
- 'stats',
- 'playlistinfo',
- 'next',
- 'find',
- }
-
- def __getattr__(self, attr):
- #logging.debug(attr)
- command = attr
- wrapper = self._command
- if command not in self._commands:
- command = command.replace("_", " ")
- if command not in self._commands:
- raise AttributeError("'%s' object has no attribute '%s'" %
- (self.__class__.__name__, attr))
- return lambda *args: wrapper(command, args)
-
- async def _connect(self, proto):
- # coroutine allowing Exception handling
- # src: http://comments.gmane.org/gmane.comp.python.tulip/1401
- try:
- await self._evloop.create_connection(lambda: proto,
- host=self._host,
- port=self._port)
- except Exception as err:
- proto.future.set_exception(ConnectionError(err))
-
- def _command(self, command, args):
- payload = command
- for arg in args:
- payload += ' "{}"'.format(escape(arg))
- future = asyncio.Future()
- # kick off a task to create the connection to MPD
- coro = self._connect(MPDProto(future, payload))
- asyncio.async(coro)
- self.futures.append(future)
- if not self.asio:
- # return once completed.
- self._evloop.run_until_complete(future)
- return future
- # alternative w/ callback
- #if not self.asio:
- # future.add_done_callback(lambda ftr: MPDClient.loop.stop())
- # self._evloop.run_forever()
- #return future
-
- def run(self):
- """Run event loop gathering tasks from self.futures
- """
- if self.futures:
- self._evloop.run_until_complete(asyncio.gather(*self.futures))
- self.futures = []
- else:
- logging.info('No task found in queue, need to set self.asio?')
-
-
-def escape(text):
- """Escapting quotes and backslash"""
- return text.replace('\\', '\\\\').replace('"', '\\"')
-