werkzeug
94 строки · 3.4 Кб
1"""Implements the wiki WSGI application which dispatches requests to
2specific wiki pages and actions.
3"""
4from os import path
5
6from sqlalchemy import create_engine
7from werkzeug.middleware.shared_data import SharedDataMiddleware
8from werkzeug.utils import redirect
9from werkzeug.wsgi import ClosingIterator
10
11from . import actions
12from .database import metadata
13from .database import session
14from .specialpages import page_not_found
15from .specialpages import pages
16from .utils import href
17from .utils import local
18from .utils import local_manager
19from .utils import Request
20
21#: path to shared data
22SHARED_DATA = path.join(path.dirname(__file__), "shared")
23
24
25class SimpleWiki:
26"""
27Our central WSGI application.
28"""
29
30def __init__(self, database_uri):
31self.database_engine = create_engine(database_uri)
32
33# apply our middlewares. we apply the middlewars *inside* the
34# application and not outside of it so that we never lose the
35# reference to the `SimpleWiki` object.
36self._dispatch = SharedDataMiddleware(
37self.dispatch_request, {"/_shared": SHARED_DATA}
38)
39
40# free the context locals at the end of the request
41self._dispatch = local_manager.make_middleware(self._dispatch)
42
43def init_database(self):
44"""Called from the management script to generate the db."""
45metadata.create_all(bind=self.database_engine)
46
47def bind_to_context(self):
48"""
49Useful for the shell. Binds the application to the current active
50context. It's automatically called by the shell command.
51"""
52local.application = self
53
54def dispatch_request(self, environ, start_response):
55"""Dispatch an incoming request."""
56# set up all the stuff we want to have for this request. That is
57# creating a request object, propagating the application to the
58# current context and instantiating the database session.
59self.bind_to_context()
60request = Request(environ)
61request.bind_to_context()
62
63# get the current action from the url and normalize the page name
64# which is just the request path
65action_name = request.args.get("action") or "show"
66page_name = "_".join([x for x in request.path.strip("/").split() if x])
67
68# redirect to the Main_Page if the user requested the index
69if not page_name:
70response = redirect(href("Main_Page"))
71
72# check special pages
73elif page_name.startswith("Special:"):
74if page_name[8:] not in pages:
75response = page_not_found(request, page_name)
76else:
77response = pages[page_name[8:]](request)
78
79# get the callback function for the requested action from the
80# action module. It's "on_" + the action name. If it doesn't
81# exists call the missing_action method from the same module.
82else:
83action = getattr(actions, f"on_{action_name}", None)
84if action is None:
85response = actions.missing_action(request, action_name)
86else:
87response = action(request, page_name)
88
89# make sure the session is removed properly
90return ClosingIterator(response(environ, start_response), session.remove)
91
92def __call__(self, environ, start_response):
93"""Just forward a WSGI call to the first internal middleware."""
94return self._dispatch(environ, start_response)
95