Veranschaulichung des doppelhierarchischen Ansatzes von CYBOP durch die Programmiersprache Python Max Brauer <[email protected]> 5. September 2012 Abstract Cybernetics Oriented Programming (CYBOP) stellt den Versuch dar, Lösungen aus der menschlichen Denkens- und Verhaltensweise in die Programmierung einieÿen zu lassen. Dieser Ansatz hat sich bereits in nahezu allen anderen Wissenschaftsbereichen durchgesetzt. Lediglich in der Programmierung ist der Mensch den Gegebenheiten der Maschienen unterworfen. Gemündet ist dieses Vorhaben in der XML-basierten Programmiersprache CYBOL und dem Interpreter CYBOI[1]. Die vorliegende Arbeit versucht den Ansatz der in CYBOP genutzten Doppelhierarchie[2, 7.3.3 Double Hierarchy, Seite 235] auf die Programmiersprache Python zu übertragen, um so die Vorteile dieses Vorgehens zu verdeutlichen und Entwicklern den Einstieg in CYBOL zu erleichtern. Inhaltsverzeichnis 3.2 Weitergehende Meta- Programmierung in Python 1 Einleitung 1 2 CYBOPS Doppelhierarchie 2 3.3 4 2.1 Modelles . . . . . . . . . . . . 2 2.2 Schema 3 2.3 Die . . . . . . . . . . . . Doppelhierarchie Zusammenfassung 6 7 8 9 1 Einleitung und deren praktischer Nutzen . . . Warum Metaprogrammierung? Literatur Herleitung des hierarchischen . 3 Schon seit vielen tausend Jahren beobachtet 3 Darstellung mittels Python 3.1 Der 4 der Mensch die Natur und versucht das da- doppelthierarchische Ansatz in Python . . . . . . . raus resultierende Wissen auf den täglichen 5 Gebrauch zu adaptieren. Ein Beispiel für 1 2 CYBOPS Doppelhierarchie eine solche Adaption ist das Einsetzen des Lotuseektes: Dank Nanopartikeln ist es möglich eine Oberäche so zu gestalten, Eine essenzielle Erkenntnis der Forschun- das keine Schmutzpartikel an ihr haften gen bleiben.[3] von alles die, dass beinhaltet. ten Räume mit Büchern. Die Bücher en- gramming im Kapitel 5 Extended Moti- thalten Kapitel, welche Absätze enthalten. vation mit folgenden Worten: Diese wiederum enthalten Sätze, welche aus Wörtern Inspect solutions of various and bestehen[2, Seite 231]. Im sel- ben Kapitel werden noch weitere Beispiele other disciplines of science, phenature, war Hierachie Knowledge Ontology: Bücherein enthal- für das Buch Cybernetics Oriented Pro- of Heller gewisse Beispiele dafür bringt er im Kapitel 7.3.1 Christian Heller beschreibt seine Motivation nomenons Christian eine genannt. ap- ply them to software engineer- Der doppelthierarchische Ansatz ist in ing out Kombination mit dem Schema der zen- be trale Verständnispunkt der kybernetikorien- if ... in existing order to nd weaknesses can tierten Programmierung. Dabei werden zwei eliminated. [2, Seite 161] Hierarchien verwendet: Ein Wissensbaum, welcher die Beziehungen der Objekte un- Auch er versucht diesen Ansatz, welcher tereinander hierarchisch abbildet, stellt die wieder und wieder zu Durchbrüchen in der erste Hierarchie dar. Teilweise wird dieser Wissenschaft geführt hat: Durch das her- Strang anziehen von Naturphänomenen und Ergeb- auch einzelnen Objekten die Zweite. sollen Schwächen in modernen Programmierparadigmen beseitigt werden. Dieses Kapitel Herleitung die Beschäftung mit dem Mikro-/Makro-Kosmos bezeichnet. Die Metainformationen an den nissen aus anderen Wissenschaftsdisziplinen Durch als Körp- der beschäftigt der Herleitung Hierarchie des im sich mit (Kapitel Buch oft der 2.1), erwäh- er, dem Geist, dem menschlichen Denken nten Schemas (Kapitel 2.2), sowie des und vielen anderen technischen und wis- Zusammenschlusses senschaftlichen, aber auch philosophischen und Punkten hat sich der Ansatz einer Dop- 2.3). deren zur praktischen Doppelhierarchie Nutzen (Kapitel pelhierarchie herauskristallisiert. Das nachfolgende Kapitel beschäftigt sich mit der Model, Herleitung hin zu von einem einem 2.1 Herleitung des hierarchischen Single Modelles Hierarchischem Modell, sowie dem zu Grunde liegenden Während der Erstellung von CYBOP ging Schema um zu verdeutlichen, wieso von ein- die er Doppelhierarchie gesprochen wird. Am Entwicklung der einem Single Model Ende des Kapitels, wird für dieses Wis- Datenstruktur von Ansatz über einen semistrukturierten Ansatz hin zu einem - sen eine mögliche praktische Anwendung nalen Ansatz, in welchem die Struktur über vorgestellt. eine Hierarchie abgebildet wurde. Das abschlieÿende Kapitel stellt den dop- Das Single Model ist die heutzutage am pelthierarchischen der weitesten verbreitete Struktur. Singel Mod- Programmiersprache Python dar und ver- el steht für einziges Model. Hierbei wird sucht das zuvor gefundene Beispiel zu real- für isieren. gelegt, welche anschlieÿend untereinander Ansatz mit Hilfe 2 jeden Typen eine eigene Klasse an- 2.2 Schema verweisen. Auf Seite 218 zeigt Heller[2] hierfür ein Beispiel: Die Klasse Person hat mehrere Attribute wie zum Beispiel das Der erste Teil der Doppelhierarchie wurde Geburtsdatum, Geschlecht, der Name oder im voran gegangenen Kapitel erläutert. Nun die Adresse. Der Name und die Adresse sind fehlt lediglich noch der zweite Teil: Das dabei komplexere Strukturen und haben Schema. Abbildung 1 ist dem Buch Cy- jeweils eine eigene Klasse. Im selben Kapi- bernetics Oriented Programming entnom- tel werden noch einige Probleme an diesem men und zeigt den Aufbau des Schemas, Ansatz aufgezeigt. So ist zum Beispiel eine welchem ein jedes Objekt (Ausnahmen bie- Erweiterung ten hier primitive Datentypen) in CYBOP bestehender Strukturen nur schwer möglich. zu Grunde liegen. Es braucht einen eindeutigen Bezeichner (name) sowie eine Ab- Darauf aufbauend existiert das Semi Structured Model, strukturiertes werden laut was übersetzt Modell Heller[2, straktion (abstraction). Letzteres ist mit teilweise bedeutet. Seite219] dem Typ eines Objektes in anderen Pro- Hierbei grammiersprachen vergleichbar. Alle weit- Named eren Informationen werden dynamisch in Values verwendet. Diese werden in Listen gespeichert und können details gespeichert. Die in Kapitel 1 1 dynamisch zu hinzugefügt und verändert werden. Auÿer- Relation in Bezug auf model wird für die im Kapitel 2.1 vorgestellte Hierarchie dem haben sie alle den einheitlichen Typ benötigt. Diese Informationen stammen aus Item. Das n dem Kapitel 7.3.2 des Buches Cybernetics hierarchische Model baut nun Oriented Programming. Für weitere Infor- wiederum auf das Semi Structured Model mationen empfehle ich dieses Kapitel, sowie auf. Heller beschreibt dieses Model im Kapi- die Kapitel 7.1.3 und 7.2.5.[2] tel 7.2 Design Reections unter der Überschrift Hierarchical Model [2, Seite 220]. Nachdem nun kurz der Aufbau des Schemas Es gibt lediglich eine verbleibende Klasse, vorgestellt wurde, wird sich im nächsten welche keine fest denierten Attribute hat. Kapitel mit der aus dem Schema sowie der Diese werden dynamisch hinzugefügt und hierarchischen Struktur entstehenden Dop- verwenden dabei dasselbe Model. So ent- pelhierarchie beschäftigt. Auÿerdem wird steht auf eine Hierarchie, welche stark dem Struktur ndet im egal wie klein oder praktischer Nutzen groÿ, sind Teil von gröÿeren. Im Umkehrschluss In den Kapiteln 2.1 und 2.2 wurde das hi- gilt, alle Dinge enthalten kleinere. Nehmen erarchische Modell, sowie das in CYBOP wir hierzu folgendes Beispiel: Es existiert ein beliebiges Sonnensystem. Dieses verwendete Schema vorgestellt. Beide Mod- ex- elle ergeben zusammen den doppelthierar- istiert wiederum in unserer Sonnenstraÿe (Makro-Kosmos) und kann eingegan- 2.3 Die Doppelhierarchie und deren Hierarchie dar: den Mikro-/Makro-Kosmos. Dinge, Nutzen cybernetischen Ansatz ihe Verwendung. Sie stellt die erste Alle praktischen gen. menschlichen Denken angelehnt ist. Diese einen Planeten chischen Ansatz, welche den gedanklichen en- Kern beim kybernetischen Programmieren thalten (Mikro-Kosmos). Dies könnte man darstellen. nun weiter fortführen. Neben dem Mirko/Makro-Kosmos gibt es noch eine zweite Es gibt mehrere Objekte, welche in ein- Hierarchie, weshalb von einer Doppelhier- er achie gesprochen wird. Die zweite Hierar- den. Dabei sollte die hierarchische Verknüp- chie ist das Schema und wird im nachfolgen- fung der Objekte logisch erfolgen um eine den Kapitel vorgestellt. Nachvollziehbarkeit zu ermöglichen. Jedes 3 hierarchischen Struktur abgelegt wer- Abbildung 1: Knowledge Schema mit Metainformationen[2, Seite 234] einzelnen Objekte formationen, verfügt welche über dynamisch Metain- groÿe Teile der Verständlichkeit und des hi- hinzuge- erarchischen Ansatzes verloren. fügt und bearbeitet werden können. Diese Metainformationen können ebenfalls in ein- Das nachfolgende Kapitel versucht genau er hierachischen Form vorliegen. das zu überbrücken: Es wird der doppelt- CYBOP stellt nicht den erste hierarchische Ansatz von CYBOP versucht Versuch in dar, in der Informatik daten hierchisch zur Stelle abzulegen. So objektorientierte seien an Zur dieser Datenbanken Objektorientierten Programmier- sprache Python zur Verfügung zu stellen. Verfügung zu stellen, zu verwenden oder persistent der persistenten Datenhaltung wird die zuvorgenannte ZODB[4] verwendet. Bei der wie ZODB handelt es sich um die Zope Object die ZODB für Python[4] oder db40 Database, einer objektorientierten Daten- für Java oder .NET[5] zu nennen, welche bank, welche dem Zope-Projekt[6] entsprun- meist die Möglichkeit einer hierachischen gen ist. Verwendung mitbringen. Der Vorteil solcher Datenbanken liegt in der fehlenden Abstraktionsschicht zwischen Datenbank und 3 Darstellung mittels Python Anwendung. Bei relationalen Datenbanken ist eine ORM (Object Relational Mapper) Schicht nötig um die Daten aus der Datenbank in Objekte zu serialisieren Der und Heller, nach dem Ändern rialisieren und zu wieder zu dese- kybernetischorientierte welcher im Kapitel Ansatz 2 nach vorgestellt Allerdings wurde, setzt einen hohen Grad an dyna- werden die hierarchisch vorliegenden Ob- mischer Programmierung vorraus. Diesen jekte anschlieÿend mit anderen Program- hatte mierparadigmen vermischt. Dadurch gehen CYBOL[1] speichern. 4 Heller in der erschaen. Programmiersprache Aber auch andere Programmiersprachen können diese An- zurückgegeben werden. Auf das forderung erfüllen. Python ist Beispiel angewendet, sieht dies wie folgt eine sehr dynamische aus: Pro- 1 grammiersprache, in welcher es mit wenig Aufwand möglich archischen Testobject ist Ansatz den zu doppelthier- 2 implementieren. >>> myobject.__getattribute__(' name') 'Testobject' Dieses Kapitel beschäftigt sich mit diesem Vorhabden und erläutert, welche Dieses Wissen genügt, um die notwendi- weit- gen eren Möglichkeiten in Python über Meta- zu Programmierung möglich wären. Funktionen in implementieren hinzufügen und eine eigene Klasse um das dynamische lesen von Attributen an Objekten zu ermöglichen. Funktionen, deren 3.1 Der doppelthierarchische Ansatz in Namen Python trachten. welche fügt in jedes viel Funktionalität mit Python Objekt zu in nutzen. Python So ver- über eine __setattr__(prop, value)-Methode. greifen Attribute hinzufügen. In gendem Beispiel wird eine Klasse duch hinzugefügt. Punkt-Notation Anschlieÿend darauf 2 3 4 5 6 7 8 9 10 11 diese können, der wird, wird eine Funktio- deutiger Pfad Liste Properties gemäÿ zum Objekt zur vewendet. nach deutiger Bezeichner) sowie der fol- den name path im An(ein(ein- Wissens- baum) gespeichert. Der Pfad ist notwendig um anhand des Objektes herausnden zu At- können, in welchem Punkt des Wissens- dy- baumes er gespeichert liegt. kann zugegrien 1 # ~ encoding: utf-8 ~ 2 >>> class Testobject(object): ... pass ... >>> myobject = Testobject() >>> myobject.name Traceback (most recent call last): File "<stdin>", line 1, in < module> AttributeError: 'Testobject' object has no attribute 'name' >>> myobject.__setattr__('name', ' Testobject') >>> myobject.name 'Testobject' 3 5 6 7 8 def __init__(self, name, path= ""): self.name = name self.path = path self.properties = [] 9 10 11 12 13 def add_property(self, prop, value): """ """ self.__setattr__(prop, value) 14 Zum Auslesen einmal gesetzter Attribute 16 gibt es ebenfalls eine Funktion in Python: 17 Damit class Part(object): 4 15 __getattribute__(prop). werden forderungen an das Schema, der werden: 1 zu Auÿerdem Ihr Test- object erstellt, welche nicht über das tribut name verfügt. Dieses wird erst Daher Datenhaltung einen Wert übergeben und auf diesem Wege namisch begin- erzeit auf alle verfügbaren Attribute zu- kann man einen Variablenbezeichner, sowie dynamisch __ mit in get_property() beziehungsweise add_property() gekapselt. Um auch jed- sich bringt um den doppelthierarchischen Ansatz Python nen Python ist eine dynamische Programmiersprache, in nen, sind als private Funktionen zu be- kann 18 über den Namen des Attributes desen Wert 5 def get_property(self, prop): """ """ return self. __getattribute__(prop) Um den Mikro-/Makro-Kosmos (beziehungsweise Hierarchie) die abbilden zu Diese 23 Zeilen reichen aus um den dop- Teil-/Ganzeskönnen, pelthierarchischen müssen Ansatz in nutzen. Durch das Erben von Python zu Persistent auch die Parts, welche in dem aktuellen ist es auch möglich, sie in der ZODB Part existieren, gespeichert werden. Hierzu abzuspeichern. Die Dateien part.py und bietet sich ein Dictionary, also eine Liste start.py sind im Anhang zu nden. Bei der von Schlüssel-Wert-Paaren an. Auÿerdem start.py handelt es sich um einen Webser- sollen die Daten persistent in der objekto- vice, welcher es erlaubt rientieren Datenbank ZODB gespeichert namisch anzulegen und abzurufen. werden. Gegenwärtig erbt die Klasse von der object. allgemeinen Part-Objekte dy- Part Python-Superklasse 3.2 Weitergehende Um die Persistenz zu gewährleis- Meta-Programmierung in Python ten, sowie um die Klasse wie ein Dictionary verwenden zu können, wird statt von object von den Klassen Persistent und UserDict geerbt. Um auf die Parts speich- Mit der Klasse, zuvor sowie weiteren, ern zu können, muss das Klassen-Attribut tel data Programmierung als Dictionary vorliegen. Sind all vorgestellten erläuterten in diesem Möglichkeiten ist diese Anforderungen implementiert, sieht äuÿerst dynamische die Klasse wie folgt aus: stellen. Python es Kapi- der Meta- möglich, Anwendung bietet Part- neben zu dem eine erdy- namischen Hinzufügen von Attributen noch 1 2 3 # ~ encoding: utf-8 ~ import persistent from UserDict import UserDict die Möglichkeit zur Laufzeit Funktionen, Klassen Bei 4 9 10 11 12 15 16 17 18 21 22 23 spricht man an- im Folgenden kurz vorgestellt und sind allesamt (Wissen sowie abgeänderte Beispiele) def __init__(self, name, path= ""): self.name = name self.path = path self.properties = [] self.data = {} den Vortrag Python Metaprogramming for Mad Scientists and Evil Geniuses von Walker Hale entnommen[7]. Synthetische Funktionen def add_property(self, prop, value): """ """ self.__setattr__(prop, value) self.properties.append(prop ) In vielen Scriptsprachen beziehungsweise dynamischen Programmiersprachen ist es möglich, Programmcode aus Strings di- rekt einzulesen. In JavaScript ist dies zum Beispiel über die Funktion Python wird dafür eval möglich. In exec verwendet. Im Fol- genden ein Beispiel: 19 20 Objekten implementieren. Modulen. Diese drei Möglichkeiten werden 13 14 zu Klassen/ class Part(persistent.Persistent, UserDict): 7 8 solchen Module schlieÿend von synthetischen Funktionen/ 5 6 und 1 def get_property(self, prop): """ """ return self. __getattribute__(prop) 2 3 4 5 6 7 6 >>> d = {} >>> exec """\ ... def say_hello_to(name): ... print("Hello %s" % (name)) ... """ in d >>> d.keys() ['__builtins__', 'say_hello_to'] 8 9 10 >>> say_hello_to = d['say_hello_to '] >>> say_hello_to("Bob") Hello Bob Synthetische Module Mit Hilfe des new Modules ist es möglich, zur Laufzeit neue Python-Module zu erzeugen. Anschlieÿend kann man sie mit Hil- Hierbei wurde die Funktion erstellt und anschlieÿend fe des say_hello_to demonstriert. Funktionen aus einem String erzeugt wer- 1 den, können sie ein enormes Sicherheits- 2 risiko darstellen! 3 4 5 Synthetische Klassen 6 7 In dem Vortrag von Walker Hale wird klar, dass eine Klasse in Python nichts anderes 8 ist als ein Dictionary, welches auf Funktionen zeigt. In Python gibt es die Funktion welche den Typen eines Objektes zurück gibt. Diese kann allerdings auch dazu genutzt werden, generisch neue Klassen zu erstellen: 1 2 3 4 5 6 7 8 9 10 11 9 10 11 12 >>> def __init__(self, name): ... self.name = name ... >>> def say_hello_to(self, name): ... print("%s says hello to %s" % (self.name, name)) ... >>> d = dict(__init__=__init__, say_hello_to=say_hello_to) >>> SynteticClass = type(' SynteticClass', (), d) >>> obj = SynteticClass('Tim') >>> obj.say_hello_to('Bob') Tim says hello to Bob Modules registrieren und an- wie folgt von statten: Allerdings sollte man hierbei aufpassen: Da type(), sys schlieÿend normal importieren. Dies geht 13 14 15 >>> import new, sys >>> my_module = new.module(' say_hello_module', 'A fake module.') >>> def say_hello_to(name): ... print("Hello %s" % (name)) ... >>> my_module.say_hello_to = say_hello_to >>> sys.modules['say_hello_module' ] = my_module >>> del new, sys, say_hello_to, my_module >>> my_module.say_hello_to("Bob") Traceback (most recent call last): File "<stdin>", line 1, in < module> NameError: name 'my_module' is not defined >>> import say_hello_module >>> say_hello_module.say_hello_to( "Bob") Hello Bob Auch hier sollte man aufpassen: auf diese Weise ist es möglich, bestehende Module zu überschreiben. Bei sogenannten MonkyPatches (Bezeichnung für das Patchen von Code zur Laufzeit) ist dieser Eekt sogar erwünscht: Man bekommt die Möglichkeit bestehende Module zu bearbeiten, ohne ihren Code ändern zu müssen. Im eben gezeigten Beispiel wurden zwei 3.3 Warum Metaprogrammierung? Funktionen erzeugt und in einem Dictionary gespeichert. Anschlieÿend wurde daraus eine Klasse erstellt, instanziiert und die Funktion say_hello_to Nachdem nun ausführlich die Möglichkeit- ausgeführt. en der Metaprogrammierung dargelegt wurden, stellt sich in die Python Frage: Auf diese Weise ist es möglich, die Part Warum oder wofür Metaprogrammierung? Klasse dynamisch zu erweitern. Wie bereits zuvor genannt worden ist, stellt 7 Abbildung 2: Schematischer Programmablauf ohne Metaprogrammierung die hierarchische Struktur der Datenhaltung darestellen. Dazu beschäftigte Dokument das besondere an CYBOP dar. im Kapitel 2 mit den theoretischen Grundlagen Wenn nun Programmcode auf die Daten- aggieren in diesem verschaft. Im zweiten Schritt erläutert es Fall das verwendete Schema genauer und fügt nur als Wrapper. Also als Programmcode, welcher die Instanzen der Part Dies die Herleitung des hierachischen Modelles hend losgelöÿt von der hierarchischen StrukFunktionen Programmieransatzes. geschiet, indem es zu erst einen Blick auf struktur zugreift, geschiet dies weitestgetur. dieses beides abschlieÿend als Doppelhierarchie Klasse zusammen. umschlieÿt und so Funktionalität auf ihr ausführt. Die Funktionalität liegt allerd- Das ings nicht selbst bei den Instanzen. Ab- er möglichen Umsetzung des doppelthier- bildung 2 zeigt dies schematisch. Mit Hil- achischen fe der Metaprogrammierung ist es unter sprache Python. Dazu wird im Kapitel 3.1 anderem kurz das dynamische Hinzufügen von At- möglich, dynamisch Funktionen Kapitel 3 beschäftigt Ansatzes in der sich mit ein- Programmier- fügen. tributen erklärt und wie daraus eine gener- Diese Funktionen stehen anschlieÿend nicht ische Klasse entsteht. Anschlieÿend wird der an Instanzen von Objekten zu lediglich in Kapitel 3.2 weitergehende Möglichkeiten einzelnen Instanzen zur Verfügung. Durch der Metaprogrammierung in Python erklärt Metaprogrammierung kann das Ziel erreicht um schlussendlich in Kapitel3.3 kurz zu er- werden, nicht nur die Datenhaltung, son- läutern, das dank der Metaprogrammierung dern auch den Programmablauf hierarchisch die Funktionalitäten direkt in den Wissens- darzustellen. Dies wird durch Abbildung 3 baum hinzugefügt werden können, statt sie dargestellt. nebenher gesammten Klasse sondern zu verwenden. Dies ermöglicht einen weitaus dynamischeren Ansatz, welcher näher an der Programmiersprache CY- 4 Zusammenfassung BOL ist. Dieses Dokument hilft nicht nur Entwick- Diese Arbeit soll eine Handreichung zum lern, welche sich neu mit dem Programmier- besseren Verständnis des cybernetischori- ansatz CYBOP auseinander setzen dessen entierten Ansatzes nach Christian Heller theoretischen Ansatz zu verstehen, sondern 8 Abbildung 3: Schematischer Programmablauf mit Metaprogrammierung http://de.wikipedia. org/wiki/Lotuseffekt. zeigt auch, das dieser nicht nur an die - Sprache CYBOL gebunden ist. Lotuseekt, [4] Zope Foundation. Data Base, Literatur [5] VERSANT CORP. db40, db4o.com/. [1] Dr. Christian Heller. Svn Repository des http://savannah. nongnu.org/svn/?group=cybop. [6] Zope Foundation. CYBOP Projektes, [2] Dr. Christian Heller. Zodb - Zope Object http://www.zodb.org/. http://www. [7] Walker Hale. Python Metaprogramming Cybernetics Ori- for ented Programming. Tux Tax, 2006. Mad Scientists and Evil Genius- http://www.youtube.com/watch? v=Adr_QuDZxuM. es, [3] Wikipedia Foundation Inc. Zope, zope.org/. http://www. Wikipedia 9 Anhang part.py 1 2 3 # ~ encoding: utf-8 ~ import persistent from UserDict import UserDict 4 5 6 class Part(persistent.Persistent, UserDict): 7 8 9 10 11 12 def __init__(self, name, path=""): self.name = name self.path = path self.properties = [] self.data = {} 13 14 15 16 17 18 def add_property(self, prop, value): """ """ self.__setattr__(prop, value) self.properties.append(prop) 19 20 21 22 23 def get_property(self, prop): """ """ return self.__getattribute__(prop) 10 start.py 1 2 # ~ encoding: utf-8 ~ 3 4 5 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer import cgi 6 7 8 9 from ZODB.FileStorage import FileStorage from ZODB.DB import DB import transaction 10 11 from part import Part 12 13 server = None 14 15 16 class CybopHTTPServer(HTTPServer): 17 18 19 20 21 22 23 def __init__(self, (HOST, PORT), Handler): self.storage = FileStorage('Data.fs') self.db = DB(self.storage) self.connection = self.db.open() self.root = self.connection.root() HTTPServer.__init__(self, (HOST, PORT), Handler) 24 25 26 class CybopHTTPHandler(BaseHTTPRequestHandler): 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 def do_GET(self): """ """ path_stack, params = self._get_path_stack() if len(params) == 1 and 'name' in params: self.send_response(301) redirect_path = '/'.join(path_stack) + '/' + params['name']. lower() self.send_header("Location", redirect_path) self.end_headers() pass elif path_stack[0] == '': self.return_root() elif path_stack[0] == 'favicon.ico': return else: context, remaining_stack = self._travers_object(path_stack) if not remaining_stack: self.return_information_view(context) elif len(remaining_stack) == 1: self.return_add_view(context) else: self.redirect_method(context) 50 51 def do_POST(self): 11 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 """ """ path_stack, params = self._get_path_stack() context, remaining_stack = self._travers_object(path_stack) params = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers['Content-Type'], } ) new_part = Part(remaining_stack[0].title(), path=self.path) for param in params: if param[:5] == 'value': pass else: number = param[-1:] if 'value' + number in params: new_part.add_property(params[param].value, params['value' + number].value) context[remaining_stack[0]] = new_part self.send_response(201) self.send_header("Location", '/') self.end_headers() transaction.commit() 76 77 78 79 80 81 82 83 84 85 86 87 88 89 def return_information_view(self, context): """ """ heading = '<h1>%s</h1>\n<h2>%s</h2>' % (context.name, context.path) child_links = ["<li><a href='%s'>%s</a></li>" % ( context[key].path, context[key].name ) for key in context.keys()] body_text = ["<b>%s:</b><p>%s</p>" % (prop, context.get_property( prop)) for prop in context.properties] message = """%s\n %s\n <h3>All properties:</h3> %s\n 90 91 92 93 94 95 96 97 <h3>All Subpages:</h3>\n <ul>\n%s</ul> """ % (heading, self._add_new_form(), '\n'.join(body_text), '\n'. join(child_links)) self.send_response(200) self.end_headers() self.wfile.write(message) return 98 99 100 def return_add_view(self, context): """ 12 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 """ self.send_response(200) self.end_headers() heading = "<h1>Add a new Part at the path %s</h1>" % (self.path) add_form = """ <p> You can now add your information to the Part. </p> <form method="POST"> <table> <tr> <td><b>attribute name<b></td> <td><b>attribute value</b></td> </tr> <tr> <td> <input name="attribute1" type="text" size="30"/> </td> <td> <textarea name="value1" type="textarea" cols="30" rows="4"></ textarea> </td> </tr> <tr> <td> <input name="attribute2" type="text" size="30" /> </td> <td> <textarea name="value2" type="textarea" cols="30" rows="4"></ textarea> </td> </tr> <tr> <td> <input name="attribute3" type="text" size="30" /> </td> <td> <textarea name="value3" type="textarea" cols="30" rows="4"></ textarea> </td> </tr> <tr> <td> <input name="attribute4" type="text" size="30" /> </td> <td> <textarea name="value4" type="textarea" cols="30" rows="4"></ textarea> </td> </tr> <tr> <td> 13 149 150 151 152 153 154 155 156 157 158 159 160 <input name="attribute5" type="text" size="30" /> </td> <td> <textarea name="value5" type="textarea" cols="30" rows="4"></ textarea> </td> </tr> </table> <input type="submit" value=" Add "> </form> """ self.wfile.write("%s\n%s" % (heading, add_form)) return 161 162 163 164 165 166 167 168 169 170 171 def redirect_method(self, context): """ """ self.send_response(301) if context == self.server.root: self.send_header("Location", '/') else: self.send_header("Location", context.path) self.end_headers() pass 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 def _travers_object(self, path_stack, active_object=None): active_element = None if not active_object: active_object = self.server.root while path_stack != []: active_element = path_stack.pop(0) if active_element in active_object: active_object = active_object[active_element] active_element = None else: break if active_element: remaining_stack = [active_element] + path_stack else: remaining_stack = path_stack return active_object, remaining_stack 189 190 191 192 193 194 195 196 197 198 def return_root(self): heading = '<h1>Welcome to the Cybernetic Oriented Webservice</h1>' child_links = ["<li><a href='%s'>%s</a></li>" % ( self.server.root[key].path, self.server.root[key].name ) for key in self.server.root.keys()] message = "%s\n%s<h3>All Subpages:</h3>\n<ul>\n%s</ul>" % (heading, self._add_new_form(), '\n'.join(child_links)) self.send_response(200) self.end_headers() 14 199 200 self.wfile.write(message) return 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 def _get_path_stack(self): path_stack = self.path.split('/') path_stack.pop(0) if path_stack == [''] or '?' not in path_stack[-1]: return path_stack, {} else: params = {} stack_rest, param_string = path_stack.pop(-1).split('?') if stack_rest != ['']: path_stack.append(stack_rest) param_list = param_string.split(',') for param_pair in param_list: attribute, argument = param_pair.split('=') params[attribute] = argument return path_stack, params 217 218 219 220 221 222 def _add_new_form(self): return """<form> Add new Subpage with name: <input name="name" type="text" size="30" /> <input type="submit" value=" Ok "> </form> """ 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 if __name__ == '__main__': try: HOST, PORT = "localhost", 9999 server = CybopHTTPServer((HOST, PORT), CybopHTTPHandler) print("\n**********************************************************\ n" + \ "*** ***\n" +\ "*** This Server handels WebRequests and works with a ***\n" +\ "*** Cybernetics Oriented Database Model in the ***\n" +\ "*** Backend. ***\n" +\ "*** ***\n" +\ "*** Starting TCPServer on %s:%s ***\n" % (HOST, PORT) + \ "*** To shut down, press <CTRL-C> ***\n" +\ "*** ***\n" +\ "**********************************************************\n" ) server.serve_forever() except KeyboardInterrupt: pass finally: server.connection.close() server.db.pack() server.db.close() server.storage.close() 15