Seite 1 von 1

Oniguruma Wrapper [noch im Aufbau]

Verfasst: Dienstag 25. September 2007, 22:28
von poker
Topic: Wrapper für die Regex-Engine Oniguruma 5.9.0

@birkenfeld: Danke für den Ausführlichen post. Wenn ich dich richtig verstanden habe, sollte der Wrapper eine Python C-Extension sein, der auf C ebene mit der Python API Oniguruma wrappt. Diese C-Extension muss mit VS 7 oder VS 8 kompiliert werden, damit die Extension unter Python 2.5@Win läuft. Anschließend wird die Extension über Python eingebunden das widerum die Extension Wrappt. Kurz (?): PyOnigurumaWrapper => PyOnigurumaC-Extension => libonig.a

Folgende Fragen dazu: Verstehe ich das richtig das ich für das kompilieren einer Python Extension den gleichen Kompiler verwenden muss mit dem `libpython25.a' kompiliert wurde, weil es sonst beim linken der Extension gegen die `libpython25.a' zu Problemen kommen kann (Daher die Empfehlung zu VS 7 oder VS 8 )?

Ich wollte das ganze eigentlich mit `ctypes` realisieren. Was spricht dagegen? Mein bisheriger Wrappcode (Eher Testcode) nutzt `ctypes`.

@blackbird: Schicke ich dir gerne zu, wenn es lauffähig ist und vom Status Testcode weck ist. Bisher beschränkt sich das ganze nur auf die definierung der Konstanten von `oniguruma.h' und der public API und ist nicht sonderlich schön strukturiert (Ist halt gechakter testcode). Mal sehen, wenn ich morgen zeitig zu Hause bin, schreibe ich's fertig und ne Abstraktion. Den Code könnte ich dir dann im laufe der Woche zusenden.

Um Unicode habe ich mir bisher keine Gedanken gemacht, das lässt sich aber sicherlich direkt über Python in eine für Oniguruma verständliche form transformieren.

Falls das mit `ctypes` keine so gute Idee ist, lasst es mich bitte wissen und auch mit Begründung.

Probleme:
Hab gestern onig unter Win gebacken gekriegt. Hab http://www.mingw.org/ und http://www.mingw.org/msys.shtml benutzt. Lief alles reibungslos (libonig.a einwandfrei, `make check' auf ohne Fehler) bis auf das erzeugen der DLL.

Konkret: `configure' == IO; `make' spuckt mir folgende Meldung aus:
libtool: link: warning: undefined symbols not allowed in i686-pc-mingw32 shared libraries
(Was mich auch ein wenig wunder ist, dass mein Sys als 686 erkannt wird und nicht als 386, hmm.)

