Allgemeines Feedback zur Code-Struktur

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
chpo7234
User
Beiträge: 35
Registriert: Dienstag 29. September 2015, 10:19

Hallo Leute,

seit ungefähr zwei Monaten beschäftige ich mich nun mit Python. Derweil habe ich schon verschiedene Scripts geschrieben.

Da ich aber nicht wirklich jemanden habe, der das mal kontrolliert, was ich entwickel, wollte ich mich gerne an das Forum wenden. Hier wurde mir stets immer gut weiter geholfen.

Es soll nicht der ganze Code kontrolliert und verbessert werden. Mir geht es vorrangig eher um die Code-Struktur, und darum, dass andere Entwickler diesen später auch gut verstehen können.

Meine Fragen wären hiernach:
Ist der Aufbau der Klassen und Methoden-Struktur so geeignet?
Ist das Setzen der Instanzvariablen innerhalb von __init__ so korrekt?
Sind die Methoden-Namen korrekt gewählt?
Ist der Inhalt innerhalb der Methoden in Ordnung - oder müsste ich einzelne Abschnitte weiter in kleinere, neue Methoden auslagen?
Sind die privaten Methoden, die innerhalb anderer Methoden aufgerufen werden, so in Ordnung?
Gibt es allgemeine Tipps?

Ich habe ein Script, welches nach Benutzereingabe einen Host aus Text-Dateien entfernt. Die Arbeitsweise ist ungefähr so:
- Es durchsucht die Textdateien nach dem Host-Namen
- Wenn der gesuchte Hostname in der Datei gefunden wurde:
-- dann wird die Textdatei durchlaufen und der Inhalt passend dargestellt, ohne diesen Host, neu abgespeichert
-- zusätzlich wird eine zweite Datei abgeändert, die die Anzahl der Hosts zählt
-- letztendlich wird der Host aus einer JSON-Datei entfernt

Meine eigene Einschätzung:
Mir kommt die Methode __removeFromHostsFile() sehr lang vor. Vllt. liegt es auch an den Kommentaren
Die privaten Methoden, die innerhalb anderer Methoden aufgerufen werden, halte ich dort für ziemlich versteckt. Ich habe das allerdings so gemacht, weil die halt nur dann ausgeführt werden sollen, wenn die überliegende Methode auch ausgeführt wird.

Das Script ist (auch aufgrund der Kommentare) leider sehr lang: ungefähr 200 Zeilen. Verzeiht 's mir.. Ich hoffe jemand hat dennoch wertvolle Tipps auf Lager, wie ich das Coding allgemein verbessern könnte.

Lieben Gruß

Code: Alles auswählen

# class HostRemover
#    __init__()
#    removeHost()
#    __removeFromDiscoveriesJson()
#    __removeFromHostsFiles()
#    __findContentFromAst()
#    __substractNumberOfHosts()
#    __cleanHostFile
 
import os
import sys
import re
import mkFileVisitor
import ast
import json
import fifi
 
