Domanda

Sto usando il modulo Python-MPD2 per controllare un lettore multimediale su un Raspberry PI in un'applicazione GUI. Quindi, vorrei gestire con grazia errori e timeout di connessione (il giocatore in questione cade le connessioni MPD dopo 60 secondi) in background. Tuttavia, il modulo MPD non ha un singolo punto di ingresso attraverso il quale vengono inviati tutti i comandi o le informazioni che sono riparati a patch.

Vorrei una classe che consente di accedere a tutti gli stessi metodi di mpd.mpdclient, ma io aggiunci la mia gestione degli errori. In altre parole, se lo faccio:

client.play()
.

E un errore di connection è lanciato, vorrei prenderlo e inviare nuovamente lo stesso comando. Oltre al piccolo ritardo causato dal dover ricollegare al server, l'utente non dovrebbe notare che qualsiasi cosa è ammiss.

Finora, ecco la soluzione con cui ho trovato. Sta lavorando nella mia applicazione, ma non soddisfa davvero i miei obiettivi.

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

Potrei aggiungere un metodo a questa classe per ogni singolo comando MPD, ad esempio.:

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

Ma questo sembra lontano dal modo migliore per realizzarlo.

È stato utile?

Soluzione

Se non ti dispiace creare un elenco di tutte le 91 stringhe che formano nomi di comando , puoi fare qualcosa lungo le linee di Questa risposta .Credo che questo approccio abbia molti vantaggi perché coinvolge meno magia.

otoh, 91 è davvero molto.Quindi ecco una soluzione automatica, facendo uso di un __getattr__ personalizzato, che restituisce un 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()
.

Come stavo scrivendo in questo, ho notato @Mattoufoutu aveva pubblicato una soluzione simile (ci sono alcune differenze, però).Non so perché lo ha cancellato ... Se quella risposta viene indelettata, darei volentieri il credito che merita.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top