Python Modul erweitern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
martinjo
User
Beiträge: 186
Registriert: Dienstag 14. Juni 2011, 20:03

Hallo

Schon seit Ewigkeiten versuche ich den Schlüsselbund des Systems mit Python richtig zu verwenden. Nun glaube ich herausgefunden zu haben, dass dieses Modul hier eigentlich genau dafür geschaffen wurde:
https://github.com/jaraco/keyring/tree/ ... g/backends

Nun habe ich damit nur ein Problem, es wird nur ein Benutzername und ein Passwort gespeichert, es sollen aber noch weitere Infos (Attribute) wie z.b. eine URL hinterlegt werden können.

Es muss also die Funktion set_password dieses Scriptes hier angepasst werden: https://github.com/jaraco/keyring/blob/ ... Service.py

Bisher mache ich ja einfach:

Code: Alles auswählen

import keyring
keyring.set_password("system", "username", "password")
Wie gehe ich den nun am besten vor, wenn ich dieses Modul erweitern möchte. Vielen Dank


edit:
vermutlich am beten einfach die Klasse überschreiben, nur wie erhalte ich die default Argumente und für nur ein neues hinzu:

Code: Alles auswählen

class Keyring(keyring.backends.SecretService.Keyring):
    def set_password(self):
        print "test"
        return
edit2: Meine Lösung sieht bisher so aus, ich übergebe also zusätzlich data:

Code: Alles auswählen

    def set_password(self, service, username, password, data=None):
        """Set password for the username of the service
        """
        collection = self.get_preferred_collection()
        attributes = {
            "application": self.appid,
            "service": service,
            "username": username
        }
        if data:
            attributes.update(data)
        label = "Password for '%s' on '%s'" % (username, service)
        collection.create_item(label, attributes, password, replace=True)

Aber damit sind nun ja alle anderen Methoden der Klasse weg!?
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wieso nicht einfach das machen, was die Methode auch macht:

Code: Alles auswählen

 collection = self.get_preferred_collection()
 attributes = {
            "application": self.appid,
            "service": service,
            "username": username,
            # deine attribute
        }
label = "Data for '%s' on '%s'" % (username, service)
collection.create_item(label, attributes, password, replace=True)
get_preferred_collection ist doch sogar oeffentlich.

Mal abgesehen davon, das etwas "set_password" zu nennen, aber dann etwas deutlich anderes zu machen auch keine schoene API ist.

Last but not least: wenn das Ding tut, was es tun soll, dann fork es doch, bau es um, und mach einen PR. Aber wie gesagt, set_password wuerde ich das nicht nennen. Sondern set_attributes oder so, und dann set_password als Standardfall das benutzen lassen.

Nachtrag: deinen letzten Kommentar verstehe ich nicht. Was ist dann warum weg?
Benutzeravatar
martinjo
User
Beiträge: 186
Registriert: Dienstag 14. Juni 2011, 20:03

Nachtrag: deinen letzten Kommentar verstehe ich nicht. Was ist dann warum weg?
Oh, davon ging ich aus da die Methode der zu überschreibenden Klasse in der Methode meiner Klasse nicht mehr gefunden wurde:

Code: Alles auswählen

class Keyring(keyring.backends.SecretService.Keyring):
    def set_password_and_attributes(self, service, username, password, data=None):
        """Set password for the username of the service
        """
        collection = self.get_preferred_collection()
        attributes = {
            "application": self.appid,
            "service": service,
            "username": username
        }
        if data:
            attributes.update(data)
        label = "Password for '%s' on '%s'" % (username, service)
        collection.create_item(label, attributes, password, replace=True)
        
Keyring().set_password_and_attributes("system", "username", "password")
response:

Code: Alles auswählen

Traceback (most recent call last):
  File "KeyringManager.py", line 43, in <module>
    Keyring().set_password_and_attributes("system", "username", "password")
  File "KeyringManager.py", line 29, in set_password_and_attributes
    collection = self.get_preferred_collection()
AttributeError: 'Keyring' object has no attribute 'get_preferred_collection'
Benutzeravatar
martinjo
User
Beiträge: 186
Registriert: Dienstag 14. Juni 2011, 20:03

Der Fehler hier war, dass ich eine ältere Version installiert habe ohne die Methode get_preferred_collection

Wenn ich nun jedoch direkt das Module überschreibe, warum eigentlich nicht einfach so:

Code: Alles auswählen

import keyring

def set_password(self, service, username, password):
    collection = self.get_default_collection()
    attributes = {
        "service": service,
        "username": username
        }
    label = "Password for '%s' on '%s'" % (username, service)
    collection.create_item(label, attributes, password, replace=True)
    
keyring.backends.SecretService.Keyring.set_password = set_password

keyring.set_password("system", "username", "password")

Somit kann ich ja weiter die Basisfunktionen des Modules nutzen mit angepasster Klasse, im Gegensatz dazu kann ich ja beim erweitern der Klasse nur diese Klasse des Modules nutzen. Sprich andere Funktionen des Moduls würden ja sonst nicht auf die modifizierte Klasse zugreifen können.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das nennt sich monkey-patching. Gehen tut das, ist halt raeudig. Wehe du haettest mal code (ggf von einer dritten Partei, oder auch nur innerhalb dieses Paketes selbst), der das alte Verhalten will, und du hast da irgendwas nachtraeglich veraendert. Dann kracht es, und dein Kopf faengt an zu rauchen bei der Frage, woran's liegt.

Wie gesagt, der wirklich richtige weg ist: fork, etwas eigenes entwickeln, PR und hoffen, dass es angenommen wird. Und ich wuerde dann eben den Namen veraendern, weil beliebige Attribute so mit einem wenig sagenden data-Sack-Argument einzufuehren finde ich keine klare API. Da wuerde ich einen extra call fuer vorsehen.
Antworten