Die Annahmen dass der Pfad zum Skript immer als erstes in `sys.path` steht, und dass eine Datei sofort vom Garbage Collector abgeräumt wird und man deshalb kein explizites `close()` braucht, sind IMHO gewagt. Das sind beides Implementierungsdetails, die sich jederzeit ändern können.
Zum Verbinden von Pfadelementen würde ich `os.path.join()` der "manuellen" Konkatenation mit `os.sep` vorziehen.
Für Plugins ist es sinnvoll `sys.path` zu manipulieren, aber `functions` hätte man in ein Package stecken können. Oder eben nicht in ein Unterverzeichnis, sondern auf die gleiche Ebene wie `decrypter`.
Dann ist die Namensgebung sowohl von den Konventionen als auch von der Bedeutung nicht immer einwandfrei. Zum Beispiel kommt sehr häufig der Postfix 'list' bei Namen vor die an Dictionaries gebunden sind. Da würde ich den irreführenden "Typzusatz" ganz weglassen und einfach nur die Bedeutung in den Namen packen, also `plugins` statt `pluginlist` oder `instances` statt `instancelist`. Und `plugin_instance` sagt auch nicht viel mehr aus als `plugin`. Gleiches gilt für den Namenszusatz `class`. Klassen erkennt man an der Konvention das die Namen in MixedCase geschrieben sind. Wobei der Unterschied Klasse vs. Funktion auch nur wirklich wichtig ist, wenn man eine Unterklasse von dem Objekt ableiten will, ansonsten sind beides "callables".
`decrypt_main()` ist mir viel zu kompliziert. Die ist zu lang zu tief verschachtelt und da merkt man das irgendwo Dokumentation fehlt, die einen Überblick über den Entwurf bietet.
Von der Funktion aus wird auch viel zu tief in andere Datenstrukturen "durchgegriffen". So etwas wie ``plugin_instance.instancelist[name].url_list.append(link)`` wird schnell sehr unübersichtlich weil man beim lesen bei jedem Punktoperator erst einmal klären muss was für ein Objekt man an der entsprechenden Stelle eigentlich hat. Ausserdem hat man beim "Durchgriff" seine Finger in Objektinterna, die man manchmal besser dem Objekt selbst überlässt. Es ist zum Beispiel nicht möglich im `hoster_class`-Objekt von einer Liste auf ein `set()` umzustellen, ohne das der Client-Code davon unberührt bleibt.
Eine Menge Punktoperatoren kann man in der Funktion einsparen, wenn man in der äussersten Schleife nicht über die Namen, sondern gleich über die Objekte iteriert.
Bei den Operationen, die auf den Links durchgeführt werden, bieten sich `set()`\s statt Listen an, wenn ich das richtig sehe.
Bei Threads ist das `threading`-Modul etwas komfortabler als das eher Low-Level `thread`-Modul. Die `exitmutexes` kann man wunderbar mit `join()`-Methode ersetzen. Womit auch die `hoster_class_master.thread_decrypt()`-Methode überflüssig wird.
Wenn man die Ausgabe in ein `StringIO`-Objekt umleitet, sammelt sie sich im Speicher an. Falls das zu einem Problem werden kann, gibt's unter `os.devnull` einen Dateinamen von einer Datei in der alles verschwindet. Das Original `sys.stdout` muss man sich nicht selbst merken, das steht auch unter `sys.__stdout__` noch einmal zur Verfügung.
Auf zu den `functions`. Eine `GET`-Methode die `POST` aufruft und in `POST` unter Umständen eine `GET`-Anfrage absetzt sieht verwirrend und unsauber aus.
Die drei `get_*`-Methoden vom `FakeBrowser` kann man weglassen und stattdessen gleich auf die Attribute zugreifen.
Die beiden Funktionen für Passwort bzw. Captcha sind nahezu identisch und können durch eine ersetzt werden. Der Name `StringIO_Object` sagt viel zuviel über den Typ und gar nichts über die Bedeutung des Objekts. `captcha_file` wäre zum Beispiel passender.
Das `master`-Argument würde ich an der ersten Stelle in der Argumentliste belassen, das wird bei Widgets an der Stelle erwartet.
Bei vielen Widgets wird die Erzeugung auf mehr Zeilen verteilt als nötig ist, weil man die Informationen oft als Schlüsswelwortargumente bei der Initialisierung mitgeben kann. Zum Beispiel den Text bei `Label`-Objekten.
Beim `Button` benutzt man normalerweise das `command`-Argument für eine Funktion die beim Drücken ausgeführt werden soll.
`hex_unescape()` kann man einfacher schreiben:
Code: Alles auswählen
In [22]: '507974686f6e'.decode('hex')
Out[22]: 'Python'
Bei der Ausgabe von jeweils nur einer Zeile ist Sperren überhaupt nicht nötig. Das `lock` ist IMHO auch an der falschen Stelle. Das Objekt das geschützt werden soll ist `sys.stdout`, darum gehört auch das `lock` eher in dessen "Nähe" und nicht in die `hoster_class_master`-Klasse.