98
WSGI A resposta para a questão definitiva sobre Python, a web e tudo mais?

WSGI - PyCon Brasil 2008

Embed Size (px)

Citation preview

Page 1: WSGI - PyCon Brasil 2008

WSGIA resposta para a questão definitiva sobre

Python, a web e tudo mais?

Page 2: WSGI - PyCon Brasil 2008

Humberto Diógenes

Python developer & evangelistdesde 2004

SysAdminde 2000 a 2004

Python, TurboGears e GitFISL 7.0 e 9.0

1o ENSOLEPSL’s I, II e III

PyCon Brasil 2008

Palestras e Minicursos

Page 3: WSGI - PyCon Brasil 2008

• Provedor de acesso e hospedagem de sites

• Sistema principal escrito em Python (Caju)

• Clientes, Contratos, Serviços, E-mails, etc.

• PostgreSQL, MySQL, SQLite, Web Services com JSON, consulta a Cable Modems via SNMP, Autenticação RADIUS, etc...

• classificados.digi.com.br

• TurboGears

• >1.000.000 acessos por mês

• Vários outros sistemas em Python (Central do Assinante, etc.)

• Cursos de Python (e Web Standards, Linux, etc.)

Page 4: WSGI - PyCon Brasil 2008

“Python: a única linguagem com mais frameworks web do que

keywords”

Page 5: WSGI - PyCon Brasil 2008
Page 6: WSGI - PyCon Brasil 2008

4SuiteAlbatrossAquariumCherryPyCrusaderCymbeline

Divmod NevowDjangoGizmo

JOWWeb2Karrigell

makimod_python

PastePylons

Python Server PagesPython Servlet Engine

PyWebLibQP

QuixoteSkunkWebSnakelets

SparkSpyce

TurboGearsWasp

web.pyweb2pyWebwareZope

Page 7: WSGI - PyCon Brasil 2008

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

Page 8: WSGI - PyCon Brasil 2008

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

Page 9: WSGI - PyCon Brasil 2008

andas

assertbreakclass

continuedefdelelifelse

exceptexec

finallyforfrom

globalif

importinis

lambdanot

nonlocalor

passraisereturntry

whileyieldwith

4SuiteAlbatrossAquariumCherryPyCrusaderCymbeline

Divmod NevowDjangoGizmo

JOWWeb2Karrigell

makimod_python

PastePylons

Python Server PagesPython Servlet Engine

PyWebLibQP

QuixoteSkunkWebSnakelets

SparkSpyce

TurboGearsWasp

web.pyweb2pyWebwareZopeGrok

Page 10: WSGI - PyCon Brasil 2008

andas

assertbreakclass

continuedefdelelifelse

exceptexec

finallyforfrom

globalif

importinis

lambdanot

nonlocalor

passraisereturntry

whileyieldwith

4SuiteAlbatrossAquariumCherryPyCrusaderCymbeline

Divmod NevowDjangoGizmo

JOWWeb2Karrigell

makimod_python

PastePylons

Python Server PagesPython Servlet Engine

PyWebLibQP

QuixoteSkunkWebSnakelets

SparkSpyce

TurboGearsWasp

web.pyweb2pyWebwareZopeGrok

Page 11: WSGI - PyCon Brasil 2008

andas

assertbreakclass

continuedefdelelifelse

exceptexec

finallyforfrom

globalif

importinis

lambdanot

nonlocalor

passraisereturntry

whileyieldwith

4SuiteAlbatrossAquariumCherryPyCrusaderCymbeline

Divmod NevowDjangoGizmo

JOWWeb2Karrigell

makimod_python

PastePylons

Python Server PagesPython Servlet Engine

PyWebLibQP

QuixoteSkunkWebSnakelets

SparkSpyce

TurboGearsWasp

web.pyweb2pyWebwareZopeGrok

TrueFalseNone

Page 12: WSGI - PyCon Brasil 2008

O que WSGI não é

Page 13: WSGI - PyCon Brasil 2008

Não émais um framework web em Python

Page 14: WSGI - PyCon Brasil 2008

Não émais um framework web em Python

Não éuma especificação enorme e assustadora que

deve ser evitada a todo custo

Page 15: WSGI - PyCon Brasil 2008

Não émais um framework web em Python

Não éuma especificação enorme e assustadora que

deve ser evitada a todo custo

J2EE? :-)

Page 16: WSGI - PyCon Brasil 2008
Page 17: WSGI - PyCon Brasil 2008

WebServer

GatewayInterface

Page 18: WSGI - PyCon Brasil 2008

WebServer

GatewayInterface

PEP 333Python Enhancement Proposal #333

