WSGIA resposta para a questão definitiva sobre
Python, a web e tudo mais?
Humberto Diógenes
Python developer & evangelistdesde 2003
SysAdminde 2000 a 2003
Python, TurboGears e GitEPSL’s I, II e II
1o ENSOLFISL 7.0
Rumo à PyCon! :-)
Graduando de Ciências da Computação - UFRNTécnico em Informática - CEFET-RN
Palestras e Minicursos
• Provedor com ~10k assinantes
• Sistema principal escrito em Python (Caju)
• Clientes, Contratos, Serviços, E-mails, Cable Modems, etc.
• classificados.digi.com.br
• TurboGears
• 900.000 acessos por mês
• Vários outros sistemas em Python (Central do Assinante, etc.)
“Python: a única linguagem com mais frameworks web do que
keywords”
4SuiteAlbatrossAquariumCherryPyCrusaderCymbeline
Divmod NevowDjangoGizmo
JOWWeb2Karrigell
makimod_python
PastePylons
Python Server PagesPython Servlet Engine
PyWebLibQP
QuixoteSkunkWebSnakelets
SparkSpyce
TurboGearsWasp
web.pyweb2pyWebwareZope
4SuiteAlbatrossAquariumCherryPyCrusaderCymbeline
Divmod NevowDjangoGizmo
JOWWeb2Karrigell
makimod_python
PastePylons
Python Server PagesPython Servlet Engine
PyWebLibQP
QuixoteSkunkWebSnakelets
SparkSpyce
TurboGearsWasp
web.pyweb2pyWebwareZope
andas
assertbreakclass
continuedefdelelifelse
exceptexec
finallyforfrom
globalif
importinis
lambdanotor
passprintraisereturntry
whileyieldwith
4SuiteAlbatrossAquariumCherryPyCrusaderCymbeline
Divmod NevowDjangoGizmo
JOWWeb2Karrigell
makimod_python
PastePylons
Python Server PagesPython Servlet Engine
PyWebLibQP
QuixoteSkunkWebSnakelets
SparkSpyce
TurboGearsWasp
web.pyweb2pyWebwareZope
andas
assertbreakclass
continuedefdelelifelse
exceptexec
finallyforfrom
globalif
importinis
lambdanotor
passprintraisereturntry
whileyieldwith
O que WSGI não é
Não émais um framework web em Python
Não émais um framework web em Python
Não éuma especificação enorme e assustadora que
deve ser evitada a todo custo
Não émais um framework web em Python
Não éuma especificação enorme e assustadora que
deve ser evitada a todo custo
J2EE? :-)
WebServer
GatewayInterface
WebServer
GatewayInterface
PEP 333Python Enhancement Proposal #333
WebServer
GatewayInterface
PEP 333Python Enhancement Proposal #333
Dezembro de 2003
Apenas uma forma para servidores conversarem com frameworks, e
vice-versa.
Ilustração: Ian Bicking
Missão:
Portabilidade
Missão:
Portabilidade
Minha Aplicação
Missão:
Portabilidade
Minha AplicaçãoFramework XYZ
Missão:
Portabilidade
Minha AplicaçãoFramework XYZ
Servidor Web X
Missão:
Portabilidade
Minha AplicaçãoFramework XYZ
Apache
Missão:
Portabilidade
Minha AplicaçãoFramework XYZ
Twisted
Missão:
Portabilidade
Minha AplicaçãoFramework XYZ
Paste
Missão:
Portabilidade
Minha AplicaçãoFramework XYZ
etc., etc., etc...
Portabilidade requer...
Implementação fácil
Portabilidade requer...
Implementação fácil
Simplicidade
Portabilidade requer...
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)
return [output]
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)
return [output]
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers)
return [output]
{'HTTP_HOST': '127.0.0.1', 'PATH_INFO': '/', 'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': '', 'SERVER_NAME': '127.0.0.1', 'SERVER_PORT': '80', 'SERVER_PROTOCOL': 'HTTP/1.0',
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers)
return [output]
{'HTTP_HOST': '127.0.0.1', 'PATH_INFO': '/', 'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': '', 'SERVER_NAME': '127.0.0.1', 'SERVER_PORT': '80', 'SERVER_PROTOCOL': 'HTTP/1.0',
'wsgi.errors': <StringIO.StringIO instance at 0x1169530>,'wsgi.input': <StringIO.StringIO instance at 0x1159c60>,'wsgi.multiprocess': 0,'wsgi.multithread': 0,'wsgi.run_once': 0,'wsgi.url_scheme': 'http','wsgi.version': (1, 0)}
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)
return [output]
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)
return [output]
def run_with_cgi(application):
environ = dict(os.environ.items()) environ['wsgi.input'] = sys.stdin environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1,0) environ['wsgi.multithread'] = False environ['wsgi.multiprocess'] = True environ['wsgi.run_once'] = True
if environ.get('HTTPS','off') in ('on','1'): environ['wsgi.url_scheme'] = 'https' else: environ['wsgi.url_scheme'] = 'http' def write(data): sys.stdout.write('Status: %s\r\n' % status) for header in response_headers: sys.stdout.write('%s: %s\r\n' % header) sys.stdout.write('\r\n') sys.stdout.write(data) sys.stdout.flush()
def start_response(status,response_headers,exc_info=None): if exc_info: ... return write
result = application(environ, start_response) for data in result: write(data)
import os, sys
def run_with_cgi(application):
environ = dict(os.environ.items()) environ['wsgi.input'] = sys.stdin environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1,0) environ['wsgi.multithread'] = False environ['wsgi.multiprocess'] = True environ['wsgi.run_once'] = True
if environ.get('HTTPS','off') in ('on','1'): environ['wsgi.url_scheme'] = 'https' else: environ['wsgi.url_scheme'] = 'http'
headers_set = [] headers_sent = []
def write(data): if not headers_set: raise AssertionError("write() before start_response()")
elif not headers_sent: # Before the first output, send the stored headers status, response_headers = headers_sent[:] = headers_set sys.stdout.write('Status: %s\r\n' % status) for header in response_headers: sys.stdout.write('%s: %s\r\n' % header) sys.stdout.write('\r\n')
sys.stdout.write(data) sys.stdout.flush()
def start_response(status,response_headers,exc_info=None): if exc_info: try: if headers_sent: # Re-raise original exception if headers sent raise exc_info[0], exc_info[1], exc_info[2] finally: exc_info = None # avoid dangling circular ref elif headers_set: raise AssertionError("Headers already set!")
headers_set[:] = [status,response_headers] return write
result = application(environ, start_response) try: for data in result: if data: # don't send headers until body appears write(data) if not headers_sent: write('') # send headers now if body was empty finally: if hasattr(result,'close'): result.close()
import os, sys
def run_with_cgi(application):
environ = dict(os.environ.items()) environ['wsgi.input'] = sys.stdin environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1,0) environ['wsgi.multithread'] = False environ['wsgi.multiprocess'] = True environ['wsgi.run_once'] = True
if environ.get('HTTPS','off') in ('on','1'): environ['wsgi.url_scheme'] = 'https' else: environ['wsgi.url_scheme'] = 'http'
headers_set = [] headers_sent = []
def write(data): if not headers_set: raise AssertionError("write() before start_response()")
elif not headers_sent: # Before the first output, send the stored headers status, response_headers = headers_sent[:] = headers_set sys.stdout.write('Status: %s\r\n' % status) for header in response_headers: sys.stdout.write('%s: %s\r\n' % header) sys.stdout.write('\r\n')
sys.stdout.write(data) sys.stdout.flush()
def start_response(status,response_headers,exc_info=None): if exc_info: try: if headers_sent: # Re-raise original exception if headers sent raise exc_info[0], exc_info[1], exc_info[2] finally: exc_info = None # avoid dangling circular ref elif headers_set: raise AssertionError("Headers already set!")
headers_set[:] = [status,response_headers] return write
result = application(environ, start_response) try: for data in result: if data: # don't send headers until body appears write(data) if not headers_sent: write('') # send headers now if body was empty finally: if hasattr(result,'close'): result.close()
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers)
return [output]
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers)
return [output]
if __name__ == '__main__': from paste import httpserver httpserver.serve(application, host='127.0.0.1', port='8080')
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers)
return [output]
eddie:~/src/wsgi humberto$ python myapp.py serving on http://127.0.0.1:8080
if __name__ == '__main__': from paste import httpserver httpserver.serve(application, host='127.0.0.1', port='8080')
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers)
return [output]
eddie:~/src/wsgi humberto$ python myapp.py serving on http://127.0.0.1:8080
if __name__ == '__main__': from paste import httpserver httpserver.serve(application, host='127.0.0.1', port='8080')
>>> from myapp import application>>> from paste.fixture import TestApp
>>> app = TestApp(application)
>>> res = app.get('/')
>>> from myapp import application>>> from paste.fixture import TestApp
>>> app = TestApp(application)
>>> res = app.get('/')
>>> res
>>> from myapp import application>>> from paste.fixture import TestApp
>>> app = TestApp(application)
>>> res = app.get('/')
>>> res<Response 200 OK 'Hello World!'>
>>> from myapp import application>>> from paste.fixture import TestApp
>>> app = TestApp(application)
>>> res = app.get('/')
>>> res<Response 200 OK 'Hello World!'>
>>> res.body
>>> from myapp import application>>> from paste.fixture import TestApp
>>> app = TestApp(application)
>>> res = app.get('/')
>>> res<Response 200 OK 'Hello World!'>
>>> res.body'Hello World!'
Middleware
Ilustração: Ian Bicking
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)
return [output]
from paste.pony import make_ponyapplication = make_pony(application, dict())
if __name__ == '__main__': from paste import httpserver httpserver.serve(application, host='127.0.0.1', port='8080')
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)
return [output]
from paste.pony import make_ponyapplication = make_pony(application, dict())
if __name__ == '__main__': from paste import httpserver httpserver.serve(application, host='127.0.0.1', port='8080')
Um middleware um pouco mais útil...
{vídeo com demonstração do EvalException no Pylons}
from paste.pony import make_ponyfrom paste.evalexception import EvalException
def application(environ, start_response): status = '200 OK' output = 'Hello World!'
response_headers = [('Content-Type', 'text/plain'), ('Content-Length', str(len(output)))] start_response(status, response_headers) raise Exception("Hope it's Somebody Else's Problem.") return [output]
application = make_pony(application, dict())application = EvalException(application)
if __name__ == '__main__': from paste import httpserver httpserver.serve(application, host='127.0.0.1', port='8080')
{vídeo com demonstração do EvalException no Hello World}
Mais Middleware
ErrorMiddleware
Profiling
Session Recording / Playback
...
Servidor WSGI
Middleware WSGI
Middleware WSGI
Middleware WSGI
Framework comInterface WSGI
Aplicação
“Estado Atual das Coisas”
Passo I
Fornecer uma interface WSGIdef application(environ, start_response): ...
Passo I
Fornecer uma interface WSGIdef application(environ, start_response): ...
Todos os grandes frameworks já fornecem
Conversão interna para WSGI (middleware)
Passo II
Conversão interna para WSGI (middleware)
Passo II
Em andamento...
Conversão interna para WSGI (middleware)
Passo II
Pylons
Em andamento...
Cada vez menos frameworks full-stack
Mais middlewares reaproveitáveis
Passo III
Cada vez menos frameworks full-stack
Mais middlewares reaproveitáveis
Passo III
Engatinhando...
Cada vez menos frameworks full-stack
Mais middlewares reaproveitáveis
Passo III
Engatinhando...
TurboGears 2.0
Pylonsconfig/middleware.py
# Projeto Pylons: dirk/config/middleware.py# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
# Routing/Session/Cache Middlewareapp = RoutesMiddleware(app, config['routes.map'])app = SessionMiddleware(app, config)app = CacheMiddleware(app, config)...app = ErrorHandler(app, global_conf, **config['pylons.errorware'])...javascripts_app = StaticJavascripts()app = RegistryManager(app)...# Static files (If running in production, and Apache or another web # server is handling this static content, remove the following 3 lines)static_app = StaticURLParser(config['pylons.paths']['static_files'])app = Cascade([static_app, javascripts_app, app])...app = AuthBasicHandler(app, "Senha LDAP", wsgi_check_password)
BeakerRoutes
Paste
Pylons
my_ldap
WSGI não foi criado para aplicações
“Encorajamos (...) o desenvolvimento de APIs e frameworks alto-nível. (...)
“Dessa forma, o WSGI pode permanecer baixo-nível o suficiente para autores de servidores e
middleware, sem ser ‘feio’ para desenvolvedores de aplicações.”
PEP 333
wsgirefPython 2.5
WebObRequest & Response
wsgirefPython 2.5
PasteTanta Coisa Que Não Cabe Aqui
WebObRequest & Response
wsgirefPython 2.5
wsgirefmod_wsgi PasteTwistedFlupCherrypymodjy (Jython)ISAPI (IIS)NWSGI (.NET)
Servidores WSGI
chiral.web.httpdFastCoroutine-based pagesDeeply-integrated COMET supportNative memcached client
Fonte: wsgi.org/Servers
mod_wsgi
Auto-reloadApenas com `touch`
virtualenv
Auto-reloadApenas com `touch`
Authentication ProviderEx.: Trac e DjangoApenas no Apache 2.2
virtualenv
Auto-reloadApenas com `touch`
Authentication ProviderEx.: Trac e DjangoApenas no Apache 2.2
Daemon modeProcesso separadoOutro usuário (!= www-data)Limitar processos "memory-hungry"
Ex.: geração de PDFsHosting
virtualenv
Auto-reloadApenas com `touch`
Google App Engine
TIOBE Index 2007
PortabilidadeServidores e Frameworks
Desempenhomod_wsgi
PortabilidadeServidores e Frameworks
ReaproveitamentoMiddleware
Desempenhomod_wsgi
PortabilidadeServidores e Frameworks
Top Related