OK, hab `make dll' gemacht und das spuckte mir ne dll aus. Hab aber gestern keine Zeit mehr gehabt um zu sehen ob es funktioniert, bin auch erst eben gerade wider zurück. Werd es morgen testen.

mfg

----
Weiterführung von diesem thread.

Re: Oniguruma Wrapper [noch im Aufbau]

Verfasst: Mittwoch 26. September 2007, 19:12
von birkenfeld
poker hat geschrieben:Topic: Wrapper für die Regex-Engine Oniguruma 5.9.0

@birkenfeld: Danke für den Ausführlichen post. Wenn ich dich richtig verstanden habe, sollte der Wrapper eine Python C-Extension sein, der auf C ebene mit der Python API Oniguruma wrappt. Diese C-Extension muss mit VS 7 oder VS 8 kompiliert werden, damit die Extension unter Python 2.5@Win läuft. Anschließend wird die Extension über Python eingebunden das widerum die Extension Wrappt. Kurz (?): PyOnigurumaWrapper => PyOnigurumaC-Extension => libonig.a
Ja, wobei ein Python-Modul optional ist, wenn die C-Extension alle nötigen Funktionen und Klassen schon bereitstellt.
Ich wollte das ganze eigentlich mit `ctypes` realisieren. Was spricht dagegen? Mein bisheriger Wrappcode (Eher Testcode) nutzt `ctypes`.
Spricht eigentlich nichts dagegen, wenn das ctypes-Interface ausreicht. Dann sparst du dir natürlich, überhaupt C zu schreiben.
Um Unicode habe ich mir bisher keine Gedanken gemacht, das lässt sich aber sicherlich direkt über Python in eine für Oniguruma verständliche form transformieren.
Gerade das ist aber eine spannende Frage, da Python einmal intern zwei verschiedene Unicode-Formate verwendet (2 bzw. 4 bytes pro character) und das ganze natürlich auch einigermaßen schnell gehen sollte.
Gerade da ist auch die Frage, ob ctypes mitspielt.

Re: Oniguruma Wrapper [noch im Aufbau]

Verfasst: Mittwoch 26. September 2007, 19:48
von mitsuhiko
poker hat geschrieben:Ich wollte das ganze eigentlich mit `ctypes` realisieren. Was spricht dagegen? Mein bisheriger Wrappcode (Eher Testcode) nutzt `ctypes`.
Du dürftest Probleme haben die Structs nachzubasteln fürchte ich. Ich glaub das beste ist die onguruma Funktionen in C ein wenig wrappen und den Rest in Python erledigen. Aber gerade die Oniguruma Unicode Case Folding Sachen müssen vorher noch genauer Betrachtet werden, und wie man mit Bytestrings umgeht. Nimmt man an, dass die in einem Encoding vorliegen? Oder fällt man zurück auf ASCII. Und dann ist halt noch das von birkenfeld angesprochene UCS2/4 Problem.

Verfasst: Donnerstag 27. September 2007, 08:44
von poker
=Encodings
Hmm, I see. Die Sache mit den Encodings scheint dann wirklich ein Problem darzustellen. Das es so kompliziert ist hätte ich nicht gedacht; kenne mich aber mit den *internen* Umsetzung von Encodings auch nicht aus.

Mein Vorstellung war dahingehend, das ich über Python Bordmittel schauen wollte welches Encoding vorliegt, und dann an `onig_new` das entsprechende `OnigEncoding` übergebe. Das ist aber insofern scheiße, da encoding guess total ineffizient ist (Zumindest auf Python Seite nicht schnell genug(?). Müsste man auf C verlegen, oder? Wie macht das SRE(?)).

Ich muss sagen das Thema Encoding und dessen interne Realisierung übersteigt mein Wissen! Bei dem Punkt bin ich raus.

=UCS2/UCS4
Das hängt doch aber auch vom build ab oder? So weit ich das hier verstehe ist `Py_UNICODE` von type `wchar_t` und je nach build `unsigned short` oder `unsigned long`.

Onigurumas `OnigUChar` ist ein typedef auf `unsigned char`. In `onig_search` wird er für den zu durchsuchenden string genutzt (WTF?!). `unsigned char` ist doch ein byte und ein cast von `unsigned short` auf `unsigned char` ist nicht möglich, weil der Wertbereich zu gering ist. So langsam aber sicher bezweifel ich ob sich Oniguruma überhaupt ohne unverhältnismäßigen Aufwand vollständig wrappen lässt.

Irgendwelche Vorschläge?

=Oniguruma struct`s über ctypes
@blackbird: Es geht, ich habe fast alle struct`s in ctypes definieren können und scheinen auch zu Funktionieren (Hoffentlich). Das wirklich schwierigste war `OnigEncodingType`, da ich erstmal raus finden musste wie man Sachen der Form

Code: Alles auswählen

int (*is_mbc_newline)(const OnigUChar *p, const OnigUChar *end);
umsetzt. Auch

Code: Alles auswählen

int (*apply_all_case_fold)(OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg);`
war nicht einfach, da `OnigApplyAllCaseFoldFunc ` ein callback der form

Code: Alles auswählen

typedef int (*OnigApplyAllCaseFoldFunc)(OnigCodePoint from,
                                        OnigCodePoint* to, int to_len, void* arg);
ist. Hat ein wenig gedauert bis ich die struct mit solchen Membern in ctypes hinbekommen habe.