Page 19: WSGI - PyCon Brasil 2008

WebServer

GatewayInterface

PEP 333Python Enhancement Proposal #333

Dezembro de 2003

Page 20: WSGI - PyCon Brasil 2008

Apenas uma forma para servidores conversarem com frameworks, e

vice-versa.

Page 21: WSGI - PyCon Brasil 2008

Ilustração: Ian Bicking

Page 22: WSGI - PyCon Brasil 2008

Missão:

Portabilidade

Page 23: WSGI - PyCon Brasil 2008

Missão:

Portabilidade

Minha Aplicação

Page 24: WSGI - PyCon Brasil 2008

Missão:

Portabilidade

Minha AplicaçãoFramework XYZ

Page 25: WSGI - PyCon Brasil 2008

Missão:

Portabilidade

Minha AplicaçãoFramework XYZ

Servidor Web X

Page 26: WSGI - PyCon Brasil 2008

Missão:

Portabilidade

Minha AplicaçãoFramework XYZ

Apache

Page 27: WSGI - PyCon Brasil 2008

Missão:

Portabilidade

Minha AplicaçãoFramework XYZ

Twisted

Page 28: WSGI - PyCon Brasil 2008

Missão:

Portabilidade

Minha AplicaçãoFramework XYZ

Paste

Page 29: WSGI - PyCon Brasil 2008

Missão:

Portabilidade

Minha AplicaçãoFramework XYZ

etc., etc., etc...

Page 30: WSGI - PyCon Brasil 2008

Portabilidade requer...

Page 31: WSGI - PyCon Brasil 2008

Simplicidade

Portabilidade requer...

Page 32: WSGI - PyCon Brasil 2008

Implementação FácilSimplicidade

Portabilidade requer...

Page 33: WSGI - PyCon Brasil 2008

def application(environ, start_response): status = '200 OK' output = 'Hello World!'

response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)

return [output]

Page 34: WSGI - PyCon Brasil 2008

def application(environ, start_response): status = '200 OK' output = 'Hello World!'

response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)

return [output]

Page 35: WSGI - PyCon Brasil 2008

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',

Page 36: WSGI - PyCon Brasil 2008

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)}

Page 37: WSGI - PyCon Brasil 2008

def application(environ, start_response): status = '200 OK' output = 'Hello World!'

response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)

return [output]

Page 38: WSGI - PyCon Brasil 2008

def application(environ, start_response): status = '200 OK' output = 'Hello World!'

response_headers = [('Content-Type', 'text/plain')] start_response(status, response_headers)

return [output]

Page 39: WSGI - PyCon Brasil 2008

Também é fácil implementar um servidor

Page 40: WSGI - PyCon Brasil 2008

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)

Page 41: WSGI - PyCon Brasil 2008

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()

Page 42: WSGI - PyCon Brasil 2008

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()

Page 43: WSGI - PyCon Brasil 2008

def application(environ, start_response): status = '200 OK' output = 'Hello World!'

response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers)

return [output]

Page 44: WSGI - PyCon Brasil 2008

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')

Page 45: WSGI - PyCon Brasil 2008

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')

Page 46: WSGI - PyCon Brasil 2008

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')

Page 47: WSGI - PyCon Brasil 2008

>>> from myapp import application>>> from paste.fixture import TestApp

>>> app = TestApp(application)

>>> res = app.get('/')

Page 48: WSGI - PyCon Brasil 2008

>>> from myapp import application>>> from paste.fixture import TestApp

>>> app = TestApp(application)

>>> res = app.get('/')

>>> res

Page 49: WSGI - PyCon Brasil 2008

>>> from myapp import application>>> from paste.fixture import TestApp

>>> app = TestApp(application)

>>> res = app.get('/')

>>> res<Response 200 OK 'Hello World!'>

Page 50: WSGI - PyCon Brasil 2008

>>> from myapp import application>>> from paste.fixture import TestApp

>>> app = TestApp(application)

>>> res = app.get('/')

>>> res<Response 200 OK 'Hello World!'>

>>> res.body

Page 51: WSGI - PyCon Brasil 2008

>>> 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!'

Page 52: WSGI - PyCon Brasil 2008

Middleware

Page 53: WSGI - PyCon Brasil 2008

Ilustração: Ian Bicking

Page 54: WSGI - PyCon Brasil 2008

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')

Page 55: WSGI - PyCon Brasil 2008

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')

Page 56: WSGI - PyCon Brasil 2008

Um middleware um pouco mais útil...

Page 57: WSGI - PyCon Brasil 2008

{vídeo com demonstração do EvalException no Pylons}

Page 58: WSGI - PyCon Brasil 2008
Page 59: WSGI - PyCon Brasil 2008

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')

