WSGI - PyCon Brasil 2008

  • View
    1.530

  • Download
    1

  • Category

    Business

Preview:

Citation preview

WSGIA resposta para a questão definitiva sobre

Python, a web e tudo mais?

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

• 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.)

“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

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

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

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

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...

Simplicidade

Portabilidade requer...

Implementação FácilSimplicidade

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]

Também é fácil implementar um servidor

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...

Conversão interna para WSGI (middleware)

Passo II

Pylons

Em andamento...

Repoze

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

Google App EngineWSGI

Já vem com Django

TIOBE Index 2007

PortabilidadeServidores e Frameworks

Desempenhomod_wsgi

PortabilidadeServidores e Frameworks

ReaproveitamentoMiddleware

Desempenhomod_wsgi

PortabilidadeServidores e Frameworks

wsgi.org

humberto.digi.com.br

wsgi.org

humberto.digi.com.br

twitter.com/hdiogenes