token, tokenize

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
jens
Moderator
Beiträge: 8482
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Mittwoch 4. Mai 2005, 10:45

Das in dem Thread vorgestellte online Anzeigen der Sourcen, habe ich mit einer Modifizierten Version des MoinMoin - Python Source Parser erzeugt.

Leider gibt es dabei eine Vermischung von Python built-in Module/Befehle und die bei dem Projekt selber geschriebenen Klassen... Schön wäre, wenn es in der Datenbank getrennt in zwei Tabellen gespeichert werden würde, oder zumindest du Information zusätlich, aus welchen Modulen es stammt.

Mir fällt nur ein, direkt in den Hiletexten nach der Angabe "FILE" ausschau zu halten. Anhand des Pfades kann man drauf rückschließen, ob es ein built-in ist oder nicht...

Gibt es da eine bessere/einfachere Möglichkeit das zu Unterscheiden?

Hier mein Parser:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: UTF-8 -*-

"PySource2HTML by Jens Diemer (www.jensdiemer.de)"

__version__ = "0.0.1"

__history__ = """
v0.0.1
    - erste Version
"""

__doc__ = """Parsed eine Python-Source Datei.
Schreibt dabei Hilfstexte, erzeugt mit help(), von allen vorhandenen
"Befehlen" und schreibt diese in eine DB.
Erzeugt eine HTML-Datei mit Syntaxhighlighting und Verknüpfungen bei den
gefundenen "Befehle"

Based on
    MoinMoin - Python Source Parser
    http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52298


Hinweis
-------
Man muss die aktuelle Hilfe, in HTML Version herrunterladen und am besten in
"./python/Doc/" entpacken! Ansonsten erh孴 man oft folgenden Fehler:

    Sorry, topic and keyword documentation is not available because the Python
    HTML documentation files could not be found.  If you have installed them,
    please set the environment variable PYTHONDOCS to indicate their location.

"""

import os
if os.environ.has_key("SERVER_SIGNATURE"):
    import cgitb;cgitb.enable()


import sys, cgi, string, cStringIO
import keyword, token, tokenize



class RedirectStdOut:
    """
    ermöglicht es die Ausgaben zu speichern und als String abzurufen
    wird von help2() benötigt.
    """
    def __init__(self):
        self.MeinStdOut = sys.stdout
        self.save = False
        self.data = ""

    def store( self ):
        self.save = True

    def write(self,*text):
        text=" ".join([str(i) for i in text])

        if self.save:
            self.data += text
            return

        self.MeinStdOut.write(text)

    def get( self ):
        self.save = False
        result = self.data
        self.data = ""
        return result


# stdout für help2() erweitern
sys.stdout = RedirectStdOut()


def help2( cmd ):
    """
    Liefert mit help() den Hilfetext als String zurück.
    Wird in Parser.process_help() verwendet.
    """
    sys.stdout.store()
    help( cmd )
    return sys.stdout.get()

#~ print "-"*80
#~ print help2( "cgi" )[:50]
#~ print "-"*80

#~ sys.exit()









#############################################################################
### Python Source Parser (does Hilighting)
#############################################################################

_KEYWORD = token.NT_OFFSET + 1
_TEXT    = token.NT_OFFSET + 2

_colors = {
    token.NUMBER:       '#0080C0',
    token.OP:           '#0000C0',
    token.STRING:       '#004080',
    tokenize.COMMENT:   '#008000',
    token.NAME:         '#000000',
    token.ERRORTOKEN:   '#FF8080',
    _KEYWORD:           '#C00000',
    _TEXT:              '#000000',
}