Page 60: WSGI - PyCon Brasil 2008

{vídeo com demonstração do EvalException no Hello World}

Page 61: WSGI - PyCon Brasil 2008
Page 62: WSGI - PyCon Brasil 2008

Mais Middleware

ErrorMiddleware

Profiling

Session Recording / Playback

...

Page 63: WSGI - PyCon Brasil 2008

Servidor WSGI

Middleware WSGI

Middleware WSGI

Middleware WSGI

Framework comInterface WSGI

Aplicação

Page 64: WSGI - PyCon Brasil 2008

“Estado Atual das Coisas”

Page 65: WSGI - PyCon Brasil 2008

Passo I

Fornecer uma interface WSGIdef application(environ, start_response): ...

Page 66: WSGI - PyCon Brasil 2008

Passo I

Fornecer uma interface WSGIdef application(environ, start_response): ...

Todos os grandes frameworks já fornecem

Page 67: WSGI - PyCon Brasil 2008

Conversão interna para WSGI (middleware)

Passo II

Page 68: WSGI - PyCon Brasil 2008

Conversão interna para WSGI (middleware)

Passo II

Em andamento...

Page 69: WSGI - PyCon Brasil 2008

Conversão interna para WSGI (middleware)

Passo II

Pylons

Em andamento...

Page 70: WSGI - PyCon Brasil 2008

Conversão interna para WSGI (middleware)

Passo II

Pylons

Em andamento...

Repoze

Page 71: WSGI - PyCon Brasil 2008

Cada vez menos frameworks full-stack

Mais middlewares reaproveitáveis

Passo III

Page 72: WSGI - PyCon Brasil 2008

Cada vez menos frameworks full-stack

Mais middlewares reaproveitáveis

Passo III

Engatinhando...

Page 73: WSGI - PyCon Brasil 2008

Cada vez menos frameworks full-stack

Mais middlewares reaproveitáveis

Passo III

Engatinhando...

TurboGears 2.0

Page 74: WSGI - PyCon Brasil 2008

Pylonsconfig/middleware.py

Page 75: WSGI - PyCon Brasil 2008

# 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

Page 76: WSGI - PyCon Brasil 2008

WSGI não foi criado para aplicações

Page 77: WSGI - PyCon Brasil 2008

“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

Page 78: WSGI - PyCon Brasil 2008

wsgirefPython 2.5

Page 79: WSGI - PyCon Brasil 2008

WebObRequest & Response

wsgirefPython 2.5

Page 80: WSGI - PyCon Brasil 2008

PasteTanta Coisa Que Não Cabe Aqui

WebObRequest & Response

wsgirefPython 2.5

Page 81: WSGI - PyCon Brasil 2008

wsgirefmod_wsgi PasteTwistedFlupCherrypymodjy (Jython)ISAPI (IIS)NWSGI (.NET)

Servidores WSGI

chiral.web.httpdFastCoroutine-based pagesDeeply-integrated COMET supportNative memcached client

Fonte: wsgi.org/Servers

Page 82: WSGI - PyCon Brasil 2008

mod_wsgi

Page 83: WSGI - PyCon Brasil 2008

Auto-reloadApenas com `touch`

Page 84: WSGI - PyCon Brasil 2008

virtualenv

Auto-reloadApenas com `touch`

Page 85: WSGI - PyCon Brasil 2008

Authentication ProviderEx.: Trac e DjangoApenas no Apache 2.2

virtualenv

Auto-reloadApenas com `touch`

Page 86: WSGI - PyCon Brasil 2008

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`

Page 87: WSGI - PyCon Brasil 2008
Page 88: WSGI - PyCon Brasil 2008
Page 89: WSGI - PyCon Brasil 2008
Page 90: WSGI - PyCon Brasil 2008

Google App Engine

Page 91: WSGI - PyCon Brasil 2008

Google App EngineWSGI

Já vem com Django

Page 92: WSGI - PyCon Brasil 2008

TIOBE Index 2007

Page 93: WSGI - PyCon Brasil 2008

PortabilidadeServidores e Frameworks

Page 94: WSGI - PyCon Brasil 2008

Desempenhomod_wsgi

PortabilidadeServidores e Frameworks

Page 95: WSGI - PyCon Brasil 2008

ReaproveitamentoMiddleware

Desempenhomod_wsgi

PortabilidadeServidores e Frameworks

Page 97: WSGI - PyCon Brasil 2008

wsgi.org

humberto.digi.com.br

Page 98: WSGI - PyCon Brasil 2008

wsgi.org

humberto.digi.com.br

twitter.com/hdiogenes