[1] Um sicherzustellen das die unter ctypes nachgebaute `OnigEncodingType` Funktioniert, habe ich mir `OnigEncodingType` in stark minimierter Form in eine C Datei mit dazugehörigen Testcode gepackt. Das ganze dan mit ctypes eingebunden und getestet. Schien zu laufen. Aber ob`s 100%ig Funktioniert, wird sich erst zeigen.


=Python C API
Müsste ich mich erst einarbeiten; und das ist nicht von Heute auf Morgen geschehen.

mfg

Verfasst: Donnerstag 27. September 2007, 15:49
von mitsuhiko
Also mein oniguruma wrapper (aka ponyguruma) sieht momentan so aus: http://dev.pocoo.org/hg/sandbox/file/tip/ponyguruma/

Und er matcht noch nicht, und ehrlichgesagt hab ich auch gerade die Lust verloren den Bug zu suchen ^^

Verfasst: Donnerstag 27. September 2007, 16:11
von poker
Hi, kurz überfolgen. Werde mir den Code gleich näher anschauen.
Wann genau hast du damit angefangen den zu schreiben, wenn man mal fragen darf?

EDIT: Hehe, wie ich sehe hast das gleiche Prob mit ``ONIG_ENCODING_KOI8``. :D

EDIT2:
Wow! Schaut sehr gut aus! :)

Also so castest du den pystring nach unsigned char?

Code: Alles auswählen

state->string = (UChar*)((PyStringObject*)string)->ob_sval;
BTW: Ich arbeite heut Abend an das ctypes ding weiter. Dann könnte man das als fallback für die windows user nutzen ;)

Verfasst: Donnerstag 27. September 2007, 21:12
von mitsuhiko
poker hat geschrieben:Also so castest du den pystring nach unsigned char?

Code: Alles auswählen

state->string = (UChar*)((PyStringObject*)string)->ob_sval;
Weil oniguruma uchars mag das für den Moment die einfachste Lösung zum Testen ist. Die Unicode Probleme mach ich später eh noch weg. Hauptsache es tut was, dass ich testen kann :-)
BTW: Ich arbeite heut Abend an das ctypes ding weiter. Dann könnte man das als fallback für die windows user nutzen ;)
Da bau ich lieber einen Windows Build oder lass mir einen Bauen ^^

So. Hab den Bug mal rausgemacht, jetzt kann man ein paar Dinge damit tun (wenn es auch noch nicht schneidet):

Code: Alles auswählen

>>> from ponyguruma import Regexp
>>> r = Regexp("(?<foo>.)")
>>> m = r.search("blub")
>>> dir(m)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__len__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'begin', 'end', 'names_to_index', 'span', 'spans', 'state']
>>> m.state
<ponyguruma._lowlevel.MatchState object at 0x50e128>
>>> dir(m.state)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__']
>>> m.begin()
0
>>> m.end()
1
>>> m.names_to_index
{'foo': 1}
>>> m.begin("foo")
0

Verfasst: Freitag 28. September 2007, 06:03
von poker
Jetzt mal ernsthaft. Wann hast du genau angefangen das ding zu schreiben?
blackbird hat geschrieben: Da bau ich lieber einen Windows Build oder lass mir einen Bauen ^^
Kein Problem. Na, dann viel Spaß bei deinem neuen Projekt :) Ich such mir dann mal in der Zwischenzeit was anderes...

Verfasst: Freitag 28. September 2007, 10:07
von mitsuhiko
poker hat geschrieben:Jetzt mal ernsthaft. Wann hast du genau angefangen das ding zu schreiben?
Vor zwei Tagen.

Nutzt leider noch immer UTF-8 intern, aber es matcht jetzt:

Code: Alles auswählen

>>> from ponyguruma import *
>>> Regexp("<([a-z]+)>").search("haha <foo> hehe")[1]
'foo'

Verfasst: Freitag 28. September 2007, 10:30
von poker
Ironie nicht erkannt im zweiten Absatz? Ein wenig frech ist es ja schon da du mein Projekt geklaut hast. Beschissener ist es aber das du anscheinend garnicht interessiert bist an einer zusammenarbeit. Sei es drum, kannst es gerne haben.

BTW: Mein "fallback" matcht nun auch :) Konnte zwar nicht alles mit ctypes machen und habe daher ein par Zeilen in C geschrieben, aber es läuft nun endlich.

Ich wrapp da mal in der zwischen zeit die PCRE Regexe Engine :D ... falls du mir nicht zuvorkommst.

BTW2:
blackbird hat geschrieben:

Code: Alles auswählen

>>> from ponyguruma import *
>>> Regexp("<([a-z]+)>").search("haha <foo> hehe")[1]
'foo'

Code: Alles auswählen

Regexp("<([a-z]+)>").search("blubb <isa> ;)")[1][::-1]
/off

Verfasst: Freitag 28. September 2007, 11:38
von mq
poker hat geschrieben:Ironie nicht erkannt im zweiten Absatz? Ein wenig frech ist es ja schon da du mein Projekt geklaut hast.
Geklaut? Du hast eine Idee gepostet, und blackbird hat angefangen, sie umzusetzen (uebrigens voellig unabhaengig von deinem Code). Niemand hindert dich dran, das auch zu tun, Ideen sind (sofern man sie veroeffentlicht) an sich Allgemeingut (oder sollten es sein, gewisse Teile bestimmter Industriezweige sehen das leider anders). Wenn du nicht willst, das jemand anders deine Ideen verwendet, dann behalt sie fuer dich.
Beschissener ist es aber das du anscheinend garnicht interessiert bist an einer zusammenarbeit. Sei es drum, kannst es gerne haben.
Oeh, wo sagt er hier, dass er das alleine machen will? Im Gegensatz zu dir veroeffentlich er sogar seinen Code (und aktualisiert das Ganze sogar regelmaessig), waehrend du hier rumflamest. Irgendwie sieht das fuer mich so aus, als seist du da deutlich weniger kooperativ als er.

Verfasst: Freitag 28. September 2007, 11:48
von BlackVivi
poker hat geschrieben:Ironie nicht erkannt im zweiten Absatz? Ein wenig frech ist es ja schon da du mein Projekt geklaut hast. Beschissener ist es aber das du anscheinend garnicht interessiert bist an einer zusammenarbeit. Sei es drum, kannst es gerne haben.
Mann nennt's auch Freiheit. Wenn du's so schlimm findest, solltest du Python nich benutzen, find ich oO' Ich fänds gut, wenn sich jemand an meinem geistigen Gut ergötzt... Auch wenn er genau dasselbe macht. Irgendwas is immer anders...

Immerhin gibts auch mehrere Implementationen von dieser .net Geschichte... IronPython und .netpy oder so oo' Und ironpython hat sich durchgesetzt. Oft basieren dann Projekte auf dem Code eines anderen oder auf der Idee und dadurch entsteht was grossartiges. Das find ich ja so schön an Open Source.

Verfasst: Freitag 28. September 2007, 23:05
von birkenfeld
Kleines Statusupdate: der Wrapper kann jetzt direkt mit Unicodestrings umgehen.

Außerdem ist quasi die gesamte Funktionalität von sre implementiert. Nächster Schritt: Tests und Doku schreiben... :cry:

Verfasst: Samstag 29. September 2007, 01:23
von mitsuhiko
birkenfeld hat geschrieben:Außerdem ist quasi die gesamte Funktionalität von sre implementiert. Nächster Schritt: Tests und Doku schreiben... :cry:
Und dann noch einen Benchmark, damit man weiß ob das ganze auch was gebracht hat. Die erweiterten Oniguruma Features alleine sind nicht gerade ein Totschlag Argument für eine weitere Library :-)

@poker: Ich geh auf die Anschuldigungen jetzt nicht weiter ein, niemand hindert dich daran sich dem Projekt anzuschließen. Da gibts genug zu tun.

Verfasst: Samstag 29. September 2007, 23:34
von mitsuhiko
Benchmark gibts jetzt einen, aber der ist eher ein Argument für sre:
5.2 Sekunden versus 0.2 Sekunden fürs kompilieren von 10000 Regular Expressions, 5.8 fürs Matchen der selbigen versus 0.5. Die besseren Werte stehen auf der Seite von SRE. Möglicherweise ist der Benchmark auch einfach komplett falsch, wer Lust hat kann was besseres Beisteuern.

Ansonsten ist das Ding jetzt schon auf SRE Level, birkenfeld arbeitet noch an Python-Escapes (Oniguruma hat da andere) und dann müsste man noch ein paar der Funktionen vom Python Layer nach C umlegen (zum Beispiel die sub() Funktion), aber so wie es scheint ist das Performance Problem in Oniguruma selber.

Die History Groups sind auch noch nicht verfügbar, da stellt sich aber die Frage wie die in Python dargestellt werden sollen. Übergibt man da start()/end()/span()/group() dann mehrere Argumente? Oder ein Tuple? Wie sieht der Rückgabewert aus. etc.

Verfasst: Donnerstag 4. Oktober 2007, 20:31
von poker
So, bin jetzt erst wider zurück und deshalb mein verspäteter Post.
blackbird hat geschrieben:Möglicherweise ist der Benchmark auch einfach komplett falsch, wer Lust hat kann was besseres Beisteuern.
"Ist er auch"[1], was aber weniger an dir liegt als an Oniguruma :)

Dir ist schon bewusst, das die Optimierung von Oniguruma noch in einer "sehr frühen" Phase ist? Kosako arbeitet an seiner Engine auch gerade erst ca. 4-5 Jahren und Optimierungen hat er erst "kürzlich" vorgenommen (geschätzt vor ca. 1,5 Jahren). Dabei hat er es auch par mal geschafft das einige Optimierungen zu Verschlechterung der Laufzeit geführt haben (Die er aber wider gefixt hat), was IMO normal und Menschlich ist, wenn man berücksichtigt was für ein Monster eine Regexp-Engine eigentlich ist **und** das er **alleine** die Engine entwickelt! :) Das ist IMO alles bekannt und ich kann daher deine Verwunderung nicht wirklich nachvollziehen.

Ans Herz gelegt, wenn gewünschte, sei dir dieser thread[2]. Ich würde mich da garnicht wunder das selbst im 5.Y.Z-Zweig da noch einiges unentdecktes lauert.

[1][2]: Unter dem Kontext des verlinkten threads kann es durchaus sein, das du ein par Ausdrücke erwischt hast, die für eine nicht beabsichtigte Erhöhung der Laufzeit sorgen; ähnlich wie das Problem mit den zwei Quantoren, das erst ab ca. Oniguruma 4.3.0 auftrat.

blackbird hat geschrieben: und dann müsste man noch ein paar der Funktionen vom Python Layer nach C umlegen (zum Beispiel die sub() Funktion), aber so wie es scheint ist das Performance Problem in Oniguruma selber.
Ich denke nicht das es was zur Verbesserung dre Laufzeit beiträgt Funktionen wie sub in C auszulagern, da bei SRE sehr viel mehr in ``sre_*.py`` ausgelagert ist als z.B. in deinem Wrapper und es dennoch der Performance nicht merklich verschlechtert. Z.B.: Erzeugen ja eine der ``sre_*.py``S einen Kompletten AST in menschen lesbarer Form die dann auch wider auf *.py Seite erst in eine für _sre.c lesbarer Form überführt werden:
poker hat geschrieben:

Code: Alles auswählen

In [87]: import sre_parse as srep
In [88]: import sre_compile as srec
In [89]: srep.parse(r"\*")
Out[89]: [('literal', 42)]
In [90]: unichr(42)
Out[90]: u'*'
In [91]: srep.parse(r"x|y|z")
Out[91]: [('in', [('literal', 120), ('literal', 121), ('literal', 122)])]
In [92]: srep.parse(r"x|y|zz")
Out[92]: [('branch', (None, [[('literal', 120)], [('literal', 121)], [('literal', 122), ('literal', 122)]]))]
In [93]: p = srep.parse("\*",0)
In [94]: srec._code(p,0)
Out[95]: [17, 8, 3, 1, 1, 1, 1, 42, 0, 19, 42, 1]
In [96]:
Das und vieles mehr macht ja dein Wrapper nicht und daher müsste dein wrapper sogar schneller sein. Daher liegt das Performance Problem an Oniguruma und nicht an deinem Wrapper.

Verfasst: Freitag 5. Oktober 2007, 22:31
von mitsuhiko
Den Thread kenn ich ;-) Zum Python Zeug: Der Parser in Python ist kein Problem, der Rest läuft in C ab. Aber das Strings in Python zerschneiden kann schon sehr teuer werden. Auf alle Fälle ist ponyguruma für den Moment ziemlich uninteressant weil es einfach zu langsam ist.