I tried to read the code of Bottle, a web framework of Python. The source code for Bottle is aggregated in bottle.py, so you should read this file.
Click here for GitHub. https://github.com/bottlepy/bottle
The code quoted below is from bottle.py with only the necessary parts. In addition, I am adding comments in places.
It is difficult to follow the details from the beginning, so I would like to grasp the overall flow first.
I would like to follow the source in order from startup. Start Bottle as follows, for example, according to the documentation.
from bottle import route, run, template
@route('/hello/<name>')
def index(name):
return template('<b>Hello {{name}}</b>!', name=name)
run(host='localhost', port=8080)
Let's start with run ()
.
Again, just in case, the source code is (mostly) quoted.
bottle.py
def run(app=None,
server='wsgiref',
host='127.0.0.1',
port=8080,
interval=1,
reloader=False,
quiet=False,
plugins=None,
debug=None,
config=None, **kargs):
app = app or default_app()
if server in server_names:
server = server_names.get(server)
if isinstance(server, basestring):
server = load(server)
if isinstance(server, type):
server = server(host=host, port=port, **kargs)
if not isinstance(server, ServerAdapter):
raise ValueError("Unknown or unsupported server: %r" % server)
if reloader:
lockfile = os.environ.get('BOTTLE_LOCKFILE')
bgcheck = FileCheckerThread(lockfile, interval)
with bgcheck:
server.run(app)
if bgcheck.status == 'reload':
sys.exit(3)
else:
server.run(app)
in short
app = app or default_app() #The default is app=None
server = server_names.get(server) #The default is server='wsgiref'
server.run(app)
That's right.
First, let's take a look at the definition of default_app ()
on the first line.
The definition part is
apps = app = default_app = AppStack()
so,
bottle.py
class AppStack(list):
""" A stack-like list. Calling it returns the head of the stack. """
def __call__(self):
""" Return the current default application. """
return self.default
def push(self, value=None):
""" Add a new :class:`Bottle` instance to the stack """
if not isinstance(value, Bottle):
value = Bottle()
self.append(value)
return value
new_app = push
@property
def default(self):
try:
return self[-1]
except IndexError:
return self.push()
I see. AppStack inherits list.
From the side that uses ʻAppStack () (
server), the important thing is the
call` method.
To summarize the review of the call method,
app = AppStack()
app()
It seems that a Bottle
instance will be returned (if it is the default argument).
At the same time, it's a mechanism to accumulate it as a list.
I'd like to put together the source of the Bottle
class, but as the name suggests, this is the core of Bottle and is long.
In the first place, the ʻAppStack` instance is
server.run(app)
It is passed as an argument to the run method of server, and it should be called ʻapp ()after that, so let's check
server` first.
Looking back a little,
app = app or default_app() #The default is app=None
server = server_names.get(server) #The default is server='wsgiref'
server.run(app)
The story of trying to find out who the server
defined in the part of.
bottle.py
server_names = {
'cgi': CGIServer,
'flup': FlupFCGIServer,
'wsgiref': WSGIRefServer, #This is the default
'waitress': WaitressServer,
#The following is omitted
}
It is a dictionary, and each value inherits the ServerAdapter
class. Since this is a "parent of form only", I suddenly go to the WSGIRefServer
class.
bottle.py
class WSGIRefServer(ServerAdapter):
def run(self, app): # pragma: no cover
from wsgiref.simple_server import make_server
from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
import socket
server_cls = self.options.get('server_class', WSGIServer)
self.srv = make_server(self.host, self.port, app, server_cls,
handler_cls)
self.port = self.srv.server_port # update port actual port (0 means random)
try:
self.srv.serve_forever()
except KeyboardInterrupt:
self.srv.server_close() # Prevent ResourceWarning: unclosed socket
raise
ʻApp = AppStack ()passed to
server.run ()is passed to the
make_serverfunction. The
make_server function itself is from the python standard library
wsgiref`.
https://docs.python.org/ja/3/library/wsgiref.html
Web Server Gateway Interface (WSGI) is a standard interface between web server software and web applications written in Python. Having a standard interface makes it easy to use applications that support WSGI on a number of different web servers.
So, let's take a look at the sample in the wsgiref
documentation.
from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server
def simple_app(environ, start_response):
setup_testing_defaults(environ)
status = '200 OK'
headers = [('Content-type', 'text/plain')]
start_response(status, headers)
ret = ["%s: %s\n" % (key, value)
for key, value in environ.iteritems()]
return ret
httpd = make_server('', 8000, simple_app)
print "Serving on port 8000..."
httpd.serve_forever()
-Pass the function you defined yourself (simple_app
function in the sample) as the third argument to the make_server
function!
-Then, when a request comes in, you can let that function handle it!
-However, prepare two arguments (environ, start_response)
!
That's right.
So, looking back at the sources so far, that function (simple_app
in the sample) wasBottle ()
.
It's an instance, not a function!
but it's okay!
If you have a __call__
instance, you can call it like a function!
It seems to be tedious, but if you organize it, it is a sample
simple_app(environ, start_response)
The act to be executed is
Bottle()(environ, start_response)
Is equivalent to being executed.
That is </ b>
It seems obvious, but the Bottle instance is responsible for the actual processing </ b>
I found out.
Actually
bottle.py
class Bottle(object):
def __call__(self, environ, start_response):
Is defined.
It was so long.
The Bottle
class should be Next.
Bottle ()
is passed as an argument to the make_server
function of the wsgiref
library, and this is responsible for the actual processing.
Recommended Posts