Pergunta

Estou usando o módulo python-mpd2 para controlar um media player em um Raspberry Pi em um aplicativo GUI.Portanto, gostaria de lidar normalmente com erros de conexão e tempos limite (o player em questão interrompe as conexões MPD após 60 segundos) em segundo plano.No entanto, o módulo MPD não tem um único ponto de entrada através do qual todos os comandos são enviados ou são recuperadas informações que eu possa corrigir.

Eu gostaria de uma classe que permitisse acesso a todos os mesmos métodos do mpd.MPDClient, mas deixe-me adicionar meu próprio tratamento de erros.Em outras palavras, se eu fizer:

client.play()

E um erro de conexão é gerado, gostaria de capturá-lo e reenviar o mesmo comando.Além do pequeno atraso causado pela necessidade de se reconectar ao servidor, o usuário não deve perceber que algo está errado.

Até agora, aqui está a solução que encontrei.Está funcionando na minha aplicação, mas realmente não atende aos meus objetivos.

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()

Eu poderia adicionar um método a esta classe para cada comando MPD, por exemplo:

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

Mas esta parece estar longe de ser a melhor maneira de conseguir isso.

Foi útil?

Solução

Se você não se importa em criar uma lista de todas as 91 strings que formam nomes de comandos, você pode fazer algo como esta resposta.Acredito que esta abordagem tem muitas vantagens porque envolve menos magia.

OTOH, 91 é realmente muito.Então aqui está uma solução automágica, fazendo uso de um custom __getattr__, que retorna um wrapper:

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()

Enquanto escrevia isso, percebi que @MatToufoutu havia postado uma solução semelhante (embora existam algumas diferenças).Não sei por que ele apagou...Se essa resposta não for excluída, eu ficaria feliz em dar-lhe o crédito que ela merece.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top