class HostRemover():
    def __init__(self, hostname):
        self.hostname = hostname
        self.ownFile = os.path.basename(__file__)
 
    # public class
    # needs a list of files
    def removeHost(self, files, discoveriesJson):
        for filename in files:
            if(self.__removeFromHostsFile(filename)):
                break
        self.__removeFromDiscoveriesJson(discoveriesJson)
           
    def __removeFromDiscoveriesJson(self, discoveryFile):
        if os.path.isfile(discoveryFile):
            #load discoveries (json-object) and convert it to a list-object
            discoveries = open(discoveryFile).read()
            discoveriesDecoded = json.loads(discoveries)
            newDiscoveries = dict()
           
            # loop the discoveries-list
            # fill new list (without our host)
            for host in discoveriesDecoded:
                if self.hostname not in host:
                    newDiscoveries[host] = discoveriesDecoded[host]
       
            #convert our new list to an json-object
            newDiscoveries = json.dumps(newDiscoveries)
           
            #save the new discovieres (without our host)
            discoveries = open(discoveryFile,"w")
            discoveries.write(str(newDiscoveries))
            discoveries.close()
        else:
            print self.ownFile + " couldn't read " + os.path.abspath(discoveryFile)
           
    # such a stupid method to parse the host-files :@@@
    def __removeFromHostsFile(self, filename):
       
        with open(filename, "r+") as hostfile:
            hostInFile = False
           
            # loop the hosts-file
            # and check if it contains our host
            for line in hostfile:
                if self.hostname in line:
                    hostInFile = True
                    break
           
            if hostInFile:
                # set fileobject to begin
                hostfile.seek(0)
               
                # creating new hosts-descriptions without our hosts:
                newContent = ""
                newContent += self.__findContentFromAst(filename, "ipaddresses", "update")
                newContent += self.__findContentFromAst(filename, "host_attributes", "update")
                newContent += self.__findContentFromAst(filename, "explicit_snmp_communities", "update")
               
                # in "hosts.mk"-file are some host-descriptions
                # which are not traceable with python's AST-module:
                #
                #       host_contactgroups += [ ..content.. ]
                #       all_hosts += [ ..content.. ]
                #       extra_host_conf.setdefault('alias', []).extend( [ ..content.. ] )
                #       extra_host_conf.setdefault('parents', []).extend( [ ..content.. ] )
                #
                # so in the following i need to parse it on another way...
                contactGroupOrAllHosts = False
                extraHostConf = False
               
                for line in hostfile:
                   
                    # check if the line contains necessary keywords
                    if "host_contactgroups +=" in line or "all_hosts +=" in line:
                        contactGroupOrAllHosts = True
                    elif "extra_host_conf.setdefault('" in line:
                        extraHostConf = True
       
                    if contactGroupOrAllHosts:
                        if self.hostname not in line:
                            newContent += line
                        if "]" in line and len(line) < 3:
                            contactGroupOrAllHosts=False
                            newContent += "\n"
                    elif extraHostConf:
                        if "[(u'" in line and self.hostname in line:
                            newContent += "["
   
                        if self.hostname in line and ")])" in line:
                            newContent += "])\n\n"
                        elif self.hostname not in line:
                            newContent += line
                           
                        if ")])" in line:
                            extraHostConf = False
                            newContent += "\n"
       
                newContent = self.__cleanHostFile(newContent)
 
                hostfile.seek(0)
                hostfile.write(newContent)
                hostfile.truncate()
                watoFile = filename.split("hosts.mk")[0] + ".wato"
                self.__subtractNumberOfHosts(watoFile)
                return True
            else:
                return False
               
    def __cleanHostFile(self, content):
            # remove empty entries
            content = content.replace("all_hosts += [\n]","")
            content = content.replace("extra_host_conf.setdefault('alias', []).extend(\n[])","")
            content = content.replace("extra_host_conf.setdefault('parents', []).extend(\n[])","")
            content = content.replace("explicit_snmp_communities.update(\n{})","")
            content = content.replace("host_contactgroups += [\n]","")
            content = content.replace("ipaddresses.update(\n{})","")
            content = content.replace("host_attributes.update(\n{})","")
           
            #remove needless line-breaks
            for i in range(0,10):
                content = content.replace("\n\n\n","\n\n")
               
            return content
       
    # AST = abstract syntax tree
    # .mk-files are also written in python-syntax,
    # so we can find few content with pythons AST-module
    def __findContentFromAst(self, filename, param1, param2):
        fileContent = open(filename).read()
       
        # let us visit the file and seek for the passed parameter.
        # visitor uses the AST-module for parsing the code
        contentVisitor = mkFileVisitor.Visitor(param1, param2)
        contentVisitor.visit(ast.parse(fileContent))
       
        newContent = dict()
       
        # loop visitors result
        # save result as "newContent" without our host
        for host in contentVisitor.result:
            for item in host:
                if self.hostname not in item:
                    newContent[item] = host[item]
                   
        return param1 + "." + param2 + "(\n" + str(newContent) + ")\n\n"
       
    # This method is written for hidden ".wato"-configuration-files.
    # These files just store the number of hosts in a folder.
    # In addition these files prsent the foldername.
    def __subtractNumberOfHosts(self, filename):
        with open(filename, "r+") as watofile:
            number = 0
            title = ""
           
            for line in watofile:
                # takes the number of host while using a regular expression:
                number = re.findall('([0-9]+)', line)[0]
               
                # stores the folder-title while using split()
                if "'title': u'" in line:
                    title = line.split("'title': u'")[1].split("'}")[0]
                else:
                    title = line.split("'title': '")[1].split("'}")[0]
           
            if number:
                number = int(number)-1
           
            output = "{'attributes': {}, 'num_hosts': " + str(number) + ", 'title': u'" + title + "'}"
           
            watofile.seek(0)
            watofile.write(output)
            watofile.truncate()
 