class Parser:
    """ Send colored python source.
    """

    def __init__( self, raw, helpDB, out = sys.stdout ):
        """ Store the source text.
        """
        self.raw = string.strip(string.expandtabs(raw))
        self.out = out
        self.helpDB = helpDB

        self.keycache = []

    def format(self, formatter, form):
        """ Parse and send the colored source.
        """
        # store line offsets in self.lines
        self.lines = [0, 0]
        pos = 0
        while 1:
            pos = string.find(self.raw, '\n', pos) + 1
            if not pos: break
            self.lines.append(pos)
        self.lines.append(len(self.raw))

        # parse the source and write it
        self.pos = 0
        text = cStringIO.StringIO(self.raw)
        self.out.write('<pre><font face="Lucida,Courier New">')
        try:
            tokenize.tokenize(text.readline, self)
        except tokenize.TokenError, ex:
            msg = ex[0]
            line = ex[1][0]
            self.out.write("<h3>ERROR: %s</h3>%s\n" % (
                msg, self.raw[self.lines[line]:]))
        self.out.write('</font></pre>')

    def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
        """ Token handler.
        """
        if 0:
            print "type", toktype, token.tok_name[toktype], "text", toktext,
            print "start", srow,scol, "end", erow,ecol, "<br>"

        # calculate new positions
        oldpos = self.pos
        newpos = self.lines[srow] + scol
        self.pos = newpos + len(toktext)

        # handle newlines
        if toktype in [token.NEWLINE, tokenize.NL]:
            self.out.write('\n')
            return

        # send the original whitespace, if needed
        if newpos > oldpos:
            self.out.write(self.raw[oldpos:newpos])

        # skip indenting tokens
        if toktype in [token.INDENT, token.DEDENT]:
            self.pos = newpos
            return

        # map token type to a color group
        if token.LPAR <= toktype and toktype <= token.OP:
            toktype = token.OP
        elif toktype == token.NAME and keyword.iskeyword(toktext):
            toktype = _KEYWORD
        color = _colors.get(toktype, _colors[_TEXT])

        style = ''
        if toktype == token.ERRORTOKEN:
            style = ' style="border: solid 1.5pt #FF0000;"'

        # ---------------------------------------------------------

        helplink = False
        #~ print toktext, toktype
        if toktype in [1, _KEYWORD]:
            helplink = self.process_help( toktext )

        # ---------------------------------------------------------
        if helplink:
            # Beim Hilfelink erfolgt Einfärbung über CSS
            self.out.write( """<a href="JavaScript:h('%s');">""" % toktext )
        else:
            self.out.write('<font color="%s"%s>' % (color, style))

        self.out.write(cgi.escape(toktext))

        if helplink:
            self.out.write( "</a>" )
        else:
            self.out.write('</font>')

    def process_help( self, toktext ):
        """
        Extrahiert zum >toktext< die Hiletexte und schreibt diese in die DB
        zurückgeliefert wird True oder False, jenachdem ob eine Verknüpfung
        erstellt werden soll oder nicht.
        """
        if toktext in self.keycache:
            # Hilfstext wurde schon einmal erzeugt.
            return True

        #~ print ">",toktext

        # Hilfe-Text extrahieren
        txt = help2(toktext)

        #~ print txt[:100]

        if (txt == None) or txt.startswith("no Python documentation found"):
            # Hilfe-Text nicht vorhanden -> Kein Link
            return False

        self.keycache.append(toktext)

        # Hilfetext in DB schreiben
        self.helpDB[toktext] = txt

        # Link soll erzeugt werden
        return True


if __name__ == "__main__":
    class LocalTestDB(dict):
        pass

    class stdoutnull:
        def write( self, txt):
            pass

    TestDB = LocalTestDB()

    f = file( __file__, "r")
    TestSourcen = f.read()
    f.close()

    MyParser = Parser( TestSourcen, TestDB, sys.stdout )
    #~ MyParser = Parser( TestSourcen, TestDB, stdoutnull() )
    MyParser.format(None, None)
Der Lokale Test öffnet das Skript selber und schreibt die HTML-Seite direkt nach stdout.
Noch wird ein unschönes "<font color=...>" für die einfärbung verwendet. Das muß ich bei gelegenheit durch CSS-Klassen ersetzten...

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten