commit 247d2303b22ffea38b5a4b9f9c4fb0d89fee6783 Author: Andrea Papotti Date: Thu Nov 15 23:15:44 2012 +0100 first commit diff --git a/cgi/cgi_test.py b/cgi/cgi_test.py new file mode 100755 index 0000000..66cadc6 --- /dev/null +++ b/cgi/cgi_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + +# +# in caso di traceback mostra una pagina più leggibile +# +import cgitb +cgitb.enable() + +# +# import librerie utilizzate +# +import os +import cgi + +print "Content-type: text/html" +print + +# +# variabili d'ambiente (dall' OS) +# +print "

ambiente

" +keys = os.environ.keys() +keys.sort() + + + +for k in keys: + print "%s = %s
\n" % ( k, str( os.environ[k] ) ) + +# +# pagina di provenienza (utile per rilevare direct link da altri siti) +# +print +print +print "

pagina di provenienza

" +print os.environ.get( 'HTTP_REFERER', '' ) + + +# +# parametri della richiesta HTTP +# +print +print +print "

parametri form

" +form = cgi.FieldStorage( keep_blank_values=1 ) # non omettere i parametri senza valori + +keys = form.keys() +keys.sort() + +for k in keys: + print "%s = %s
\n" % ( k, str( form.getvalue( k, None ) ) ) + + diff --git a/cgi/cgi_wrapper.py b/cgi/cgi_wrapper.py new file mode 100755 index 0000000..9c9cfc5 --- /dev/null +++ b/cgi/cgi_wrapper.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + + +############################################################################### +## +## +## WRAPPER CGI PER IL NOSTRO FRAMEWORK WSGI +## +## +## +## !!! DA USARE SOLO INTERNAMENTE! __MAI__ IN AMBIENTE DI PRODUZIONE !!! +## +## - performance misere +## - inizializza tutto l'ambiente ad ogni richiesta +## - mostra parte del codice sorgente in caso di errori ( password, dati, ... ) +## +## +############################################################################### + + +# +# in caso di traceback mostra una pagina più leggibile +# +import cgitb +cgitb.enable() + +# +# libreria standard WSGI +# +import wsgiref.handlers + +# +# importa il dispatcher WSGI ( si trova in ../ ) +# +import sys + +wsgilib = '/'.join( __file__.split('/')[:-2] ) + +if wsgilib not in sys.path: + sys.path.insert(0, wsgilib) + +import dispatch_wsgi as mywsgi + +# +# Magia! +# +if __name__ == '__main__': + wsgiref.handlers.CGIHandler().run( mywsgi.application ) diff --git a/cgi/template1.tmpl b/cgi/template1.tmpl new file mode 100644 index 0000000..ea35cdc --- /dev/null +++ b/cgi/template1.tmpl @@ -0,0 +1,13 @@ + +il valore di v1 è {{= v1 }} +
+mentre il valore di v2 è {{= v2 }} +
+questo è il risultato di un loop che genera 10 valori casuali:
+{{ import random }} +{{ for x in range( 10 ): }} +
+ {{= random.randint( 10, 100 ) }} +
+ {{ pass }} + diff --git a/config.py b/config.py new file mode 100644 index 0000000..71cee13 --- /dev/null +++ b/config.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + +config = { + 'MYSQL_HOST': 'localhost', + 'MYSQL_USER': 'corso', + 'MYSQL_PASSWORD': 'pwdcorso', + 'MYSQL_DB': 'mywiki', +} diff --git a/config.py.example b/config.py.example new file mode 100644 index 0000000..cb1ba75 --- /dev/null +++ b/config.py.example @@ -0,0 +1,8 @@ +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + +config = { + 'MYSQL_HOST': 'your_mysql_server_name_or_ip_address', + 'MYSQL_USER': 'your_mysql_username', + 'MYSQL_PASSWORD': 'your_mysql_password', + 'MYSQL_DB': 'your_mysql_db', +} diff --git a/controllers/demo.py b/controllers/demo.py new file mode 100644 index 0000000..52f4552 --- /dev/null +++ b/controllers/demo.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + +# +# esempio minimo di controller WSGI +# +def application( environ, start_response ): + start_response( '200 OK', [('content-type', 'text/html')] ) + return [ 'Greetings from demo' ] + +# +# funzioni specializzate per GET e POST. +# _se_ presenti hanno la precedenza rispetto ad 'application' +# +def get( environ, start_response ): + import template + + start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) + + return [ template.render( + content=''' + Greetings from demo (GET)
+ {% for i in range(a): %} + {% = i %}
+ {% pass %} +
+ (questo contenuto è generato da un template) + ''', + + context=dict(a=5), + + delimiters=( '{%', '%}' ) + ) + ] + +def post( environ, start_response ): + start_response( '200 OK', [('content-type', 'text/html')] ) + return [ 'Greetings from demo (POST)' ] + + + +""" +@template( 'hello_world.tmpl' ) +def render( environ ): + return { 'v1': 100, 'cube':lambda x: x**3 } +""" diff --git a/controllers/demo/due.py b/controllers/demo/due.py new file mode 100644 index 0000000..848ebe2 --- /dev/null +++ b/controllers/demo/due.py @@ -0,0 +1,20 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + +import deco # decoratori +import traceback + +# +# esempio minimo di controller WSGI +# +@deco.template( 'template1.tmpl' ) +def application( environ, start_response ): + + try: + html = environ['template']( context=dict( v1=1, v2='pippo') ) + except: + html = "
" + traceback.format_exc() + "
" + + start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) + + return [ html ] diff --git a/deco.py b/deco.py new file mode 100644 index 0000000..c9a607b --- /dev/null +++ b/deco.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + +import template as tmpl +from functools import partial + +def template( filename=None ): + def real_decorator( wsgi_application ): + def wrapper( environ, start_response ): + environ[ 'template' ] = partial( tmpl.render, filename=filename ) + return wsgi_application( environ, start_response ) + return wrapper + return real_decorator \ No newline at end of file diff --git a/dispatch_wsgi.py b/dispatch_wsgi.py new file mode 100755 index 0000000..744d17c --- /dev/null +++ b/dispatch_wsgi.py @@ -0,0 +1,58 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + + +# +# aggiungiamo la directory corrente al path (se non presente) +# +import sys +wsgilib = '/'.join( __file__.split('/')[:-1] ) +if wsgilib not in sys.path: + sys.path.insert(0, wsgilib) + +# +# importazione handler +# +from router import WSGIRouter, WSGISmartRouter + +# +# index: esempio minimo di applicazione WSGI (pagina iniziale) +# +def index( environ, start_response ): + start_response( '200 OK', [('content-type', 'text/html')] ) + return [ 'Index was here' ] + +# +# hello: esempio minimo di applicazione WSGI +# +def hello( environ, start_response ): + start_response( '200 OK', [('content-type', 'text/html')] ) + return [ 'Simon says: Hello world!' ] + +# +# fallback: mostra tutti i parametri disponibili all'applicazione WSGI +# +def fallback( environ, start_response ): + from pprint import pformat + start_response( '200 OK', [('Content-Type', 'text/html')] ) + return [ + '-- EMBEDDED MODE --' if not environ.get('mod_wsgi.process_group','') else '-- DAEMON MODE --', + '
', + pformat( environ, width=132 ).replace('\n','
\n'), + ] + +# +# definiamo gli handler per le url che vogliamo servire +# +from test_mysql import simple_mysql + +handlers = ( + ( r'/', index ), + ( r'/hello', hello ), + ( r'/mysql', simple_mysql ), +) + +# +# !!! mod_wsgi richiede che la nostra applicazione si chiami 'application' +# +application = WSGISmartRouter( handlers, fallback, cont_dir='controllers' ) diff --git a/externals/access.log b/externals/access.log new file mode 120000 index 0000000..9d906e3 --- /dev/null +++ b/externals/access.log @@ -0,0 +1 @@ +/var/log/apache2/access.log \ No newline at end of file diff --git a/externals/error.log b/externals/error.log new file mode 120000 index 0000000..2b589ab --- /dev/null +++ b/externals/error.log @@ -0,0 +1 @@ +/var/log/apache2/error.log \ No newline at end of file diff --git a/externals/etc_apache2_sites-available_nanowsgi b/externals/etc_apache2_sites-available_nanowsgi new file mode 120000 index 0000000..3331227 --- /dev/null +++ b/externals/etc_apache2_sites-available_nanowsgi @@ -0,0 +1 @@ +/etc/apache2/sites-available/nanowsgi \ No newline at end of file diff --git a/externals/sei_framework_python.txt b/externals/sei_framework_python.txt new file mode 100644 index 0000000..a2a0838 --- /dev/null +++ b/externals/sei_framework_python.txt @@ -0,0 +1 @@ +http://www.infoworld.com/d/application-development/pillars-python-six-python-web-frameworks-compared-169442?page=0,1 diff --git a/nanowsgi.wpr b/nanowsgi.wpr new file mode 100644 index 0000000..6b928e4 --- /dev/null +++ b/nanowsgi.wpr @@ -0,0 +1,13 @@ +#!wing +#!version=4.0 +################################################################## +# Wing IDE project file # +################################################################## +[project attributes] +proj.directory-list = [{'dirloc': loc('.'), + 'excludes': (), + 'filter': '*', + 'include_hidden': False, + 'recursive': True, + 'watch_for_changes': True}] +proj.file-type = 'shared' diff --git a/nanowsgi.wpu b/nanowsgi.wpu new file mode 100644 index 0000000..fc827d9 --- /dev/null +++ b/nanowsgi.wpu @@ -0,0 +1,665 @@ +#!wing +#!version=4.0 +################################################################## +# Wing IDE project file : User-specific branch # +################################################################## +[user attributes] +console.id-seed = 2 +console.toolbox = [{'autosave': False, + 'ctype': ('cmd', + 'tail -f /var/log/apache2/error.log'), + 'env': ('project', + ['']), + 'id': 'cmd-1', + 'io_encoding': None, + 'key_binding': '', + 'line_mode': True, + 'pseudo_tty': True, + 'pypath': ('project', + ''), + 'raise_panel': True, + 'rundir': ('project', + ''), + 'title': 'apache error log'}] +guimgr.overall-gui-state = {'windowing-policy': 'combined-window', + 'windows': [{'name': 'T8mo9t3gr2UfAuntANdsFb69OL'\ + 'cVmlgL', + 'size-state': '', + 'type': 'dock', + 'view': {'area': 'tall', + 'constraint': None, + 'current_pages': [1, + 2], + 'full-screen': False, + 'notebook_display': 'tabs only', + 'notebook_percent': 0.41268292682926833, + 'override_title': None, + 'pagelist': [('debug-stack', + 'tall', + 1, + {'codeline-mode': 'below'}), + ('debug-io', + 'tall', + 0, + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}), + ('indent', + 'tall', + 2, + {}), + ('project', + 'tall', + 0, + {'tree-state': {'file-sort-method': 'by name', + 'list-files-first': False, + 'tree-states': {'deep': {'column-widths': None, + 'expanded-nodes': [(0,), + (1,), + (2,), + (3,)], + 'selected-nodes': [(3, + 3)], + 'top-node': (0,)}}, + 'tree-style': 'deep'}}), + ('refactoring', + 'tall', + 0, + {}), + ('snippets', + 'tall', + 0, + {'tree-states': {'__all__': [], + u'c': [], + u'django': [], + u'html': [], + u'py': []}}), + ('source-assistant', + 'tall', + 2, + {'docstring-during-complete': False, + 'wrap-lines': True}), + ('browser', + 'tall', + 0, + {'all_tree_states': {u'By Module': {'column-w'\ + 'idths': [1.0], + 'expanded-nodes': [], + 'selected-nodes': [], + 'top-node': None}}, + 'browse_mode': u'By Module', + 'follow-selection': False, + 'sort_mode': 'Alphabetically', + 'visibility_options': {u'Derived Classes': False, + u'Imported': False, + u'Modules': True}})], + 'primary_view_state': {'area': 'wide', + 'constraint': None, + 'current_pages': [6], + 'notebook_display': 'normal', + 'notebook_percent': 0.39739413680781754, + 'override_title': None, + 'pagelist': [('bookmarks', + 'wide', + 1, + {}), + ('debug-breakpoints', + 'wide', + 0, + {'tree-state': []}), + ('debug-probe', + 'wide', + 2, + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'history': {None: [], + u'file:/home/joshua/CIA/PROJECTS/oeseo/iniezione24/importer.py': [''\ + 'fields\n', + 'eval( fields)\n', + 'parse_month( value )\n'], + u'file:/home/joshua/CIA/PROJECTS/quake time machine/ingv_grabber.py': [''\ + 'query\n', + 'print query\n'], + u'file:/home/joshua/CIA/PROJECTS/scribd/scribd_grabber.py': [''\ + 'value.split()\n', + 'path.exists( fname)\n', + 'path.isfile( fname )\n', + "fcontent[ fcontent.find('('): ]\n", + "fcontent[ fcontent.find('['): ]\n", + "print fcontent[ fcontent.find('['): ]\n", + "print fcontent[ fcontent.find('['):-4 ]\n", + "print fcontent[ fcontent.find('['):-3]\n", + "print fcontent[ fcontent.find('[')+1:-3]\n", + 'fcontent\n', + "json.loads( fcontent[ fcontent.find('[')+1:-3] )\n", + "print json.loads( fcontent[ fcontent.find('[')+1:-3] )\n", + "jcontent.encode('utf-8')\n", + 'self.rawdata\n', + "self.rawdata += jcontent.encode('utf-8')\n", + 'len( self.rawdata )\n', + 'econtent\n'], + u'file:/home/joshua/Desktop/ir-audio.py': [''\ + 'type( data )\n', + 'l\n', + 'data\n', + 'for c in data:\n print ord( c )\n\n', + 'len( data )\n'], + u'file:/home/joshua/Projects/corso_python/decoratori.py': [''\ + 'time()\n', + 't0\n'], + u'file:/home/joshua/Projects/corso_python/mille_code.py': [''\ + 'self.__obj_list\n', + 'self\n', + 'self.__class__\n', + 'self.__class__.__init_code()\n', + 'self.__class__.__init_code\n', + 'self.__class__.\n', + '_Supermarket__init_code\n', + 'self.__init_code\n', + 'self.__code\n', + 'self.__store\n', + 'self.store\n', + 'self.code\n', + 'coda.len()\n'], + u'file:/media/Kindle/screensaver/renumber.py': [''\ + 'file_list\n']}, + 'sel-line': 4, + 'sel-line-start': 46, + 'selection_end': 46, + 'selection_start': 46}), + ('debug-exceptions', + 'wide', + 0, + {}), + ('messages', + 'wide', + 2, + {'current-domain': 8}), + ('debug-modules', + 'wide', + 1, + {}), + ('os-command', + 'wide', + 1, + {'last-percent': 0.80000000000000004, + 'toolbox-percent': 1.0, + 'toolbox-tree-sel': 'cmd-1'}), + ('python-shell', + 'wide', + 2, + {'attrib-starts': [], + 'first-line': 4, + 'folded-linenos': [], + 'history': {None: ['import stat\n', + 'imporet urllib2\n', + 'import urllib2\n', + 'INGV_PAGE = "http://cnt.rm.ingv.it/"\n', + 'html = urllib2.urlopen(INGV_PAGE).read()\n', + 'print html\n', + 'import MySQLdb\n', + 'x = ["
\\n
\\n \\n
\\nChiss\xc3\x83 da quanto tempo s'\ + 'i era accorta che lui la stavaosservando?- Siamo in due, direi. \xc3\xa2\xe2\x82\xac\xe2\x80\x9c Ris'\ + 'pose sulla difensiva, alzandosie raggiun'\ + 'gendola sul balcone. \xc3\xa2\xe2\x82\xac\xe2\x80\x9c Eppure dovres'\ + 'ti esserestanca. Non riposi da due giorni'\ + ' almeno.- Non dormo mai molto, da un anno'\ + ' a questa parte. \xc3\xa2\xe2\x82\xac\xe2\x80\x9c\xc3\x82 Il suo tono si fece all\xc3\xa2\xe2\x82\xac\xe2\x84'\ + '\xa2improvviso fragile, da donnaimpaurita.- Pensieri?Si accorse quasi su'\ + 'bito di quanto fosse superficiale la sua'\ + 'domanda, che per\xc3\x83\xc2\xb2 parve aprire un varco tra le ferre'\ + 'edifese di Sara.\xc3\x82 \xc3\xa2\xe2\x82\xac\xe2\x80'\ + '\x9c Incubi.Lei lo guard\xc3\x83\xc2\xb2. Sembrava che avesse vogli'\ + 'a di parlare eche si stesse domandando se'\ + ' lui fosse la persona adatta.Robert non '\ + 'disse nulla, Sara stava seguendo il filo di
\\n
\\'\ + 'n
\\n
\\n
\\n\\n
\\n
\\n
\\n\\n"]\n', + 'x\n', + 'print x\n', + 'len( x )\n', + 'x.length\n', + 'x.size()\n', + 'dir( x )\n', + 'y = x\n', + 'y.sort\n', + 'y.sort()\n', + 'y = x[0]\n', + 'y\n', + 'print y\n', + '"%05d" % 42\n', + '"%05d" % 42.5\n', + '"%05f" % 42.5\n', + '"%05.3f" % 42.5\n', + '"%05.1f" % 42.5\n', + '"%05.1f" % 42.545\n', + '"%05.1f" % 42.567\n', + '"%5.1f" % 42.567\n', + '"%9.1f" % 42.567\n', + '"%9.7f" % 42.567\n', + '"%9.9f" % 42.567\n', + '"%-9.9f" % 42.567\n', + '"%5f" % 1.234567\n', + '"%5f" % 1.23456789\n', + '"%5.2f" % 1.23456789\n', + '"%7.2f" % 1.23456789\n', + "'%f' % 12.34567\n", + "'%0f' % 12.34567\n", + "'%3f' % 12.34567\n", + "'%3f' % 12.345678\n", + '345678\n\n', + "'%1.3f' % 12.345678\n", + "'%1.3f' % 435243512.345678\n", + "'%.3f' % 435243512.345678\n", + "'%.3f' % 12.345678\n", + "'%4.3f' % 12.345678\n", + "'%-4.3f' % 12.345678\n", + "'%4.2f' % 12.345678\n", + "'%.2f' % 12.345678\n", + "'%.-2f' % 12.345678\n", + "'%10.2f' % 12.345678\n", + "'%-10.2f' % 12.345678\n", + '"%-8s" % "loremipsum"\n', + '"%8s" % "loremipsum"\n', + '"%4s" % "loremipsum"\n', + '"%-4s" % "loremipsum"\n', + '"%-40s" % "loremipsum"\n', + '"%40s" % "loremipsum"\n', + '"%-5s" % "loremipsum"\n', + '"%-.5s" % "loremipsum"\n', + '"%.5s" % "loremipsum"\n', + '"%10.5s" % "loremipsum"\n', + '"%-10.5s" % "loremipsum"\n', + '2.56**5\n', + "giorni = \"\"\"\n Lunedi Martedi Mercoledi\n Giovedi Venerdi "\ + "Sabato\n Domenica\n \"\"\".split()\n\nfor i, g in enumerate( "\ + "giorni ):\n print \"il\", i+1, \"^ giorno e' un\", g\n\n", + 'giorni\n', + 'help( len )\n', + 'a = 1\n', + 'b = 2\n', + 'a, b = b,a\n', + 'print a, b\n', + '34\n', + 'x = 34\n', + "x=input('valore di x ')\n", + 'pippo\n', + 'x = pippo\n', + 'a = [ 1, 2, 3, 4 ]\n', + 'len( a )\n', + 'a[5] = 7\n', + 'a.insert(0, 7)\n', + 'a.insert(-1, 17)\n', + 'a[::-1].insert(0,44)\n', + 'a\n', + 'import paste\n', + 'import webob\n', + 'import sys\n', + 'sys.argv()\n', + 'sys.argv\n', + '*sys.argv\n', + "\"{0}\".format( 'hello' )\n", + "\"{0}\".format( ['hello'] )\n", + "\"{0}\".format( *['hello'] )\n", + "*['hello']\n", + 'None()\n', + 'import subprocess\n', + 'p = Popen( "ls -lR", stdin = PIPE )\n', + 'p = subprocess.Popen( "ls -lR", stdin = PIPE )\n', + 'p = subprocess.Popen( "ls -lR", stdin = subprocess.PIPE )\n', + 'import re\n']}, + 'sel-line': 3, + 'sel-line-start': 130, + 'selection_end': 130, + 'selection_start': 130}), + ('interactive-search', + 'wide', + 0, + {'fScope': {'fFileSetName': u'All Source Files', + 'fLocation': None, + 'fRecursive': True, + 'fType': 'project-files'}, + 'fSearchSpec': {'fEndPos': None, + 'fIncludeLinenos': True, + 'fInterpretBackslashes': False, + 'fMatchCase': False, + 'fOmitBinary': True, + 'fRegexFlags': 46, + 'fReplaceText': u"po'", + 'fReverse': False, + 'fSearchText': u'po"', + 'fStartPos': 0, + 'fStyle': 'text', + 'fWholeWords': False, + 'fWrap': True}, + 'fUIOptions': {'fAutoBackground': True, + 'fFilePrefix': 'short-file', + 'fFindAfterReplace': True, + 'fInSelection': False, + 'fIncremental': True, + 'fReplaceOnDisk': False, + 'fShowFirstMatch': False, + 'fShowLineno': True, + 'fShowReplaceWidgets': True}, + 'replace-entry-expanded': False, + 'search-entry-expanded': False}), + ('batch-search', + 'wide', + 0, + {'fScope': {'fFileSetName': u'All Source Files', + 'fLocation': None, + 'fRecursive': True, + 'fType': 'current-file'}, + 'fSearchSpec': {'fEndPos': None, + 'fIncludeLinenos': True, + 'fInterpretBackslashes': False, + 'fMatchCase': False, + 'fOmitBinary': True, + 'fRegexFlags': 38, + 'fReplaceText': u'\\1', + 'fReverse': False, + 'fSearchText': u'__([^(]+?[^_ ])', + 'fStartPos': 0, + 'fStyle': 'regex', + 'fWholeWords': False, + 'fWrap': True}, + 'fUIOptions': {'fAutoBackground': True, + 'fFilePrefix': 'short-file', + 'fFindAfterReplace': True, + 'fInSelection': False, + 'fIncremental': True, + 'fReplaceOnDisk': False, + 'fShowFirstMatch': False, + 'fShowLineno': True, + 'fShowReplaceWidgets': True}, + 'replace-entry-expanded': False, + 'search-entry-expanded': False}), + ('debug-data', + 'wide', + 0, + {}), + ('testing', + 'wide', + 0, + {'added-files': [], + 'filter': '', + 'recent-filters': None, + 'sort-order': 'alpha'}), + ('debug-watch', + 'wide', + 0, + {'node-states': [('eval', + 'attrs')], + 'tree-state': {'column-widths': [0.4008515815085158, + 0.59914841849148415], + 'expanded-nodes': [(0,)], + 'selected-nodes': [], + 'top-node': (0,)}})], + 'primary_view_state': {'editor_states': {'bookmarks': ([[loc('../corso_python/www/wsgi/wsgi_test.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 148, + 'selection_start': 0}, + 1352922666.5966339], + [loc('../corso_python/www/wsgi/sqlpooler.py'), + {'attrib-starts': [('WSGISqlPoolMySQL', + 4)], + 'first-line': 0, + 'folded-linenos': [21, + 34, + 67, + 80], + 'sel-line': 7, + 'sel-line-start': 268, + 'selection_end': 275, + 'selection_start': 275}, + 1352922666.8889589], + [loc('../corso_python/www/static/index.html'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 4, + 'sel-line-start': 68, + 'selection_end': 78, + 'selection_start': 78}, + 1352922667.347193], + [loc('../corso_python/www/static/dispatch_wsgi.py'), + {'attrib-starts': [], + 'first-line': 37, + 'folded-linenos': [], + 'sel-line': 51, + 'sel-line-start': 1203, + 'selection_end': 1249, + 'selection_start': 1217}, + 1352922667.6854961], + [loc('../corso_python/www/static/dispatch_fcgi.py'), + {'attrib-starts': [], + 'first-line': 25, + 'folded-linenos': [], + 'sel-line': 41, + 'sel-line-start': 913, + 'selection_end': 945, + 'selection_start': 913}, + 1352922668.0699551], + [loc('../corso_python/www/static/.htaccess'), + {'attrib-starts': [], + 'first-line': 34, + 'folded-linenos': [], + 'sel-line': 54, + 'sel-line-start': 1157, + 'selection_end': 1157, + 'selection_start': 1157}, + 1352922668.490283], + [loc('../corso_python/www/protected/info_handler.py'), + {'attrib-starts': [('info', + 22)], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 34, + 'sel-line-start': 1127, + 'selection_end': 1151, + 'selection_start': 1127}, + 1352922668.901685], + [loc('../corso_python/www/protected/hello_handler.py'), + {'attrib-starts': [('hello', + 18)], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 46, + 'sel-line-start': 1148, + 'selection_end': 1213, + 'selection_start': 1153}, + 1352922669.3464761], + [loc('../corso_python/www/protected/fallback_handler.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 3, + 'sel-line-start': 81, + 'selection_end': 108, + 'selection_start': 81}, + 1352922669.920784], + [loc('../corso_python/www/cgi/dispatch_cgi.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 5, + 'sel-line-start': 162, + 'selection_end': 164, + 'selection_start': 164}, + 1352922670.2010989], + [loc('../corso_python/www/apache_config.txt'), + {'attrib-starts': [], + 'first-line': 6, + 'folded-linenos': [], + 'sel-line': 16, + 'sel-line-start': 467, + 'selection_end': 499, + 'selection_start': 499}, + 1352922670.649184], + (loc('controllers/demo.py'), + {'attrib-starts': [('get', + 14)], + 'first-line': 17, + 'folded-linenos': [], + 'sel-line': 27, + 'sel-line-start': 805, + 'selection_end': 811, + 'selection_start': 811}, + 1352970566.1362541), + (loc('static/index.html'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 11, + 'sel-line-start': 315, + 'selection_end': 315, + 'selection_start': 315}, + 1352970992.829721), + (loc('controllers/demo.py'), + {'attrib-starts': [('get', + 14)], + 'first-line': 17, + 'folded-linenos': [], + 'sel-line': 27, + 'sel-line-start': 805, + 'selection_end': 811, + 'selection_start': 811}, + 1352971019.481142), + (loc('dispatch_wsgi.py'), + {'attrib-starts': [], + 'first-line': 31, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}, + 1352971025.356674), + (loc('static/index.html'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 10, + 'sel-line-start': 268, + 'selection_end': 318, + 'selection_start': 318}, + 1352971029.3790801), + (loc('dispatch_wsgi.py'), + {'attrib-starts': [], + 'first-line': 31, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}, + 1352971052.15623), + (loc('static/index.html'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 9, + 'sel-line-start': 209, + 'selection_end': 240, + 'selection_start': 235}, + 1352971058.338448), + (loc('dispatch_wsgi.py'), + {'attrib-starts': [], + 'first-line': 31, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}, + 1352971071.333266), + [loc('static/index.html'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 9, + 'sel-line-start': 209, + 'selection_end': 240, + 'selection_start': 235}, + 1352971072.960921]], + 19), + 'current-loc': loc('static/index.html'), + 'editor-states': {loc('controllers/demo.py'): {'attrib-starts': [('g'\ + 'et', + 14)], + 'first-line': 17, + 'folded-linenos': [], + 'sel-line': 27, + 'sel-line-start': 805, + 'selection_end': 811, + 'selection_start': 811}, + loc('dispatch_wsgi.py'): {'attrib-starts': [], + 'first-line': 31, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}, + loc('static/index.html'): {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 11, + 'sel-line-start': 335, + 'selection_end': 394, + 'selection_start': 394}}, + 'has-focus': True}, + 'open_files': [u'controllers/demo.py', + u'dispatch_wsgi.py', + u'static/index.html']}, + 'saved_notebook_display': None, + 'split_percents': {0: 0.33189122373300373}, + 'splits': 1, + 'tab_location': 'top', + 'user_data': {}}, + 'saved_notebook_display': None, + 'split_percents': {0: 0.7046688382193268}, + 'splits': 2, + 'tab_location': 'left', + 'user_data': {}}, + 'window-alloc': (38, + 40, + 1322, + 964)}]} +guimgr.recent-documents = [loc('static/index.html'), + loc('dispatch_wsgi.py'), + loc('controllers/demo.py')] diff --git a/router.py b/router.py new file mode 100644 index 0000000..4263180 --- /dev/null +++ b/router.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + +import re +import imp +import os + +class WSGIRouter(object): + """ parse http uri and call the proper handler (controller in MVC terms) + """ + def __init__( self, handlers=(), fallback=None ): + """ handlers = list of tuples in the form ( regex, handler ) + groups in regex are passed to handlers within environ['router.args'] + + fallback = if defined, is the default handler to be called if no + regex in 'handlers' matches + """ + self.handlers = handlers + self.fallback = fallback + + def __call__( self, environ, start_response ): + # + # search form matching handler + # + for regex, handler in self.handlers: + match = re.match( '^' + regex + '$', environ['SCRIPT_URL']) + if match: + environ['router.args'] = match.groups() + return handler( environ, start_response ) + + # + # use fallback ( if defined ) + # + if self.fallback: + environ['router.args'] = [ + token for token in environ['SCRIPT_URL'].split('/') if token ] + return self.fallback( environ, start_response ) + + # + # last resort + # + start_response( '202 Accepted', [('Content-Type', 'text/plain')] ) + return [ "WSGIRouter: I have no route for '%s'" % environ['SCRIPT_URL'] ] + +class WSGISmartRouter(object): + """ like WSGIRouter, but before calling fallback handler, + searches in 'cont_dir' folder for a file matching the url. + + uri = http:///lorem/ipsum/dolor/sit + + handler: environ['router.args']: + + any handler matching in handlers - capture groups - + /lorem/ipsum/dolor/sit () + /lorem/ipsum/dolor ('sit') + /lorem/ipsum ('dolor','sit') + /lorem ('ipsum','dolor','sit') + fallback ('lorem','ipsum','dolor','sit') + """ + def __init__( self, handlers=(), fallback=None, cont_dir='.' ): + self.handlers = handlers + self.fallback = fallback + self.cont_dir = os.path.normpath( os.path.join( os.path.split(__file__)[0], cont_dir ) ) + '/' + + def __call__( self, environ, start_response ): + # + # search form matching handler + # + for regex, handler in self.handlers: + match = re.match( '^' + regex + '$', environ['SCRIPT_URL']) + if match: + environ['router.args'] = match.groups() + return handler( environ, start_response ) + + # + # search in filesystem for matching file + # + tokens = [ token for token in environ['SCRIPT_URL'].split('/') if token ] + + #for idx in range( 1, len( tokens ) + 1 ): # from shortest path to longest one + # from longest path to shortest one + for idx in range( len( tokens ) , 0, -1 ): + module = os.path.normpath( self.cont_dir + '/'.join( tokens[:idx] ) + '.py' ) + args = tokens[idx:] + + try: + handler = imp.load_source( 'application', module ) + environ['router.args'] = args + + if environ['REQUEST_METHOD'] == 'GET' and hasattr( handler, 'get' ): + # FIXME: forse è meglio eseguire QUI la start_response + # e compilare il template (solo per GET e POST) + return handler.get( environ, start_response ) + + elif environ['REQUEST_METHOD'] == 'POST' and hasattr( handler, 'post' ): + return handler.post( environ, start_response ) + + elif hasattr( handler, 'application' ): + return handler.application( environ, start_response ) + + except IOError: + # not found! Better luck next time... + pass + + # + # use fallback ( if defined ) + # + if self.fallback: + environ['router.args'] = tokens + return self.fallback( environ, start_response ) + + # + # last resort + # + start_response( '202 Accepted', [('Content-Type', 'text/plain')] ) + return [ "WSGISmartRouter: I have no route for '%s'" % environ['SCRIPT_URL'] ] diff --git a/static/.htaccess b/static/.htaccess new file mode 100644 index 0000000..c6b6b25 --- /dev/null +++ b/static/.htaccess @@ -0,0 +1,37 @@ +RewriteEngine On # Turn on the rewriting engine +RewriteBase / + + +#################### +RewriteRule ^/?$ /index + + +############################################################################### +## ## +## CGI WRAPPER PER WSGI ## +## ## +############################ + +## +## qualunque richiesta non statica viene passata a cgi/cgi_wrapper.py +## +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_URI} !^(/.*)+cgi_wrapper.py/? +RewriteRule ^(.*)$ cgi/cgi_wrapper.py/$1 [QSA,L] + + + +############################################################################### +## ## +## WSGI PURO ## +## ## +################# + +## +## qualunque richiesta non statica viene passata a /dispatch_wsgi.py +## +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)$ /dispatch_wsgi/$1 [QSA,L] + diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..fb6ff33 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..9efadfd --- /dev/null +++ b/static/index.html @@ -0,0 +1,21 @@ + + + + nanoWSGI + + + +

Elenco funzioni disponibili

+ +
+ + nanoWSGI © 2012, Alchimie Digitali S.r.l. +
+ + diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 0000000..0449737 Binary files /dev/null and b/static/logo.png differ diff --git a/template.py b/template.py new file mode 100644 index 0000000..50312f9 --- /dev/null +++ b/template.py @@ -0,0 +1,917 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +This file is part of the web2py Web Framework (Copyrighted, 2007-2011). +License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) + +Author: Thadeus Burgess + +Contributors: + +- Thank you to Massimo Di Pierro for creating the original gluon/template.py +- Thank you to Jonathan Lundell for extensively testing the regex on Jython. +- Thank you to Limodou (creater of uliweb) who inspired the block-element support for web2py. +""" + +import os +import cgi +import logging +from re import compile, sub, escape, DOTALL +try: + import cStringIO as StringIO +except: + from io import StringIO + +try: + # have web2py + from restricted import RestrictedError + from globals import current +except ImportError: + # do not have web2py + current = None + + def RestrictedError(a, b, c): + logging.error(str(a) + ':' + str(b) + ':' + str(c)) + return RuntimeError + + +class Node(object): + """ + Basic Container Object + """ + def __init__(self, value=None, pre_extend=False): + self.value = value + self.pre_extend = pre_extend + + def __str__(self): + return str(self.value) + + +class SuperNode(Node): + def __init__(self, name='', pre_extend=False): + self.name = name + self.value = None + self.pre_extend = pre_extend + + def __str__(self): + if self.value: + return str(self.value) + else: + # raise SyntaxError("Undefined parent block ``%s``. \n" % self.name + "You must define a block before referencing it.\nMake sure you have not left out an ``{{end}}`` tag." ) + return '' + + def __repr__(self): + return "%s->%s" % (self.name, self.value) + + +def output_aux(node, blocks): + # If we have a block level + # If we can override this block. + # Override block from vars. + # Else we take the default + # Else its just a string + return (blocks[node.name].output(blocks) + if node.name in blocks else + node.output(blocks)) \ + if isinstance(node, BlockNode) \ + else str(node) + + +class BlockNode(Node): + """ + Block Container. + + This Node can contain other Nodes and will render in a hierarchical order + of when nodes were added. + + ie:: + + {{ block test }} + This is default block test + {{ end }} + """ + def __init__(self, name='', pre_extend=False, delimiters=('{{', '}}')): + """ + name - Name of this Node. + """ + self.nodes = [] + self.name = name + self.pre_extend = pre_extend + self.left, self.right = delimiters + + def __repr__(self): + lines = ['%sblock %s%s' % (self.left, self.name, self.right)] + lines += [str(node) for node in self.nodes] + lines.append('%send%s' % (self.left, self.right)) + return ''.join(lines) + + def __str__(self): + """ + Get this BlockNodes content, not including child Nodes + """ + return ''.join(str(node) for node in self.nodes + if not isinstance(node, BlockNode)) + + def append(self, node): + """ + Add an element to the nodes. + + Keyword Arguments + + - node -- Node object or string to append. + """ + if isinstance(node, str) or isinstance(node, Node): + self.nodes.append(node) + else: + raise TypeError("Invalid type; must be instance of ``str`` or ``BlockNode``. %s" % node) + + def extend(self, other): + """ + Extend the list of nodes with another BlockNode class. + + Keyword Arguments + + - other -- BlockNode or Content object to extend from. + """ + if isinstance(other, BlockNode): + self.nodes.extend(other.nodes) + else: + raise TypeError( + "Invalid type; must be instance of ``BlockNode``. %s" % other) + + def output(self, blocks): + """ + Merges all nodes into a single string. + blocks -- Dictionary of blocks that are extending + from this template. + """ + return ''.join(output_aux(node, blocks) for node in self.nodes) + + +class Content(BlockNode): + """ + Parent Container -- Used as the root level BlockNode. + + Contains functions that operate as such. + """ + def __init__(self, name="ContentBlock", pre_extend=False): + """ + Keyword Arguments + + name -- Unique name for this BlockNode + """ + self.name = name + self.nodes = [] + self.blocks = {} + self.pre_extend = pre_extend + + def __str__(self): + return ''.join(output_aux(node, self.blocks) for node in self.nodes) + + def _insert(self, other, index=0): + """ + Inserts object at index. + """ + if isinstance(other, (str, Node)): + self.nodes.insert(index, other) + else: + raise TypeError( + "Invalid type, must be instance of ``str`` or ``Node``.") + + def insert(self, other, index=0): + """ + Inserts object at index. + + You may pass a list of objects and have them inserted. + """ + if isinstance(other, (list, tuple)): + # Must reverse so the order stays the same. + other.reverse() + for item in other: + self._insert(item, index) + else: + self._insert(other, index) + + def append(self, node): + """ + Adds a node to list. If it is a BlockNode then we assign a block for it. + """ + if isinstance(node, (str, Node)): + self.nodes.append(node) + if isinstance(node, BlockNode): + self.blocks[node.name] = node + else: + raise TypeError("Invalid type, must be instance of ``str`` or ``BlockNode``. %s" % node) + + def extend(self, other): + """ + Extends the objects list of nodes with another objects nodes + """ + if isinstance(other, BlockNode): + self.nodes.extend(other.nodes) + self.blocks.update(other.blocks) + else: + raise TypeError( + "Invalid type; must be instance of ``BlockNode``. %s" % other) + + def clear_content(self): + self.nodes = [] + + +class TemplateParser(object): + + default_delimiters = ('{{', '}}') + r_tag = compile(r'(\{\{.*?\}\})', DOTALL) + + r_multiline = compile(r'(""".*?""")|(\'\'\'.*?\'\'\')', DOTALL) + + # These are used for re-indentation. + # Indent + 1 + re_block = compile('^(elif |else:|except:|except |finally:).*$', DOTALL) + + # Indent - 1 + re_unblock = compile('^(return|continue|break|raise)( .*)?$', DOTALL) + # Indent - 1 + re_pass = compile('^pass( .*)?$', DOTALL) + + def __init__(self, text, + name="ParserContainer", + context=dict(), + path='views/', + writer='response.write', + lexers={}, + delimiters=('{{', '}}'), + _super_nodes = [], + ): + """ + text -- text to parse + context -- context to parse in + path -- folder path to templates + writer -- string of writer class to use + lexers -- dict of custom lexers to use. + delimiters -- for example ('{{','}}') + _super_nodes -- a list of nodes to check for inclusion + this should only be set by "self.extend" + It contains a list of SuperNodes from a child + template that need to be handled. + """ + + # Keep a root level name. + self.name = name + # Raw text to start parsing. + self.text = text + # Writer to use (refer to the default for an example). + # This will end up as + # "%s(%s, escape=False)" % (self.writer, value) + self.writer = writer + + # Dictionary of custom name lexers to use. + if isinstance(lexers, dict): + self.lexers = lexers + else: + self.lexers = {} + + # Path of templates + self.path = path + # Context for templates. + self.context = context + + # allow optional alternative delimiters + self.delimiters = delimiters + if delimiters != self.default_delimiters: + escaped_delimiters = (escape(delimiters[0]), + escape(delimiters[1])) + self.r_tag = compile(r'(%s.*?%s)' % escaped_delimiters, DOTALL) + elif hasattr(context.get('response', None), 'delimiters'): + if context['response'].delimiters != self.default_delimiters: + escaped_delimiters = ( + escape(context['response'].delimiters[0]), + escape(context['response'].delimiters[1])) + self.r_tag = compile(r'(%s.*?%s)' % escaped_delimiters, + DOTALL) + + # Create a root level Content that everything will go into. + self.content = Content(name=name) + + # Stack will hold our current stack of nodes. + # As we descend into a node, it will be added to the stack + # And when we leave, it will be removed from the stack. + # self.content should stay on the stack at all times. + self.stack = [self.content] + + # This variable will hold a reference to every super block + # that we come across in this template. + self.super_nodes = [] + + # This variable will hold a reference to the child + # super nodes that need handling. + self.child_super_nodes = _super_nodes + + # This variable will hold a reference to every block + # that we come across in this template + self.blocks = {} + + # Begin parsing. + self.parse(text) + + def to_string(self): + """ + Return the parsed template with correct indentation. + + Used to make it easier to port to python3. + """ + return self.reindent(str(self.content)) + + def __str__(self): + "Make sure str works exactly the same as python 3" + return self.to_string() + + def __unicode__(self): + "Make sure str works exactly the same as python 3" + return self.to_string() + + def reindent(self, text): + """ + Reindents a string of unindented python code. + """ + + # Get each of our lines into an array. + lines = text.split('\n') + + # Our new lines + new_lines = [] + + # Keeps track of how many indents we have. + # Used for when we need to drop a level of indentation + # only to reindent on the next line. + credit = 0 + + # Current indentation + k = 0 + + ################# + # THINGS TO KNOW + ################# + + # k += 1 means indent + # k -= 1 means unindent + # credit = 1 means unindent on the next line. + + for raw_line in lines: + line = raw_line.strip() + + # ignore empty lines + if not line: + continue + + # If we have a line that contains python code that + # should be unindented for this line of code. + # and then reindented for the next line. + if TemplateParser.re_block.match(line): + k = k + credit - 1 + + # We obviously can't have a negative indentation + k = max(k, 0) + + # Add the indentation! + new_lines.append(' ' * (4 * k) + line) + + # Bank account back to 0 again :( + credit = 0 + + # If we are a pass block, we obviously de-dent. + if TemplateParser.re_pass.match(line): + k -= 1 + + # If we are any of the following, de-dent. + # However, we should stay on the same level + # But the line right after us will be de-dented. + # So we add one credit to keep us at the level + # while moving back one indentation level. + if TemplateParser.re_unblock.match(line): + credit = 1 + k -= 1 + + # If we are an if statement, a try, or a semi-colon we + # probably need to indent the next line. + if line.endswith(':') and not line.startswith('#'): + k += 1 + + # This must come before so that we can raise an error with the + # right content. + new_text = '\n'.join(new_lines) + + if k > 0: + self._raise_error('missing "pass" in view', new_text) + elif k < 0: + self._raise_error('too many "pass" in view', new_text) + + return new_text + + def _raise_error(self, message='', text=None): + """ + Raise an error using itself as the filename and textual content. + """ + raise RestrictedError(self.name, text or self.text, message) + + def _get_file_text(self, filename): + """ + Attempt to open ``filename`` and retrieve its text. + + This will use self.path to search for the file. + """ + + # If they didn't specify a filename, how can we find one! + if not filename.strip(): + self._raise_error('Invalid template filename') + + # Allow Views to include other views dynamically + context = self.context + if current and not "response" in context: + context["response"] = getattr(current, 'response', None) + + # Get the filename; filename looks like ``"template.html"``. + # We need to eval to remove the quotes and get the string type. + filename = eval(filename, context) + + # Get the path of the file on the system. + filepath = self.path and os.path.join(self.path, filename) or filename + + # try to read the text. + try: + fileobj = open(filepath, 'rb') + text = fileobj.read() + fileobj.close() + except IOError: + self._raise_error('Unable to open included view file: ' + filepath) + + return text + + def include(self, content, filename): + """ + Include ``filename`` here. + """ + text = self._get_file_text(filename) + + t = TemplateParser(text, + name=filename, + context=self.context, + path=self.path, + writer=self.writer, + delimiters=self.delimiters) + + content.append(t.content) + + def extend(self, filename): + """ + Extend ``filename``. Anything not declared in a block defined by the + parent will be placed in the parent templates ``{{include}}`` block. + """ + text = self._get_file_text(filename) + + # Create out nodes list to send to the parent + super_nodes = [] + # We want to include any non-handled nodes. + super_nodes.extend(self.child_super_nodes) + # And our nodes as well. + super_nodes.extend(self.super_nodes) + + t = TemplateParser(text, + name=filename, + context=self.context, + path=self.path, + writer=self.writer, + delimiters=self.delimiters, + _super_nodes=super_nodes) + + # Make a temporary buffer that is unique for parent + # template. + buf = BlockNode( + name='__include__' + filename, delimiters=self.delimiters) + pre = [] + + # Iterate through each of our nodes + for node in self.content.nodes: + # If a node is a block + if isinstance(node, BlockNode): + # That happens to be in the parent template + if node.name in t.content.blocks: + # Do not include it + continue + + if isinstance(node, Node): + # Or if the node was before the extension + # we should not include it + if node.pre_extend: + pre.append(node) + continue + + # Otherwise, it should go int the + # Parent templates {{include}} section. + buf.append(node) + else: + buf.append(node) + + # Clear our current nodes. We will be replacing this with + # the parent nodes. + self.content.nodes = [] + + t_content = t.content + + # Set our include, unique by filename + t_content.blocks['__include__' + filename] = buf + + # Make sure our pre_extended nodes go first + t_content.insert(pre) + + # Then we extend our blocks + t_content.extend(self.content) + + # Work off the parent node. + self.content = t_content + + def parse(self, text): + + # Basically, r_tag.split will split the text into + # an array containing, 'non-tag', 'tag', 'non-tag', 'tag' + # so if we alternate this variable, we know + # what to look for. This is alternate to + # line.startswith("{{") + in_tag = False + extend = None + pre_extend = True + + # Use a list to store everything in + # This is because later the code will "look ahead" + # for missing strings or brackets. + ij = self.r_tag.split(text) + # j = current index + # i = current item + stack = self.stack + for j in range(len(ij)): + i = ij[j] + + if i: + if not stack: + self._raise_error('The "end" tag is unmatched, please check if you have a starting "block" tag') + + # Our current element in the stack. + top = stack[-1] + + if in_tag: + line = i + + # Get rid of '{{' and '}}' + line = line[2:-2].strip() + + # This is bad juju, but let's do it anyway + if not line: + continue + + # We do not want to replace the newlines in code, + # only in block comments. + def remove_newline(re_val): + # Take the entire match and replace newlines with + # escaped newlines. + return re_val.group(0).replace('\n', '\\n') + + # Perform block comment escaping. + # This performs escaping ON anything + # in between """ and """ + line = sub(TemplateParser.r_multiline, + remove_newline, + line) + + if line.startswith('='): + # IE: {{=response.title}} + name, value = '=', line[1:].strip() + else: + v = line.split(' ', 1) + if len(v) == 1: + # Example + # {{ include }} + # {{ end }} + name = v[0] + value = '' + else: + # Example + # {{ block pie }} + # {{ include "layout.html" }} + # {{ for i in range(10): }} + name = v[0] + value = v[1] + + # This will replace newlines in block comments + # with the newline character. This is so that they + # retain their formatting, but squish down to one + # line in the rendered template. + + # First check if we have any custom lexers + if name in self.lexers: + # Pass the information to the lexer + # and allow it to inject in the environment + + # You can define custom names such as + # '{{<.< + tokens = line.split('\n') + + # We need to look for any instances of + # for i in range(10): + # = i + # pass + # So we can properly put a response.write() in place. + continuation = False + len_parsed = 0 + for k, token in enumerate(tokens): + + token = tokens[k] = token.strip() + len_parsed += len(token) + + if token.startswith('='): + if token.endswith('\\'): + continuation = True + tokens[k] = "\n%s(%s" % ( + self.writer, token[1:].strip()) + else: + tokens[k] = "\n%s(%s)" % ( + self.writer, token[1:].strip()) + elif continuation: + tokens[k] += ')' + continuation = False + + buf = "\n%s" % '\n'.join(tokens) + top.append(Node(buf, pre_extend=pre_extend)) + + else: + # It is HTML so just include it. + buf = "\n%s(%r, escape=False)" % (self.writer, i) + top.append(Node(buf, pre_extend=pre_extend)) + + # Remember: tag, not tag, tag, not tag + in_tag = not in_tag + + # Make a list of items to remove from child + to_rm = [] + + # Go through each of the children nodes + for node in self.child_super_nodes: + # If we declared a block that this node wants to include + if node.name in self.blocks: + # Go ahead and include it! + node.value = self.blocks[node.name] + # Since we processed this child, we don't need to + # pass it along to the parent + to_rm.append(node) + + # Remove some of the processed nodes + for node in to_rm: + # Since this is a pointer, it works beautifully. + # Sometimes I miss C-Style pointers... I want my asterisk... + self.child_super_nodes.remove(node) + + # If we need to extend a template. + if extend: + self.extend(extend) + +# We need this for integration with gluon + + +def parse_template(filename, + path='views/', + context=dict(), + lexers={}, + delimiters=('{{', '}}') + ): + """ + filename can be a view filename in the views folder or an input stream + path is the path of a views folder + context is a dictionary of symbols used to render the template + """ + + # First, if we have a str try to open the file + if isinstance(filename, str): + try: + fp = open(os.path.join(path, filename), 'rb') + text = fp.read() + fp.close() + except IOError: + raise RestrictedError(filename, '', 'Unable to find the file') + else: + text = filename.read() + + # Use the file contents to get a parsed template and return it. + return str(TemplateParser(text, context=context, path=path, lexers=lexers, delimiters=delimiters)) + + +def get_parsed(text): + """ + Returns the indented python code of text. Useful for unit testing. + + """ + return str(TemplateParser(text)) + + +class DummyResponse(): + def __init__(self): + self.body = StringIO.StringIO() + + def write(self, data, escape=True): + if not escape: + self.body.write(str(data)) + elif hasattr(data, 'as_html') and callable(data.as_html): + self.body.write(data.as_html()) + else: + # make it a string + if not isinstance(data, (str, unicode)): + data = str(data) + elif isinstance(data, unicode): + data = data.encode('utf8', 'xmlcharrefreplace') + data = cgi.escape(data, True).replace("'", "'") + self.body.write(data) + + +class NOESCAPE(): + """ + A little helper to avoid escaping. + """ + def __init__(self, text): + self.text = text + + def xml(self): + return self.text + +# And this is a generic render function. +# Here for integration with gluon. + + +def render(content="hello world", + stream=None, + filename=None, + path=None, + context={}, + lexers={}, + delimiters=('{{', '}}') + ): + """ + >>> render() + 'hello world' + >>> render(content='abc') + 'abc' + >>> render(content='abc\\'') + "abc'" + >>> render(content='a"\\'bc') + 'a"\\'bc' + >>> render(content='a\\nbc') + 'a\\nbc' + >>> render(content='a"bcd"e') + 'a"bcd"e' + >>> render(content="'''a\\nc'''") + "'''a\\nc'''" + >>> render(content="'''a\\'c'''") + "'''a\'c'''" + >>> render(content='{{for i in range(a):}}{{=i}}
{{pass}}', context=dict(a=5)) + '0
1
2
3
4
' + >>> render(content='{%for i in range(a):%}{%=i%}
{%pass%}', context=dict(a=5),delimiters=('{%','%}')) + '0
1
2
3
4
' + >>> render(content="{{='''hello\\nworld'''}}") + 'hello\\nworld' + >>> render(content='{{for i in range(3):\\n=i\\npass}}') + '012' + """ + # here to avoid circular Imports + try: + from globals import Response + except ImportError: + # Working standalone. Build a mock Response object. + Response = DummyResponse + + # Add it to the context so we can use it. + if not 'NOESCAPE' in context: + context['NOESCAPE'] = NOESCAPE + + # save current response class + if context and 'response' in context: + old_response_body = context['response'].body + context['response'].body = StringIO.StringIO() + else: + old_response_body = None + context['response'] = Response() + + # If we don't have anything to render, why bother? + if not content and not stream and not filename: + raise SyntaxError("Must specify a stream or filename or content") + + # Here for legacy purposes, probably can be reduced to + # something more simple. + close_stream = False + if not stream: + if filename: + stream = open(filename, 'rb') + close_stream = True + elif content: + stream = StringIO.StringIO(content) + + # Execute the template. + code = str(TemplateParser(stream.read( + ), context=context, path=path, lexers=lexers, delimiters=delimiters)) + try: + exec(code) in context + except Exception: + # for i,line in enumerate(code.split('\n')): print i,line + raise + + if close_stream: + stream.close() + + # Returned the rendered content. + text = context['response'].body.getvalue() + if old_response_body is not None: + context['response'].body = old_response_body + return text + + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/test_mysql.py b/test_mysql.py new file mode 100644 index 0000000..3f76254 --- /dev/null +++ b/test_mysql.py @@ -0,0 +1,36 @@ +import MySQLdb +from config import config + +def simple_mysql( environ, start_response ): + # + # collegamento al mysql + # + # for more options see: + # http://mysql-python.sourceforge.net/MySQLdb.html#introduction + conn = MySQLdb.connect( + host = config['MYSQL_HOST'], + user = config['MYSQL_USER'], + passwd = config['MYSQL_PASSWORD'], + db = config['MYSQL_DB'] + ) + + # + # esecuzione query + # + cur = conn.cursor( MySQLdb.cursors.DictCursor ) + cur.execute( 'SHOW tables;' ) + + # + # recupero record e generazione html + # + + start_response( '200 OK', [('Content-Type', 'text/plain')] ) + + for record in cur.fetchall(): + yield "\n" + str( record ) + + # + # chiusura connessione al mysql + # + conn.close() +