def main():
    hostname = sys.argv[1]
    try:
        finder = fifi.FileFinder("hosts.mk", root="../../../../etc/check_mk/conf.d/wato/")
        files = finder.findFiles()
    except:
        print sys.exc_info()[0]
   
    remover = HostRemover(hostname)
    remover.removeHost(files, "../discoveries.json")
 
   
if __name__ == '__main__':
    main()
http://pastebin.com/iZtfziRf#
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

So auf die Schnelle:
- Es gibt keine "privaten"-Methoden in Python! Man kann Methoden als "intern" kennzeichnen, indem man ihnen *einen* Unterstrich als Präfix spendiert. Dies ist aber lediglich eine Konvention und natürlich steht es einem Benutzer frei, diese dennoch aufzurufen. Einen Zugriffsschutz auf Sprachebene gibt es in Python schlicht nicht! Der doppelte Unterstrich ist hingegen überhaupt nicht sinnvoll an dieser Stelle komplett unsinnig! Link

- Einige Methoden sind viel zu lang; dies kann man auch anhand der Verschachtelungstiefe rein optisch erkennen.

- Du solltest Dich an PEP8 halten (s. auch obiger Link in Gänze!), insbesondere Deine Namen sind falsch.

- Das "nackte" ``except`` sollte so nicht stehen bleiben! Fang Ausnahmen immer *explizit* ab, also nur die, die Du auch erwartest und entsprechend behandeln willst (Behandeln kann durchaus auch mal ignorieren oder nichts tun bedeuten).

- Last but not least: Ich weiß aktuell gar nicht, ob man die Klasse wirklich braucht. Im Grunde genommen nutzt Du sie doch nur, um ``hostname`` und ``ownFile`` nicht an Methoden übergeben zu müssen. Manipuliert werden sie so weit ich gesehen habe nicht. Ein Exemplar wird also keinen veränderlichen inneren Zustand haben. Ergo kann die Klasse auch durch Funktionen ersetzt werden, denen man ggf. als Parameter eine zusammenfassende Struktur übergeben kann, um zu viele Parameter zu vermeiden.

- ``ownFile`` ist auch ein hübsches Beispiel für einen semantisch falschen Namen (unabhängig von der falschen camelCase Formatierung): Dahinter verbirgt sich ein Datei*name*! Kein File-Objekt.

- Viele Kommentare sind überflüssig, da sie keinen Mehrwert zum Code darunter bieten. Ich sehe anhand einer Schleife doch, dass über ein Objekt iteriert wird - das muss ich nicht im Klartext darüber hinschreiben. Generell sollte man Kommentare nur sehr sparsam verwenden und wenn sollten diese beschreiben, *wieso* etwas gemacht wird, nicht *wie* und schon gar nicht *was*. Was gemacht wird gehört höchstens in einen Docstring!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@chpo7234: Zusätzlich was schon über den (un)sinn der Klasse geschrieben wurde: Eine Klasse die neben der `__init__()` nur eine öffentliche Methode besitzt ist oft ebenfalls ein Warnzeichen das die Klasse gar keine ist. `fifi.FileFinder` sieht vom Aufruf her also auch sehr verdächtig aus nach einer Klasse die semantisch gar keine ist. Warum dieser Drang alles in Klassen stecken zu wollen? Und ist in `fifi.py` nur diese eine Klasse definiert? Eine Datei pro Klasse ist Java und kein Python! Der Name `fifi` ist süss, aber irgendwie auch nicht wirklich gut. Abkürzungen sollte man vermeiden solange die nicht geläufig sind. Was soll Beispielsweise das `mk`-Präfix bei `mkFileVisitor` bedeuten? Namen sollen erklärend wirken und nicht zum rätselraten animieren. `param1` und `param2` sind auch so Kandidaten für wesentlich bessere Namen.

Die Ausnahmebehandlung würde so wie sie da steht auch mit einer konkreten Ausnahme keinen Sinn machen. Denn wenn eine Ausnahme dort ”behandelt” wird, dann ist `files` nicht definiert, was in der letzten Zeile der `main()`-Funktion dann unweigerlich zu einer weiteren Ausnahme führen wird die nirgends behandelt wird. Der Benutzer bekommt also in jedem Fall einen kompletten Traceback präsentiert, nur halt nicht von der Stelle die das eigentliche Problem verursacht hat.

`ownFile` kann man im Sinne des Programms als Konstante auffassen weil sich der Wert während des Programmablaufs nie ändern wird. Also gehört das eher als Konstante auf Modulebene. Dann bliebe nur noch `hostname` als (unveränderlicher) Zustand übrig, und *ein* Argument kann man wirklich an Funktionen übergeben. Tut man bei den ”falschen” Methoden mit `self` ja auch schon. Die Klasse ist also wirklich überflüssig. Wäre sie das nicht sollte man in Python 2 von `object` erben wenn man sonst keine Basisklasse hat. Sonst hat man kein „new style“-Klasse und einige Spracheigenschaften funktionieren nicht richtig. Properties beispielsweise.

Um Bedingungen bei ``if`` ``while`` gehören keine Klammern, und wenn man Klammern setzt um die Auswertungsreihenfolge vorzugeben, dann sollte zwischen dem Schlüsselwort und der Klammer ein Leerzeichen stehen, damit das nicht nach einem Funktionsaufruf aussieht.

Die `remove_from_hosts_file()` (korrigierte Namensgebung) ist so eine Funktion die etwas zu lang ist weil da mehr als eine Sache drin gemacht wird. Da wird getestet ob etwas in einer Datei enthalten ist, eine Datei gelesen, verändert, und neu geschrieben, und auch noch eine Funktion aufgerufen die eine andere Datei verändert.

Textdateien im `r+`-Modus zu öffnen und mit `seek()` und `truncate()` zu arbeiten ist auch eher ungewöhnlich. Das würde ich sein lassen.

Wiederholtes aufaddieren von Zeichenketten ist potentiell sehr ineffizient. Das sammelt man üblicherweise in einer Liste und setzt die Teilstücke am Ende mit der `join()`-Methode auf Zeichenketten zusammen.

Dateien die man öffnet sollte man auch wieder schliessen. Wenn man gleich auf das Ergebnis von einem `open()` die `read()`-Methode aufruft, dann kann man das nicht machen weil man dann an das Dateiobjekt nicht mehr heran kommt.

Das was Du in `find_content_from_ast()` `item` genannt hast ist ein `key`. Das ist sehr verwirrend weil der Begriff `item` bei Wörterbüchern schon für das Paar aus Schlüssel und Wert belegt wird. Die Funktion macht mehr als der Name aussagt. Da wird nicht nur etwas gesucht sondern auch gleich verändert.

Zeichenketten und Werte mit ``+`` und `str()` zusammenstückeln hat etwas von BASIC. In Python gibt es dafür Zeichenkettenformatierung.

`clean_hostfile()` enthält zu viele Codewiederholungen. Das schreit nach einer Schleife.

Die komplette `subtract_number_of_hosts()` sieht verdächtig danach aus als wenn man `ast.literal_eval()` verwenden könnte statt das auch wieder so wenig robust mit Zeichenkettenoperationen zu machen.

`number` ist mal eine Zahl und mal eine Zeichenkette → das ist verwirrend.

Warum `findall()` wenn man eh nur den ersten Treffer verwendet?

Ich komme dann ungefähr auf so etwas (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function 
import ast
import json
import os
import re
import sys

import fifi
import mkFileVisitor

MODULE_FILENAME = os.path.basename(__file__)


def find_content_from_ast(hostname, filename, object_name, method_name):
    """AST = abstract syntax tree
    .mk-files are also written in python-syntax,
    so we can find few content with pythons AST-module
    """
    # 
    # TODO Function does more than name suggests.
    #
    with open(filename) as mk_file:
        source = mk_file.read()
   
    # let us visit the file and search for the passed parameter.
    # visitor uses the AST-module for parsing the code
    visitor = mkFileVisitor.Visitor(object_name, method_name)
    visitor.visit(ast.parse(source))
    argument = dict()
    for host in visitor.result:
        # TODO Test really ``not in`` instead of ``!=``!?
        argument.update(
            (k, v) for k, v in host.iteritems() if hostname not in k
        )
    return '{0}.{1}(\n{2!r})\n'.format(object_name, method_name, argument)


def clean_hostfile(content):
    # remove empty entries
    for needle in [
        'all_hosts += [\n]',
        "extra_host_conf.setdefault('alias', []).extend(\n[])",
        "extra_host_conf.setdefault('parents', []).extend(\n[])",
        'explicit_snmp_communities.update(\n{})',
        'host_contactgroups += [\n]',
        'ipaddresses.update(\n{})',
        'host_attributes.update(\n{})',
    ]:
        content = content.replace(needle, '')
    # 
    # TODO Replace by `re` solution.
    # 
    # remove excessive line-breaks
    for _ in range(0, 10):
        content = content.replace('\n\n\n', '\n\n')
       
    return content


def remove_from_hosts_file(hostname, filename):
    """Such a stupid method to parse the host-files :@@@"""
    # 
    # TODO Reading and parsing the very same file three times seems quite
    #   wasteful.
    # 
    new_lines = [
        find_content_from_ast(hostname, filename, object_name, 'update')
        for object_name in [
            'ipaddresses', 'host_attributes', 'explicit_snmp_communities'
        ]
    ]

    with open(filename, 'r') as hostfile:
        # in "hosts.mk"-file are some host-descriptions
        # which are not traceable with python's AST-module:
        #
        #     host_contactgroups += [ ..content.. ]
        #     all_hosts += [ ..content.. ]
        #     extra_host_conf.setdefault('alias', []).extend([ ..content.. ])
        #     extra_host_conf.setdefault('parents', []).extend([ ..content.. ])
        #
        # so in the following i need to parse it on another way...

        # 
        # TODO According to the following code the following two flags are
        #   (should be) mutually exclusive so this may be better *one* variable
        #   with three states.
        # 
        contact_group_or_all_hosts = False
        extra_host_conf = False
       
        for line in hostfile:
            if 'host_contactgroups +=' in line or 'all_hosts +=' in line:
                contact_group_or_all_hosts = True
            elif "extra_host_conf.setdefault('" in line:
                extra_host_conf = True

            if contact_group_or_all_hosts:
                if hostname not in line:
                    new_lines.append(line)
                if ']' in line and len(line) < 3:
                    contact_group_or_all_hosts = False
                    new_lines.append('')
            elif extra_host_conf:
                if "[(u'" in line and hostname in line:
                    new_lines.append('[')

                if hostname in line:
                    if ')])' in line:
                        new_lines.append('])\n')
                else:
                    new_lines.append(line)
                
                if ')])' in line:
                    extra_host_conf = False
                    new_lines.append('')

    new_content = clean_hostfile('\n'.join(new_lines))

    with open(filename, 'w') as hostfile:
        hostfile.write(new_content)


def file_contains_hostname(hostname, filename):
    with open(filename, 'r') as hostfile:
        # 
        # XXX: Returns True if a hostname is part of any used Python keyword
        #   or name in that source file!
        # 
        return any(hostname in line for line in hostfile)


def subtract_number_of_hosts(filename):
    """This method is written for hidden ".wato"-configuration-files.
    These files just store the number of hosts in a folder.
    In addition these files prsent the foldername.
    """
    number = 0
    title = ''

    with open(filename, "r+") as wato_file:
        # 
        # TODO Replaceable with `ast.literal_eval()`!?
        # 
        for line in wato_file:
            # 
            # XXX This recognises *any* sequence of digits. Put some into the
            #   'title' or 'attributes' and this `re` gets it wrong.
            # 
            # takes the number of host while using a regular expression:
            number = int(re.search(r'\d+', line).group())
            if "'title': u'" in line:
                title = line.split("'title': u'", 1)[1].split("'}", 1)[0]
            else:
                title = line.split("'title': '", 1)[1].split("'}", 1)[0]
       
    with open(filename, 'w') as wato_file:
        wato_file.write(
            repr(
                {
                    'attributes': {},
                    'num_hosts': max(number - 1, 0),
                    'title': title
                }
            )
        )


def remove_from_discoveries_json(hostname, discoveries_filename):
    try:
        with open(discoveries_filename, 'r') as discoveries_file:
            discoveries = json.load(discoveries_file)
    except EnvironmentError:
        print(MODULE_FILENAME, "couldn't read", os.path.abspath(discoveries_filename))
    else:
        with open(discoveries_filename, 'w') as discoveries_file:
            # 
            # TODO Should the test really be ``not in`` instead of ``!=``!?
            # 
            json.dump(
                dict(
                    (k, v)
                    for k, v in discoveries.iteritems() if hostname not in k
                ),
                discoveries_file
            )


def remove_host(hostname, files, discoveries_filename):
    """needs a list of files"""
    for filename in files:
        if file_contains_hostname(hostname, filename):
            remove_from_hosts_file(hostname, filename)
            # 
            # TODO With the docstring of the following function claiming
            #   that '.wato' files are hidden files, the `split()` operation
            #   looks very suspicious.  What does this do exactly?  I strongly
            #   suggest using path operations instead of string manipulation.
            # 
            subtract_number_of_hosts(filename.split('hosts.mk', 1)[0] + '.wato')
            break
    remove_from_discoveries_json(hostname, discoveries_filename)


def main():
    hostname = sys.argv[1]
    remove_host(
        hostname,
        fifi.FileFinder(
            'hosts.mk', root='../../../../etc/check_mk/conf.d/wato/'
        ).find_files(),
        '../discoveries.json'
    )
 
   
if __name__ == '__main__':
    main()
chpo7234
User
Beiträge: 35
Registriert: Dienstag 29. September 2015, 10:19

Guten Morgen,

vorweg: es ist immer wieder erstaunlich, was die Mitglieder dieses Forums manchmal leisten. So eine großartige Hilfsbereitschaft trifft man selten. Darum also: recht herzlichen Dank für die zahlreichen nützlichen Tipps!

Auf einige Punkte werde ich noch mal kurz eingehen. Die Tipps, die dabei jetzt kommentarlos bleiben, werde ich zukünftig natürlich trotzdem beachten!
Warum dieser Drang alles in Klassen stecken zu wollen? Und ist in `fifi.py` nur diese eine Klasse definiert? Eine Datei pro Klasse ist Java und kein Python!
Ich sehe es ein, dass die Klasse keinen Sinn macht, wenn sich die Variablenwerte darin nicht verändern.. Zuvor habe ich mich immer viel mit C# beschäftigt und mein Gedankengang dabei war nun auch die Objektorientierung.. "fifi" (filefinder) möchte ich gerne als einzelne Datei beibehalten, denn die Methode wird ja ebenfalls auch in anderen Scripten benötigt. So kann ich Redundanz vermeiden. Die meisten Python-Dateien werden von der Weboberfläche (PHP) vom Benutzer ausgeführt. Wenn der Benutzer also einen Host aus den Konfigurations-Dateien entfernen möchte, denn macht es für mich nicht so viel Sinn, in dem Script "removeHost.py" auch noch Methoden (oder Klassen) zu lagern, die für andere Verwendungszwecke bestimmt sind (zum Beispiel "Host hinzufügen").
Was soll Beispielsweise das `mk`-Präfix bei `mkFileVisitor` bedeuten? Namen sollen erklärend wirken und nicht zum rätselraten animieren.
Nun ja, das Script mkFileVisitor ist für Dateien des Typs ".mk" ausgelegt.
Um Bedingungen bei ``if`` ``while`` gehören keine Klammern
Ich gehe mal davon aus, dass hiermit auf Methodenaufrufe innerhalb der Bedingung angespielt wird:

Code: Alles auswählen

if os.path.isfile(discoveryFile):

Der Tipp mit der Konstante für "ownFile" ist sehr hilfreich - da wäre ich alleine nicht drauf gekommen..

Die Code-Verbesserung sieht echt prima aus.. Viele Varianten waren mir so noch nicht wirklich bekannt. Mit einer Codezeile habe ich noch Probleme:

Code: Alles auswählen

(k, v) for k, v in host.iteritems()
Hier wird gleich mit zwei Variablen iteriert. Ich habe das so zwar schon mal irgendwo gesehen, aber selbst noch nicht angewandt.. Ich werde mich deswegen noch mal schlau machen!

Vielen Dank für die gute Kritik!
chpo7234
User
Beiträge: 35
Registriert: Dienstag 29. September 2015, 10:19

Hey,

ich habe jetzt noch mal ein Problem...

Der Output sieht beispielsweise wie folgt aus:
ipaddresses.update(
{'localhost': u'127.0.0.1', 'myhost': u'10.10.10.10'})
Alle Hosts stehen nun also in einer Zeile. Bei den zahlreichen Hosts verliert man so allerdings den Überblick, weswegen ich json's-prettyprint gerne einsetzen möchte. Hierfür habe ich die Rückgabe der Methode "find_content_from_ast" mit json.dumps und einem Indent versehen:

Code: Alles auswählen

def find_content_from_ast(hostname, filename, object_name, method_name):
    with open(filename) as mk_file:
        source = mk_file.read()
   
    content_visitor = mkFileVisitor.Visitor(object_name, method_name)
    content_visitor.visit(ast.parse(source))
    
    argument = dict()
    
    for host in content_visitor.result:
        for key in host:
            if hostname not in key:
                argument[key] = host[key]
    
    return "{0}.{1}(\n{2!r})\n".format(object_name, method_name, json.dumps(argument, indent=4))
Jetzt wird auch irgendwie versucht, das schön darzustellen. Jedoch nicht so, wie ich mir das vorgestellt habe. Zeilenumbrüche werden mit "\n" dargestellt und auch zusätzliche Anführungszeichen sind erschienen:
ipaddresses.update(
'{\n "localhost": "127.0.0.1", \n "myhost": "10.10.10.10"\n}')
BlackJack

@chpo7234: Als erstes solltest Du nicht das JSON-Modul verwenden um *Python*-Quelltext zu erstellen. Auch wenn JavaScript da sehr ähnlich ist, so ist es doch nicht 100%ig gleich. Für Python gibt es das `pprint`-Modul.

Dann schau Dir mal `format()` an und was die Syntax in den Platzhaltern die Du verwendest genau bedeutet.
Antworten