2014-04-12 2 views
4

Я использую модуль python-mpd2 для управления медиаплеером на малине Pi в приложении GUI. Таким образом, я хотел бы изящно обрабатывать ошибки соединения и тайм-ауты (например, этот игрок упускает MPD-соединения через 60 секунд) в фоновом режиме. Тем не менее, модуль MPD не имеет единой точки входа, через которую отправляются все команды или информация извлекается, чтобы я мог исправлять.Проксирование класса в Python

Мне нужен класс, который позволяет получить доступ ко всем тем же методам, что и mpd.MPDClient, но давайте добавим мою собственную обработку ошибок. Другими словами, если я:

client.play() 

И ошибка connectione брошено, я хотел бы, чтобы поймать его и отправить ту же команду. Помимо небольшой задержки, вызванной повторным подключением к серверу, пользователь не должен замечать, что что-то не так.

До сих пор вот решение, которое я придумал. Он работает в моем приложении, но не выполняет мои цели.

from functools import partial 
from mpd import MPDClient, ConnectionError 

class PersistentMPDClient(object): 
    def __init__(self, host, port): 
     self.host = host 
     self.port = port 
     self.client = MPDClient() 
     self.client.connect(self.host, self.port) 

    def command(self, cmd, *args, **kwargs): 
     command_callable = partial(self.client.__getattribute__(cmd), *args, **kwargs) 
     try: 
      return command_callable() 
     except ConnectionError: 
      # Mopidy drops our connection after a while, so reconnect to send the command 
      self.client._soc = None 
      self.client.connect(self.host, self.port) 
      return command_callable() 

Я мог бы добавить метод к этому классу для каждой команды MPD, например .:

def play(self): 
    return self.command("play") 

Но это, кажется, далеко не лучший способ достигнуть этого.

+0

сколько * команд * есть в наличии? – shx2

+0

Имеются 91 команды. Не все из них необходимы или будут использоваться в моем приложении, но их большая часть. –

+0

Если вы не возражаете создать список из всех 91 строк, образующих * имена команд *, вы можете сделать что-то в соответствии с [этим ответом] (http://stackoverflow.com/a/534597/2096752). Я считаю, что этот подход имеет много преимуществ, потому что он включает в себя меньше магии. OTOH, 91 действительно много, поэтому более подходящим будет более магическое решение на основе '__getattr__'. – shx2

ответ

4

Если вы не возражаете против создания списка из всех 91 строк, образующих , имена команд, вы можете что-то сделать по линиям this answer. Я считаю, что этот подход имеет много преимуществ, потому что он включает в себя меньше магии.

OTOH, 91 действительно много. Так вот срабатывают само решение, используя заказной __getattr__, который возвращает обертку:

from functools import partial 
import types 

class DummyClient(object): 
    def connect(self, *a, **kw): print 'connecting %r %r' % (a, kw) 
    def play(self): print 'playing' 
    def stop(self): print 'stopping' 

class PersistentMPDClient(object): 
    def __init__(self, host, port): 
     self.host = host 
     self.port = port 
     self.client = DummyClient() 
     self.client.connect(self.host, self.port) 

    def __getattr__(self, attr, *args): 
     cmd = getattr(self.client, attr, *args) 
     if isinstance(cmd, types.MethodType): 
      # a method -- wrap 
      return lambda *a, **kw: self.command(attr, *a, **kw) 
     else: 
      # anything else -- return unchanged 
      return cmd 

    def command(self, cmd, *args, **kwargs): 
     command_callable = partial(self.client.__getattribute__(cmd), *args, **kwargs) 
     try: 
      return command_callable() 
     except ConnectionError: 
      # Mopidy drops our connection after a while, so reconnect to send the command 
      self.client._soc = None 
      self.client.connect(self.host, self.port) 
      return command_callable() 

c = PersistentMPDClient(hostname, port) 
c.play() 
c.stop() 

Как я писал это, я заметил @MatToufoutu разместил аналогичное решение (есть некоторые различия, хотя) , Я не знаю, почему он/она удалил его ... Если этот ответ будет восстановлен, я с радостью отдам ему должное.

+0

Фактически, я удалил свой ответ, потому что этот подход не позволяет выполнять обработку ошибок при вызове метода 'command' (который хочет OP). Возможная обработка ошибок делает вещи немного сложнее, но приходит еще один ответ :) – MatToufoutu

+0

@MatToufoutu не ясно, какая ошибка обработки отсутствует ... – shx2

+1

мой плохой, что-то не так (суббота, вы знаете ^^) , хотя ваш ответ более умен, чем то, что я опубликовал ранее (проверка типов и обработка аргументов), я оставлю его удаленным;) – MatToufoutu

Смежные вопросы