diff --git a/DEVELOPER.md b/DEVELOPER.md
new file mode 100644
index 0000000..afcc07a
--- /dev/null
+++ b/DEVELOPER.md
@@ -0,0 +1,79 @@
+# Developer readme
+
+This mockup app runs using Kivy, wsgiref and pywebview (desktop only) on Python >= 2.7.13. This document describes how to test it.
+
+## Architecture
+
+To ensure compatibility with Android, the application was split in two parts:
+
+1. The UI's main.py (main_desktop.py for desktop computers)
+2. The background webserver as a service, in service/main.py
+
+When one launch main.py, the call to `AndroidService('ZimAndroidWebserver', 'running').start()` will launch whatever is in `service/main.py`.
+
+`android.txt` is a simple file describing how to present the app in the Kivy Launcher (necessary else it won't display). It is useless for APK packaging.
+
+## Running on desktop
+
+To run on desktop, simply install pywebview module and then run `main_desktop.py`. Should work on Windows, Linux and MacOSX.
+
+## Running on Android
+
+The easiest way is to launch via the Kivy Launcher app.
+
+A good development environment consists of:
+
+1. Bluestacks emulator
+2. TotalCommander app to copy from the Bluestacks shared folder in `sdcard/windows/BstSharedFolder` to `sdcard/kivy`.
+3. Kivy Launcher, to launch the app without needing compilation (just need to copy the project folder into `sdcard/kivy`).
+4. Launch `C:\Program Files (x86)\BlueStacks\HD-Adb.exe logcat > C:\temp\logcat.txt` to enable logcat debug log (necessary to track down errors and bugs).
+
+Once you have all of these, it becomes very easy and fast to test any change you do, just copy/paste the new version of the scripts over the old ones in `sdcard/kivy`.
+
+Note that Kivy Launcher has some limitations:
+
+* Partial implementation of native Python libraries (eg, no wsgiref) and variables (eg, no IPv6 variables). However, the launcher supports any pure Python library, so wsgiref can be supported by copying the folder from python libs.
+
+A limitation of Kivy for GUI is that for the moment it is not possible to add any other widget when using a webview (except for the magicians at [kivy-gmaps](https://github.com/tito/kivy-gmaps)).
+
+However, using Kivy, it is possible to do pretty much anything that can be done via Java: use plyer to access Android libraries in a pythonic way (but with limited functionality), or pyjnius to have full access (but with less pythonic syntax).
+
+## Compiling an APK
+
+To compile an APK, one need to use python-for-android (P4A), by the same authors as Kivy.
+
+P4A can compile pretty much anything, but it needs a backend. For the moment, there are a few ones: SDL2 (default for Kivy apps) and flask. But potentially anything can be a backend, just a recipe is needed.
+
+If the application is made with Kivy, we can use Buildozer, which is another layer on P4A to make building easier by using a buildozer.spec file instead of commandline arguments to P4A.
+
+P4A can only run on Linux at the moment, so a good solution is to use the Kivy Virtual Machine. [Read this post for more info on the steps necessary](https://github.com/jaap-karssenberg/zim-android-mockapp/issues/4#issuecomment-354473348).
+
+Here are the steps:
+
+* Use the [Kivy VirtualBox machine image](https://kivy.org/docs/guide/packaging-android-vm.html).
+* Update python-for-android (to avoid the ["Invalid or unsupported command list" error](https://github.com/kivy/python-for-android/issues/1070)) by installing from github: `pip install git+https://github.com/kivy/python-for-android.git`. In case you have issues with the latest release, I used the [commit 8829312fd187121384939627ec09354657821ae9](https://github.com/kivy/python-for-android/commit/8829312fd187121384939627ec09354657821ae9) specifically.
+* Install [dependencies](https://python-for-android.readthedocs.io/en/latest/quickstart/#installing-dependencies) (replace openjdk-7-jdk by openjdk-8-jdk).
+* Continue the python-for-android install tutorial from ["Installing Android SDK"](https://python-for-android.readthedocs.io/en/latest/quickstart/#installing-android-sdk) onwards.
+* Note: be sure to clean up `/home/kivy/.local/share/python-for-android/dists/' whenever you want to recompile your APK cleanly (particularly if you had errors during compilation because of wrong arguments).
+* If you get an error complaining that ant compilation failed because of `android-sdk/tools/ant/build.xml` not found, it means that p4a did not update their codebase yet, because the Android SDK does not contain ant/build.xml file anymore. Then what you need to do is to use an older Android SDK [as explained here](https://stackoverflow.com/questions/42912824/the-ant-folder-is-suddenly-missing-from-android-sdk-did-google-remove-it), such as android-sdk-r25.2.5.
+* Another error will popup: `(use -source 7 or higher to enable multi-catch statement) `. This complains that P4A uses multi-catch statements, but it compiles against a too old version of the Java SDK. What you need to do is to edit the `android-sdk/tools/ant/build.xml` file: change this:
+
+```xml
+
+
+```
+to:
+
+```xml
+
+
+```
+* Then finally, go to the application root folder and use this command to launch the compilation (make sure to change accordingly the path to your zim-android-mockup project folder):
+
+`p4a apk --private $HOME/Desktop/zim-android-mockapp --package=org.zimandroid.zim --name "ZimAndroid" --version 0.1 --bootstrap=webview --requirements=python2,flask,pywebview,wsgiref --port=23950`
+
+This should generate an APK alright! Note that this command was done on a pure pywebview build (no android webview), thus it may now need some changes to work again.
+
+## Authors
+
+Stephen Larroque & Jaap Karssenberg
\ No newline at end of file
diff --git a/README.md b/README.md
index bace325..efa7d1a 100644
--- a/README.md
+++ b/README.md
@@ -5,3 +5,7 @@ Mock app to start development of a zim android app
See the related wiki pages at:
https://github.com/jaap-karssenberg/zim-android-mockapp/wiki
+Here is a screenshot of the current stage (mockup wsgi webserver on Android as a service + webview UI):
+
+
+
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..e882653
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,42 @@
+# TODO
+
+## Webview UI
+
+* Python 3 compatibility for webview on Android: https://github.com/kollivier/pyeverywhere/blob/master/src/pew/kivy_pew/webview.py
+* Back button goes back in history (instead of quitting): https://github.com/suchyDev/Kivy-Dynamic-Screens-Template/blob/master/screens/screenwebview.py
+
+## Zim reading
+
+* Converter Zim note -> HTML (with minimal dependencies to be maximally compatible with Android)
+* Make the wsgi server a dynamic content generator
+* Build notes tree
+* Allow to setup config from wsgi server (Zim notes location, store multiple zim notes "books"/folder to quickly jump from one to the other, etc)
+
+## Zim editing
+
+WYSIWYG would be perfect but will be challenging. Basically, there are two ways:
+
+* using a textarea that gets styled, but it can get out of sync very easily with the hidden underlying content.
+* using contentEditable property of div and MutationObserver.
+
+This last option seems the best. The idea would be to do both block-level and inline-level markup parsing, by sending the changes back to the webserver, which would add the content to the origin Zim note and convert to HTML to send back to the current window, in order to show a nicely formatted text.
+
+BUT: need to find an ergonomic way to enable contentEditable=True, because it cannot be all the time enabled (else links are unclickable!) -> an "Edit mode" link, at the top of the screen and which would always be contentEditable=False, would be a good way! Or by double tapping a text area (and double tapping again would disable).
+
+The best tutorial is probably this one:
+
+[DIY tutorial using contentEditable property of div and MutationObserver](http://pothibo.com/2015/3/building-a-markdown-editor) ([mirror here](https://web.archive.org/web/20170616192810/http://pothibo.com/2015/3/building-a-markdown-editor))
+
+Here are some projects implementing this approach for Markdown, can be good for inspiration:
+
+* A Python based one to convert back and forth to Markdown (can be a good inspiration): http://md-wysiwyg.sourceforge.net/
+* https://github.com/IonicaBizau/medium-editor-markdown
+* https://github.com/sofish/pen#readme
+* https://github.com/bergie/hallo
+* http://marklighteditor.com/ (quite interesting as it allows on the fly rendering after typing markup, instead of highlighting and clicking on a button - this would be super useful on a mobile app)
+* [Another list of WYSIWYG Markdown editors](https://softwarerecs.stackexchange.com/a/30531/16275).
+
+## Packaging
+
+Make a python-for-android or Buildozer packaging recipe.
+
diff --git a/android.txt b/android.txt
new file mode 100644
index 0000000..1810875
--- /dev/null
+++ b/android.txt
@@ -0,0 +1,3 @@
+title=ZimAndroid
+author=Jaap Karssenberg & Stephen Larroque
+orientation=portrait
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..9a401b6
Binary files /dev/null and b/icon.png differ
diff --git a/img/zimandroidmockup.png b/img/zimandroidmockup.png
new file mode 100644
index 0000000..b2cecab
Binary files /dev/null and b/img/zimandroidmockup.png differ
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..f7f157d
--- /dev/null
+++ b/main.py
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+
+print('ZIMDEBUGCLIENT')
+
+__version__ = '0.1'
+
+import sys
+import time
+
+from kivy.app import App
+
+from kivy.uix.widget import Widget
+from kivy.clock import Clock
+from jnius import autoclass
+from android.runnable import run_on_ui_thread
+
+
+WebView = autoclass('android.webkit.WebView')
+WebViewClient = autoclass('android.webkit.WebViewClient')
+activity = autoclass('org.renpy.android.PythonActivity').mActivity
+
+class Wv(Widget):
+ def __init__(self, **kwargs):
+ super(Wv, self).__init__(**kwargs)
+ Clock.schedule_once(self.create_webview, 0)
+
+ @run_on_ui_thread
+ def create_webview(self, *args):
+ webview = WebView(activity)
+ settings = webview.getSettings()
+ settings.setJavaScriptEnabled(True)
+ #settings.setUseWideViewPort(True) # enables viewport html meta tags
+ #settings.setLoadWithOverviewMode(True) # uses viewport
+ #settings.setSupportZoom(True) # enables zoom
+ #settings.setBuiltInZoomControls(True) # enables zoom controls
+ wvc = WebViewClient()
+ webview.setWebViewClient(wvc)
+ activity.setContentView(webview)
+ webview.loadUrl('http://localhost:23950')
+ #webview.loadUrl('http://10.0.2.2:23950') # use 10.0.2.2 on Android emulators to access local host of PARENT computer
+ #webview.loadData(html, "text/html", "utf-8") # to directly load html content
+
+
+class WebviewApp(App):
+ def build(self):
+ return Wv()
+
+
+class WebserverService(App):
+ def build(self):
+ self.start_service()
+
+ def start_service(self):
+ from android import AndroidService
+ service = AndroidService('ZimAndroidWebserver', 'running') # this will launch what is in the folder service/main.py as a service
+ service.start('ZimAndroidWebserver service started')
+ self.service = service
+
+ def stop_service(self):
+ if self.service:
+ self.service.stop()
+ self.service = None
+
+ def on_stop(self): # TODO: does not work! We need to close the service on leaving!
+ self.stop_service()
+
+
+if __name__ == '__main__':
+ webserver = WebserverService()
+ webserver.run()
+ time.sleep(0.1)
+ webserver.stop_service() # workaround because service might still be running on exit
+ time.sleep(0.5)
+ webserver.start_service()
+ time.sleep(1.0) # wait a long time just to make extra sure the webserver is running before showing the webview (else there is no refresh button!)
+ WebviewApp().run()
diff --git a/main_desktop.py b/main_desktop.py
new file mode 100755
index 0000000..f3c3b6c
--- /dev/null
+++ b/main_desktop.py
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+
+import sys
+
+from threading import Thread, Lock
+import logging
+import webview
+from time import sleep
+from service.main import run_server
+
+server_lock = Lock()
+
+logger = logging.getLogger(__name__)
+
+
+def url_ok(url, port):
+ """
+ Ensure the webserver is correctly launched before giving the hand to the user
+ """
+ # Use httplib on Python 2
+ try:
+ from http.client import HTTPConnection
+ except ImportError:
+ from httplib import HTTPConnection
+
+ try:
+ conn = HTTPConnection(url, port)
+ conn.request("GET", "/")
+ r = conn.getresponse()
+ return r.status == 200
+ except:
+ logger.exception("Server not started")
+ return False
+
+def start_server():
+ logger.debug("Starting server")
+ t = Thread(target=run_server)
+ t.daemon = True
+ t.start()
+ logger.debug("Checking server")
+
+ while not url_ok("127.0.0.1", 23950):
+ sleep(0.1)
+
+ logger.debug("Server started")
+
+def main():
+ start_server() # need to start the server as a thread ; in Android this is unnecessary as it will be started as a (background) service
+ webview.create_window("ZimAndroid", "http://localhost:23950", min_size=(640, 480)) # this is a blocking command, nothing can be executed after! (or only in a thread launched before)
+
+if __name__ == '__main__':
+ main()
diff --git a/pages/index.html b/pages/index.html
index 802e98a..72792b3 100644
--- a/pages/index.html
+++ b/pages/index.html
@@ -20,6 +20,7 @@
-
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris varius dui est, in porta lorem auctor at. Donec fringilla mollis neque vitae aliquam. Donec rhoncus, magna ut elementum ullamcorper, purus est mattis tellus, sit amet tristique lectus ligula gravida ligula. Etiam eget pellentesque dui. Nulla facilisis laoreet orci, sit amet pellentesque metus rutrum sit amet. Duis dapibus mi vel eros efficitur tristique. Aliquam erat volutpat. Aliquam erat volutpat. In mollis bibendum nunc ut pulvinar. Sed eros ante, tempor vel maximus et, accumsan ac ex. Nulla sit amet turpis libero. Pellentesque sed varius massa. Aenean at ultricies mi. Nullam faucibus lorem nunc, sit amet viverra lacus efficitur vel.
diff --git a/pages/page2.html b/pages/page2.html
index 602d0b2..1394707 100644
--- a/pages/page2.html
+++ b/pages/page2.html
@@ -12,19 +12,19 @@
nav ul li {display:inline; margin:5px;}
-
+
-
+
Page 2
-
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris varius dui est, in porta lorem auctor at. Donec fringilla mollis neque vitae aliquam. Donec rhoncus, magna ut elementum ullamcorper, purus est mattis tellus, sit amet tristique lectus ligula gravida ligula. Etiam eget pellentesque dui. Nulla facilisis laoreet orci, sit amet pellentesque metus rutrum sit amet. Duis dapibus mi vel eros efficitur tristique. Aliquam erat volutpat. Aliquam erat volutpat. In mollis bibendum nunc ut pulvinar. Sed eros ante, tempor vel maximus et, accumsan ac ex. Nulla sit amet turpis libero. Pellentesque sed varius massa. Aenean at ultricies mi. Nullam faucibus lorem nunc, sit amet viverra lacus efficitur vel.
diff --git a/pages/page3.html b/pages/page3.html
index a47a4a9..e7908bc 100644
--- a/pages/page3.html
+++ b/pages/page3.html
@@ -12,19 +12,19 @@
nav ul li {display:inline; margin:5px;}
-
+
-
+
Page 3
-
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris varius dui est, in porta lorem auctor at. Donec fringilla mollis neque vitae aliquam. Donec rhoncus, magna ut elementum ullamcorper, purus est mattis tellus, sit amet tristique lectus ligula gravida ligula. Etiam eget pellentesque dui. Nulla facilisis laoreet orci, sit amet pellentesque metus rutrum sit amet. Duis dapibus mi vel eros efficitur tristique. Aliquam erat volutpat. Aliquam erat volutpat. In mollis bibendum nunc ut pulvinar. Sed eros ante, tempor vel maximus et, accumsan ac ex. Nulla sit amet turpis libero. Pellentesque sed varius massa. Aenean at ultricies mi. Nullam faucibus lorem nunc, sit amet viverra lacus efficitur vel.
diff --git a/run_as_server.py b/run_as_server.py
deleted file mode 100755
index 94e70c2..0000000
--- a/run_as_server.py
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/python
-
-from wsgiref import simple_server
-
-from app import MockApp
-
-print "Running server at http://localhost:8080"
-
-app = MockApp()
-httpd = simple_server.make_server('localhost', 8081, app)
-httpd.serve_forever()
diff --git a/run_as_webkitgtk_window.py b/run_as_webkitgtk_window.py
deleted file mode 100755
index 0befdb4..0000000
--- a/run_as_webkitgtk_window.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/python
-
-from gi.repository import WebKit
-from gi.repository import Gtk
-
-from app import MockApp
-
-myapp = MockApp()
-view = WebKit.WebView()
-
-def load_page(path):
- html = myapp.get_html(path)
- view.load_html_string(html, path)
-
-
-def overrule_navigation(view, frame, request, action, decision):
- if action.get_reason() == WebKit.WebNavigationReason.OTHER:
- pass
- else:
- # TODO: for internal links load the page, for external links load external app
- decision.ignore() # overrule
- load_page(request.get_uri())
- return True
-
-
-view.connect('navigation-policy-decision-requested', overrule_navigation)
-
-
-def close(window):
- Gtk.main_quit()
-
-def main():
- Gtk.init()
- window = Gtk.Window()
- window.set_default_size(500, 500)
- swin = Gtk.ScrolledWindow()
- swin.add(view)
- window.add(swin)
- window.connect("destroy", close)
- window.show_all()
- Gtk.main()
-
-load_page('/')
-main()
diff --git a/service/__init__.py b/service/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/service/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/service/app.py b/service/app.py
new file mode 100644
index 0000000..f97ea90
--- /dev/null
+++ b/service/app.py
@@ -0,0 +1,61 @@
+#!/usr/bin/python
+
+import os
+import urllib
+
+class MockApp(object):
+
+ def get_html(self, path):
+ def start_response(response, headers):
+ if not response.startswith('200'):
+ raise AssertionError, response
+ else:
+ pass # Ignore all headers here
+
+ return self.__call__({'PATH_INFO': path}, start_response)
+
+ def __call__(self, environ, start_response):
+ '''Main function for handling a single request. Follows the
+ WSGI API.
+
+ @param environ: dictionary with environment variables for the
+ request and some special variables. See the PEP for expected
+ variables.
+
+ @param start_response: a function that can be called to set the
+ http response and headers. For example::
+
+ start_response(200, [('Content-Type', 'text/plain')])
+
+ @returns: the html page content as a list of lines
+ '''
+ path = environ.get('PATH_INFO', '/')
+ path = urllib.unquote(path)
+
+ if path == '/':
+ basename = 'index.html'
+ else:
+ if '/' in path:
+ x, basename = path.rsplit('/', 1)
+ else:
+ basename = path
+
+ pagespath = os.path.join(os.path.dirname(__file__), '..', 'pages')
+ file = os.path.join(pagespath, basename)
+ try:
+ html = open(file).read()
+ except:
+ start_response('404 Not found', [])
+ return ''
+ else:
+ start_response('200 OK', [('Content-Type', 'text/html; charset="utf-8"')])
+ html = html.replace('', '\n')
+ return html
+
+
+if __name__ == '__main__':
+ import sys
+ path = sys.argv[1]
+ app = MockApp()
+ print app.get_html(path)
+
diff --git a/service/main.py b/service/main.py
new file mode 100644
index 0000000..e02c635
--- /dev/null
+++ b/service/main.py
@@ -0,0 +1,20 @@
+#!/usr/bin/python
+
+print('ZIMDEBUGSERVICE')
+
+import os, sys
+sys.path.append(os.path.join(os.path.dirname(__file__)))
+
+from wsgiref import simple_server
+
+from app import MockApp
+
+def run_server():
+ print "Running server at http://localhost:23950"
+
+ app = MockApp()
+ httpd = simple_server.make_server('localhost', 23950, app)
+ httpd.serve_forever()
+
+if __name__ == '__main__':
+ run_server()
diff --git a/service/wsgiref/__init__.py b/service/wsgiref/__init__.py
new file mode 100644
index 0000000..46c579f
--- /dev/null
+++ b/service/wsgiref/__init__.py
@@ -0,0 +1,23 @@
+"""wsgiref -- a WSGI (PEP 333) Reference Library
+
+Current Contents:
+
+* util -- Miscellaneous useful functions and wrappers
+
+* headers -- Manage response headers
+
+* handlers -- base classes for server/gateway implementations
+
+* simple_server -- a simple BaseHTTPServer that supports WSGI
+
+* validate -- validation wrapper that sits between an app and a server
+ to detect errors in either
+
+To-Do:
+
+* cgi_gateway -- Run WSGI apps under CGI (pending a deployment standard)
+
+* cgi_wrapper -- Run CGI apps under WSGI
+
+* router -- a simple middleware component that handles URL traversal
+"""
diff --git a/service/wsgiref/handlers.py b/service/wsgiref/handlers.py
new file mode 100644
index 0000000..8cb57e2
--- /dev/null
+++ b/service/wsgiref/handlers.py
@@ -0,0 +1,450 @@
+"""Base classes for server/gateway implementations"""
+
+from types import StringType
+from util import FileWrapper, guess_scheme, is_hop_by_hop
+from headers import Headers
+
+import sys, os, time
+
+__all__ = ['BaseHandler', 'SimpleHandler', 'BaseCGIHandler', 'CGIHandler']
+
+try:
+ dict
+except NameError:
+ def dict(items):
+ d = {}
+ for k,v in items:
+ d[k] = v
+ return d
+
+# Uncomment for 2.2 compatibility.
+#try:
+# True
+# False
+#except NameError:
+# True = not None
+# False = not True
+
+
+# Weekday and month names for HTTP date/time formatting; always English!
+_weekdayname = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
+_monthname = [None, # Dummy so we can use 1-based month numbers
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+
+def format_date_time(timestamp):
+ year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp)
+ return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
+ _weekdayname[wd], day, _monthname[month], year, hh, mm, ss
+ )
+
+
+class BaseHandler:
+ """Manage the invocation of a WSGI application"""
+
+ # Configuration parameters; can override per-subclass or per-instance
+ wsgi_version = (1,0)
+ wsgi_multithread = True
+ wsgi_multiprocess = True
+ wsgi_run_once = False
+
+ origin_server = True # We are transmitting direct to client
+ http_version = "1.0" # Version that should be used for response
+ server_software = None # String name of server software, if any
+
+ # os_environ is used to supply configuration from the OS environment:
+ # by default it's a copy of 'os.environ' as of import time, but you can
+ # override this in e.g. your __init__ method.
+ os_environ = dict(os.environ.items())
+
+ # Collaborator classes
+ wsgi_file_wrapper = FileWrapper # set to None to disable
+ headers_class = Headers # must be a Headers-like class
+
+ # Error handling (also per-subclass or per-instance)
+ traceback_limit = None # Print entire traceback to self.get_stderr()
+ error_status = "500 Internal Server Error"
+ error_headers = [('Content-Type','text/plain')]
+ error_body = "A server error occurred. Please contact the administrator."
+
+ # State variables (don't mess with these)
+ status = result = None
+ headers_sent = False
+ headers = None
+ bytes_sent = 0
+
+ def run(self, application):
+ """Invoke the application"""
+ # Note to self: don't move the close()! Asynchronous servers shouldn't
+ # call close() from finish_response(), so if you close() anywhere but
+ # the double-error branch here, you'll break asynchronous servers by
+ # prematurely closing. Async servers must return from 'run()' without
+ # closing if there might still be output to iterate over.
+ try:
+ self.setup_environ()
+ self.result = application(self.environ, self.start_response)
+ self.finish_response()
+ except:
+ try:
+ self.handle_error()
+ except:
+ # If we get an error handling an error, just give up already!
+ self.close()
+ raise # ...and let the actual server figure it out.
+
+
+ def setup_environ(self):
+ """Set up the environment for one request"""
+
+ env = self.environ = self.os_environ.copy()
+ self.add_cgi_vars()
+
+ env['wsgi.input'] = self.get_stdin()
+ env['wsgi.errors'] = self.get_stderr()
+ env['wsgi.version'] = self.wsgi_version
+ env['wsgi.run_once'] = self.wsgi_run_once
+ env['wsgi.url_scheme'] = self.get_scheme()
+ env['wsgi.multithread'] = self.wsgi_multithread
+ env['wsgi.multiprocess'] = self.wsgi_multiprocess
+
+ if self.wsgi_file_wrapper is not None:
+ env['wsgi.file_wrapper'] = self.wsgi_file_wrapper
+
+ if self.origin_server and self.server_software:
+ env.setdefault('SERVER_SOFTWARE',self.server_software)
+
+
+ def finish_response(self):
+ """Send any iterable data, then close self and the iterable
+
+ Subclasses intended for use in asynchronous servers will
+ want to redefine this method, such that it sets up callbacks
+ in the event loop to iterate over the data, and to call
+ 'self.close()' once the response is finished.
+ """
+ try:
+ if not self.result_is_file() or not self.sendfile():
+ for data in self.result:
+ self.write(data)
+ self.finish_content()
+ finally:
+ self.close()
+
+
+ def get_scheme(self):
+ """Return the URL scheme being used"""
+ return guess_scheme(self.environ)
+
+
+ def set_content_length(self):
+ """Compute Content-Length or switch to chunked encoding if possible"""
+ try:
+ blocks = len(self.result)
+ except (TypeError,AttributeError,NotImplementedError):
+ pass
+ else:
+ if blocks==1:
+ self.headers['Content-Length'] = str(self.bytes_sent)
+ return
+ # XXX Try for chunked encoding if origin server and client is 1.1
+
+
+ def cleanup_headers(self):
+ """Make any necessary header changes or defaults
+
+ Subclasses can extend this to add other defaults.
+ """
+ if 'Content-Length' not in self.headers:
+ self.set_content_length()
+
+ def start_response(self, status, headers,exc_info=None):
+ """'start_response()' callable as specified by PEP 333"""
+
+ if exc_info:
+ try:
+ if self.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 self.headers is not None:
+ raise AssertionError("Headers already set!")
+
+ assert type(status) is StringType,"Status must be a string"
+ assert len(status)>=4,"Status must be at least 4 characters"
+ assert int(status[:3]),"Status message must begin w/3-digit code"
+ assert status[3]==" ", "Status message must have a space after code"
+ if __debug__:
+ for name,val in headers:
+ assert type(name) is StringType,"Header names must be strings"
+ assert type(val) is StringType,"Header values must be strings"
+ assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
+ self.status = status
+ self.headers = self.headers_class(headers)
+ return self.write
+
+
+ def send_preamble(self):
+ """Transmit version/status/date/server, via self._write()"""
+ if self.origin_server:
+ if self.client_is_modern():
+ self._write('HTTP/%s %s\r\n' % (self.http_version,self.status))
+ if 'Date' not in self.headers:
+ self._write(
+ 'Date: %s\r\n' % format_date_time(time.time())
+ )
+ if self.server_software and 'Server' not in self.headers:
+ self._write('Server: %s\r\n' % self.server_software)
+ else:
+ self._write('Status: %s\r\n' % self.status)
+
+ def write(self, data):
+ """'write()' callable as specified by PEP 333"""
+
+ assert type(data) is StringType,"write() argument must be string"
+
+ if not self.status:
+ raise AssertionError("write() before start_response()")
+
+ elif not self.headers_sent:
+ # Before the first output, send the stored headers
+ self.bytes_sent = len(data) # make sure we know content-length
+ self.send_headers()
+ else:
+ self.bytes_sent += len(data)
+
+ # XXX check Content-Length and truncate if too many bytes written?
+ self._write(data)
+ self._flush()
+
+
+ def sendfile(self):
+ """Platform-specific file transmission
+
+ Override this method in subclasses to support platform-specific
+ file transmission. It is only called if the application's
+ return iterable ('self.result') is an instance of
+ 'self.wsgi_file_wrapper'.
+
+ This method should return a true value if it was able to actually
+ transmit the wrapped file-like object using a platform-specific
+ approach. It should return a false value if normal iteration
+ should be used instead. An exception can be raised to indicate
+ that transmission was attempted, but failed.
+
+ NOTE: this method should call 'self.send_headers()' if
+ 'self.headers_sent' is false and it is going to attempt direct
+ transmission of the file.
+ """
+ return False # No platform-specific transmission by default
+
+
+ def finish_content(self):
+ """Ensure headers and content have both been sent"""
+ if not self.headers_sent:
+ # Only zero Content-Length if not set by the application (so
+ # that HEAD requests can be satisfied properly, see #3839)
+ self.headers.setdefault('Content-Length', "0")
+ self.send_headers()
+ else:
+ pass # XXX check if content-length was too short?
+
+ def close(self):
+ """Close the iterable (if needed) and reset all instance vars
+
+ Subclasses may want to also drop the client connection.
+ """
+ try:
+ if hasattr(self.result,'close'):
+ self.result.close()
+ finally:
+ self.result = self.headers = self.status = self.environ = None
+ self.bytes_sent = 0; self.headers_sent = False
+
+
+ def send_headers(self):
+ """Transmit headers to the client, via self._write()"""
+ self.cleanup_headers()
+ self.headers_sent = True
+ if not self.origin_server or self.client_is_modern():
+ self.send_preamble()
+ self._write(str(self.headers))
+
+
+ def result_is_file(self):
+ """True if 'self.result' is an instance of 'self.wsgi_file_wrapper'"""
+ wrapper = self.wsgi_file_wrapper
+ return wrapper is not None and isinstance(self.result,wrapper)
+
+
+ def client_is_modern(self):
+ """True if client can accept status and headers"""
+ return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'
+
+
+ def log_exception(self,exc_info):
+ """Log the 'exc_info' tuple in the server log
+
+ Subclasses may override to retarget the output or change its format.
+ """
+ try:
+ from traceback import print_exception
+ stderr = self.get_stderr()
+ print_exception(
+ exc_info[0], exc_info[1], exc_info[2],
+ self.traceback_limit, stderr
+ )
+ stderr.flush()
+ finally:
+ exc_info = None
+
+ def handle_error(self):
+ """Log current error, and send error output to client if possible"""
+ self.log_exception(sys.exc_info())
+ if not self.headers_sent:
+ self.result = self.error_output(self.environ, self.start_response)
+ self.finish_response()
+ # XXX else: attempt advanced recovery techniques for HTML or text?
+
+ def error_output(self, environ, start_response):
+ """WSGI mini-app to create error output
+
+ By default, this just uses the 'error_status', 'error_headers',
+ and 'error_body' attributes to generate an output page. It can
+ be overridden in a subclass to dynamically generate diagnostics,
+ choose an appropriate message for the user's preferred language, etc.
+
+ Note, however, that it's not recommended from a security perspective to
+ spit out diagnostics to any old user; ideally, you should have to do
+ something special to enable diagnostic output, which is why we don't
+ include any here!
+ """
+ start_response(self.error_status,self.error_headers[:],sys.exc_info())
+ return [self.error_body]
+
+
+ # Pure abstract methods; *must* be overridden in subclasses
+
+ def _write(self,data):
+ """Override in subclass to buffer data for send to client
+
+ It's okay if this method actually transmits the data; BaseHandler
+ just separates write and flush operations for greater efficiency
+ when the underlying system actually has such a distinction.
+ """
+ raise NotImplementedError
+
+ def _flush(self):
+ """Override in subclass to force sending of recent '_write()' calls
+
+ It's okay if this method is a no-op (i.e., if '_write()' actually
+ sends the data.
+ """
+ raise NotImplementedError
+
+ def get_stdin(self):
+ """Override in subclass to return suitable 'wsgi.input'"""
+ raise NotImplementedError
+
+ def get_stderr(self):
+ """Override in subclass to return suitable 'wsgi.errors'"""
+ raise NotImplementedError
+
+ def add_cgi_vars(self):
+ """Override in subclass to insert CGI variables in 'self.environ'"""
+ raise NotImplementedError
+
+
+class SimpleHandler(BaseHandler):
+ """Handler that's just initialized with streams, environment, etc.
+
+ This handler subclass is intended for synchronous HTTP/1.0 origin servers,
+ and handles sending the entire response output, given the correct inputs.
+
+ Usage::
+
+ handler = SimpleHandler(
+ inp,out,err,env, multithread=False, multiprocess=True
+ )
+ handler.run(app)"""
+
+ def __init__(self,stdin,stdout,stderr,environ,
+ multithread=True, multiprocess=False
+ ):
+ self.stdin = stdin
+ self.stdout = stdout
+ self.stderr = stderr
+ self.base_env = environ
+ self.wsgi_multithread = multithread
+ self.wsgi_multiprocess = multiprocess
+
+ def get_stdin(self):
+ return self.stdin
+
+ def get_stderr(self):
+ return self.stderr
+
+ def add_cgi_vars(self):
+ self.environ.update(self.base_env)
+
+ def _write(self,data):
+ self.stdout.write(data)
+ self._write = self.stdout.write
+
+ def _flush(self):
+ self.stdout.flush()
+ self._flush = self.stdout.flush
+
+
+class BaseCGIHandler(SimpleHandler):
+
+ """CGI-like systems using input/output/error streams and environ mapping
+
+ Usage::
+
+ handler = BaseCGIHandler(inp,out,err,env)
+ handler.run(app)
+
+ This handler class is useful for gateway protocols like ReadyExec and
+ FastCGI, that have usable input/output/error streams and an environment
+ mapping. It's also the base class for CGIHandler, which just uses
+ sys.stdin, os.environ, and so on.
+
+ The constructor also takes keyword arguments 'multithread' and
+ 'multiprocess' (defaulting to 'True' and 'False' respectively) to control
+ the configuration sent to the application. It sets 'origin_server' to
+ False (to enable CGI-like output), and assumes that 'wsgi.run_once' is
+ False.
+ """
+
+ origin_server = False
+
+
+class CGIHandler(BaseCGIHandler):
+
+ """CGI-based invocation via sys.stdin/stdout/stderr and os.environ
+
+ Usage::
+
+ CGIHandler().run(app)
+
+ The difference between this class and BaseCGIHandler is that it always
+ uses 'wsgi.run_once' of 'True', 'wsgi.multithread' of 'False', and
+ 'wsgi.multiprocess' of 'True'. It does not take any initialization
+ parameters, but always uses 'sys.stdin', 'os.environ', and friends.
+
+ If you need to override any of these parameters, use BaseCGIHandler
+ instead.
+ """
+
+ wsgi_run_once = True
+ # Do not allow os.environ to leak between requests in Google App Engine
+ # and other multi-run CGI use cases. This is not easily testable.
+ # See http://bugs.python.org/issue7250
+ os_environ = {}
+
+ def __init__(self):
+ BaseCGIHandler.__init__(
+ self, sys.stdin, sys.stdout, sys.stderr, dict(os.environ.items()),
+ multithread=False, multiprocess=True
+ )
diff --git a/service/wsgiref/headers.py b/service/wsgiref/headers.py
new file mode 100644
index 0000000..5a95e84
--- /dev/null
+++ b/service/wsgiref/headers.py
@@ -0,0 +1,169 @@
+"""Manage HTTP Response Headers
+
+Much of this module is red-handedly pilfered from email.message in the stdlib,
+so portions are Copyright (C) 2001,2002 Python Software Foundation, and were
+written by Barry Warsaw.
+"""
+
+from types import ListType, TupleType
+
+# Regular expression that matches `special' characters in parameters, the
+# existence of which force quoting of the parameter value.
+import re
+tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
+
+def _formatparam(param, value=None, quote=1):
+ """Convenience function to format and return a key=value pair.
+
+ This will quote the value if needed or if quote is true.
+ """
+ if value is not None and len(value) > 0:
+ if quote or tspecials.search(value):
+ value = value.replace('\\', '\\\\').replace('"', r'\"')
+ return '%s="%s"' % (param, value)
+ else:
+ return '%s=%s' % (param, value)
+ else:
+ return param
+
+
+class Headers:
+
+ """Manage a collection of HTTP response headers"""
+
+ def __init__(self,headers):
+ if type(headers) is not ListType:
+ raise TypeError("Headers must be a list of name/value tuples")
+ self._headers = headers
+
+ def __len__(self):
+ """Return the total number of headers, including duplicates."""
+ return len(self._headers)
+
+ def __setitem__(self, name, val):
+ """Set the value of a header."""
+ del self[name]
+ self._headers.append((name, val))
+
+ def __delitem__(self,name):
+ """Delete all occurrences of a header, if present.
+
+ Does *not* raise an exception if the header is missing.
+ """
+ name = name.lower()
+ self._headers[:] = [kv for kv in self._headers if kv[0].lower() != name]
+
+ def __getitem__(self,name):
+ """Get the first header value for 'name'
+
+ Return None if the header is missing instead of raising an exception.
+
+ Note that if the header appeared multiple times, the first exactly which
+ occurrence gets returned is undefined. Use getall() to get all
+ the values matching a header field name.
+ """
+ return self.get(name)
+
+ def has_key(self, name):
+ """Return true if the message contains the header."""
+ return self.get(name) is not None
+
+ __contains__ = has_key
+
+
+ def get_all(self, name):
+ """Return a list of all the values for the named field.
+
+ These will be sorted in the order they appeared in the original header
+ list or were added to this instance, and may contain duplicates. Any
+ fields deleted and re-inserted are always appended to the header list.
+ If no fields exist with the given name, returns an empty list.
+ """
+ name = name.lower()
+ return [kv[1] for kv in self._headers if kv[0].lower()==name]
+
+
+ def get(self,name,default=None):
+ """Get the first header value for 'name', or return 'default'"""
+ name = name.lower()
+ for k,v in self._headers:
+ if k.lower()==name:
+ return v
+ return default
+
+
+ def keys(self):
+ """Return a list of all the header field names.
+
+ These will be sorted in the order they appeared in the original header
+ list, or were added to this instance, and may contain duplicates.
+ Any fields deleted and re-inserted are always appended to the header
+ list.
+ """
+ return [k for k, v in self._headers]
+
+ def values(self):
+ """Return a list of all header values.
+
+ These will be sorted in the order they appeared in the original header
+ list, or were added to this instance, and may contain duplicates.
+ Any fields deleted and re-inserted are always appended to the header
+ list.
+ """
+ return [v for k, v in self._headers]
+
+ def items(self):
+ """Get all the header fields and values.
+
+ These will be sorted in the order they were in the original header
+ list, or were added to this instance, and may contain duplicates.
+ Any fields deleted and re-inserted are always appended to the header
+ list.
+ """
+ return self._headers[:]
+
+ def __repr__(self):
+ return "Headers(%r)" % self._headers
+
+ def __str__(self):
+ """str() returns the formatted headers, complete with end line,
+ suitable for direct HTTP transmission."""
+ return '\r\n'.join(["%s: %s" % kv for kv in self._headers]+['',''])
+
+ def setdefault(self,name,value):
+ """Return first matching header value for 'name', or 'value'
+
+ If there is no header named 'name', add a new header with name 'name'
+ and value 'value'."""
+ result = self.get(name)
+ if result is None:
+ self._headers.append((name,value))
+ return value
+ else:
+ return result
+
+ def add_header(self, _name, _value, **_params):
+ """Extended header setting.
+
+ _name is the header field to add. keyword arguments can be used to set
+ additional parameters for the header field, with underscores converted
+ to dashes. Normally the parameter will be added as key="value" unless
+ value is None, in which case only the key will be added.
+
+ Example:
+
+ h.add_header('content-disposition', 'attachment', filename='bud.gif')
+
+ Note that unlike the corresponding 'email.message' method, this does
+ *not* handle '(charset, language, value)' tuples: all values must be
+ strings or None.
+ """
+ parts = []
+ if _value is not None:
+ parts.append(_value)
+ for k, v in _params.items():
+ if v is None:
+ parts.append(k.replace('_', '-'))
+ else:
+ parts.append(_formatparam(k.replace('_', '-'), v))
+ self._headers.append((_name, "; ".join(parts)))
diff --git a/service/wsgiref/simple_server.py b/service/wsgiref/simple_server.py
new file mode 100644
index 0000000..35b98d1
--- /dev/null
+++ b/service/wsgiref/simple_server.py
@@ -0,0 +1,163 @@
+"""BaseHTTPServer that implements the Python WSGI protocol (PEP 333, rev 1.21)
+
+This is both an example of how WSGI can be implemented, and a basis for running
+simple web applications on a local machine, such as might be done when testing
+or debugging an application. It has not been reviewed for security issues,
+however, and we strongly recommend that you use a "real" web server for
+production use.
+
+For example usage, see the 'if __name__=="__main__"' block at the end of the
+module. See also the BaseHTTPServer module docs for other API information.
+"""
+
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+import urllib, sys
+from wsgiref.handlers import SimpleHandler
+
+__version__ = "0.1"
+__all__ = ['WSGIServer', 'WSGIRequestHandler', 'demo_app', 'make_server']
+
+
+server_version = "WSGIServer/" + __version__
+sys_version = "Python/" + sys.version.split()[0]
+software_version = server_version + ' ' + sys_version
+
+
+class ServerHandler(SimpleHandler):
+
+ server_software = software_version
+
+ def close(self):
+ try:
+ self.request_handler.log_request(
+ self.status.split(' ',1)[0], self.bytes_sent
+ )
+ finally:
+ SimpleHandler.close(self)
+
+
+
+class WSGIServer(HTTPServer):
+
+ """BaseHTTPServer that implements the Python WSGI protocol"""
+
+ application = None
+
+ def server_bind(self):
+ """Override server_bind to store the server name."""
+ HTTPServer.server_bind(self)
+ self.setup_environ()
+
+ def setup_environ(self):
+ # Set up base environment
+ env = self.base_environ = {}
+ env['SERVER_NAME'] = self.server_name
+ env['GATEWAY_INTERFACE'] = 'CGI/1.1'
+ env['SERVER_PORT'] = str(self.server_port)
+ env['REMOTE_HOST']=''
+ env['CONTENT_LENGTH']=''
+ env['SCRIPT_NAME'] = ''
+
+ def get_app(self):
+ return self.application
+
+ def set_app(self,application):
+ self.application = application
+
+
+
+class WSGIRequestHandler(BaseHTTPRequestHandler):
+
+ server_version = "WSGIServer/" + __version__
+
+ def get_environ(self):
+ env = self.server.base_environ.copy()
+ env['SERVER_PROTOCOL'] = self.request_version
+ env['REQUEST_METHOD'] = self.command
+ if '?' in self.path:
+ path,query = self.path.split('?',1)
+ else:
+ path,query = self.path,''
+
+ env['PATH_INFO'] = urllib.unquote(path)
+ env['QUERY_STRING'] = query
+
+ host = self.address_string()
+ if host != self.client_address[0]:
+ env['REMOTE_HOST'] = host
+ env['REMOTE_ADDR'] = self.client_address[0]
+
+ if self.headers.typeheader is None:
+ env['CONTENT_TYPE'] = self.headers.type
+ else:
+ env['CONTENT_TYPE'] = self.headers.typeheader
+
+ length = self.headers.getheader('content-length')
+ if length:
+ env['CONTENT_LENGTH'] = length
+
+ for h in self.headers.headers:
+ k,v = h.split(':',1)
+ k=k.replace('-','_').upper(); v=v.strip()
+ if k in env:
+ continue # skip content length, type,etc.
+ if 'HTTP_'+k in env:
+ env['HTTP_'+k] += ','+v # comma-separate multiple headers
+ else:
+ env['HTTP_'+k] = v
+ return env
+
+ def get_stderr(self):
+ return sys.stderr
+
+ def handle(self):
+ """Handle a single HTTP request"""
+
+ self.raw_requestline = self.rfile.readline(65537)
+ if len(self.raw_requestline) > 65536:
+ self.requestline = ''
+ self.request_version = ''
+ self.command = ''
+ self.send_error(414)
+ return
+
+ if not self.parse_request(): # An error code has been sent, just exit
+ return
+
+ handler = ServerHandler(
+ self.rfile, self.wfile, self.get_stderr(), self.get_environ()
+ )
+ handler.request_handler = self # backpointer for logging
+ handler.run(self.server.get_app())
+
+
+
+def demo_app(environ,start_response):
+ from StringIO import StringIO
+ stdout = StringIO()
+ print >>stdout, "Hello world!"
+ print >>stdout
+ h = environ.items(); h.sort()
+ for k,v in h:
+ print >>stdout, k,'=', repr(v)
+ start_response("200 OK", [('Content-Type','text/plain')])
+ return [stdout.getvalue()]
+
+
+def make_server(
+ host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler
+):
+ """Create a new WSGI server listening on `host` and `port` for `app`"""
+ server = server_class((host, port), handler_class)
+ server.set_app(app)
+ return server
+
+
+if __name__ == '__main__':
+ httpd = make_server('', 8000, demo_app)
+ sa = httpd.socket.getsockname()
+ print "Serving HTTP on", sa[0], "port", sa[1], "..."
+ import webbrowser
+ webbrowser.open('http://localhost:8000/xyz?abc')
+ httpd.handle_request() # serve one request, then exit
+ httpd.server_close()
diff --git a/service/wsgiref/util.py b/service/wsgiref/util.py
new file mode 100644
index 0000000..194b187
--- /dev/null
+++ b/service/wsgiref/util.py
@@ -0,0 +1,165 @@
+"""Miscellaneous WSGI-related Utilities"""
+
+import posixpath
+
+__all__ = [
+ 'FileWrapper', 'guess_scheme', 'application_uri', 'request_uri',
+ 'shift_path_info', 'setup_testing_defaults',
+]
+
+
+class FileWrapper:
+ """Wrapper to convert file-like objects to iterables"""
+
+ def __init__(self, filelike, blksize=8192):
+ self.filelike = filelike
+ self.blksize = blksize
+ if hasattr(filelike,'close'):
+ self.close = filelike.close
+
+ def __getitem__(self,key):
+ data = self.filelike.read(self.blksize)
+ if data:
+ return data
+ raise IndexError
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ data = self.filelike.read(self.blksize)
+ if data:
+ return data
+ raise StopIteration
+
+def guess_scheme(environ):
+ """Return a guess for whether 'wsgi.url_scheme' should be 'http' or 'https'
+ """
+ if environ.get("HTTPS") in ('yes','on','1'):
+ return 'https'
+ else:
+ return 'http'
+
+def application_uri(environ):
+ """Return the application's base URI (no PATH_INFO or QUERY_STRING)"""
+ url = environ['wsgi.url_scheme']+'://'
+ from urllib import quote
+
+ if environ.get('HTTP_HOST'):
+ url += environ['HTTP_HOST']
+ else:
+ url += environ['SERVER_NAME']
+
+ if environ['wsgi.url_scheme'] == 'https':
+ if environ['SERVER_PORT'] != '443':
+ url += ':' + environ['SERVER_PORT']
+ else:
+ if environ['SERVER_PORT'] != '80':
+ url += ':' + environ['SERVER_PORT']
+
+ url += quote(environ.get('SCRIPT_NAME') or '/')
+ return url
+
+def request_uri(environ, include_query=1):
+ """Return the full request URI, optionally including the query string"""
+ url = application_uri(environ)
+ from urllib import quote
+ path_info = quote(environ.get('PATH_INFO',''),safe='/;=,')
+ if not environ.get('SCRIPT_NAME'):
+ url += path_info[1:]
+ else:
+ url += path_info
+ if include_query and environ.get('QUERY_STRING'):
+ url += '?' + environ['QUERY_STRING']
+ return url
+
+def shift_path_info(environ):
+ """Shift a name from PATH_INFO to SCRIPT_NAME, returning it
+
+ If there are no remaining path segments in PATH_INFO, return None.
+ Note: 'environ' is modified in-place; use a copy if you need to keep
+ the original PATH_INFO or SCRIPT_NAME.
+
+ Note: when PATH_INFO is just a '/', this returns '' and appends a trailing
+ '/' to SCRIPT_NAME, even though empty path segments are normally ignored,
+ and SCRIPT_NAME doesn't normally end in a '/'. This is intentional
+ behavior, to ensure that an application can tell the difference between
+ '/x' and '/x/' when traversing to objects.
+ """
+ path_info = environ.get('PATH_INFO','')
+ if not path_info:
+ return None
+
+ path_parts = path_info.split('/')
+ path_parts[1:-1] = [p for p in path_parts[1:-1] if p and p != '.']
+ name = path_parts[1]
+ del path_parts[1]
+
+ script_name = environ.get('SCRIPT_NAME','')
+ script_name = posixpath.normpath(script_name+'/'+name)
+ if script_name.endswith('/'):
+ script_name = script_name[:-1]
+ if not name and not script_name.endswith('/'):
+ script_name += '/'
+
+ environ['SCRIPT_NAME'] = script_name
+ environ['PATH_INFO'] = '/'.join(path_parts)
+
+ # Special case: '/.' on PATH_INFO doesn't get stripped,
+ # because we don't strip the last element of PATH_INFO
+ # if there's only one path part left. Instead of fixing this
+ # above, we fix it here so that PATH_INFO gets normalized to
+ # an empty string in the environ.
+ if name=='.':
+ name = None
+ return name
+
+def setup_testing_defaults(environ):
+ """Update 'environ' with trivial defaults for testing purposes
+
+ This adds various parameters required for WSGI, including HTTP_HOST,
+ SERVER_NAME, SERVER_PORT, REQUEST_METHOD, SCRIPT_NAME, PATH_INFO,
+ and all of the wsgi.* variables. It only supplies default values,
+ and does not replace any existing settings for these variables.
+
+ This routine is intended to make it easier for unit tests of WSGI
+ servers and applications to set up dummy environments. It should *not*
+ be used by actual WSGI servers or applications, since the data is fake!
+ """
+
+ environ.setdefault('SERVER_NAME','127.0.0.1')
+ environ.setdefault('SERVER_PROTOCOL','HTTP/1.0')
+
+ environ.setdefault('HTTP_HOST',environ['SERVER_NAME'])
+ environ.setdefault('REQUEST_METHOD','GET')
+
+ if 'SCRIPT_NAME' not in environ and 'PATH_INFO' not in environ:
+ environ.setdefault('SCRIPT_NAME','')
+ environ.setdefault('PATH_INFO','/')
+
+ environ.setdefault('wsgi.version', (1,0))
+ environ.setdefault('wsgi.run_once', 0)
+ environ.setdefault('wsgi.multithread', 0)
+ environ.setdefault('wsgi.multiprocess', 0)
+
+ from StringIO import StringIO
+ environ.setdefault('wsgi.input', StringIO(""))
+ environ.setdefault('wsgi.errors', StringIO())
+ environ.setdefault('wsgi.url_scheme',guess_scheme(environ))
+
+ if environ['wsgi.url_scheme']=='http':
+ environ.setdefault('SERVER_PORT', '80')
+ elif environ['wsgi.url_scheme']=='https':
+ environ.setdefault('SERVER_PORT', '443')
+
+
+
+_hoppish = {
+ 'connection':1, 'keep-alive':1, 'proxy-authenticate':1,
+ 'proxy-authorization':1, 'te':1, 'trailers':1, 'transfer-encoding':1,
+ 'upgrade':1
+}.__contains__
+
+def is_hop_by_hop(header_name):
+ """Return true if 'header_name' is an HTTP/1.1 "Hop-by-Hop" header"""
+ return _hoppish(header_name.lower())
diff --git a/service/wsgiref/validate.py b/service/wsgiref/validate.py
new file mode 100644
index 0000000..c327812
--- /dev/null
+++ b/service/wsgiref/validate.py
@@ -0,0 +1,432 @@
+# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
+# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+# Also licenced under the Apache License, 2.0: http://opensource.org/licenses/apache2.0.php
+# Licensed to PSF under a Contributor Agreement
+"""
+Middleware to check for obedience to the WSGI specification.
+
+Some of the things this checks:
+
+* Signature of the application and start_response (including that
+ keyword arguments are not used).
+
+* Environment checks:
+
+ - Environment is a dictionary (and not a subclass).
+
+ - That all the required keys are in the environment: REQUEST_METHOD,
+ SERVER_NAME, SERVER_PORT, wsgi.version, wsgi.input, wsgi.errors,
+ wsgi.multithread, wsgi.multiprocess, wsgi.run_once
+
+ - That HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH are not in the
+ environment (these headers should appear as CONTENT_LENGTH and
+ CONTENT_TYPE).
+
+ - Warns if QUERY_STRING is missing, as the cgi module acts
+ unpredictably in that case.
+
+ - That CGI-style variables (that don't contain a .) have
+ (non-unicode) string values
+
+ - That wsgi.version is a tuple
+
+ - That wsgi.url_scheme is 'http' or 'https' (@@: is this too
+ restrictive?)
+
+ - Warns if the REQUEST_METHOD is not known (@@: probably too
+ restrictive).
+
+ - That SCRIPT_NAME and PATH_INFO are empty or start with /
+
+ - That at least one of SCRIPT_NAME or PATH_INFO are set.
+
+ - That CONTENT_LENGTH is a positive integer.
+
+ - That SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should
+ be '/').
+
+ - That wsgi.input has the methods read, readline, readlines, and
+ __iter__
+
+ - That wsgi.errors has the methods flush, write, writelines
+
+* The status is a string, contains a space, starts with an integer,
+ and that integer is in range (> 100).
+
+* That the headers is a list (not a subclass, not another kind of
+ sequence).
+
+* That the items of the headers are tuples of strings.
+
+* That there is no 'status' header (that is used in CGI, but not in
+ WSGI).
+
+* That the headers don't contain newlines or colons, end in _ or -, or
+ contain characters codes below 037.
+
+* That Content-Type is given if there is content (CGI often has a
+ default content type, but WSGI does not).
+
+* That no Content-Type is given when there is no content (@@: is this
+ too restrictive?)
+
+* That the exc_info argument to start_response is a tuple or None.
+
+* That all calls to the writer are with strings, and no other methods
+ on the writer are accessed.
+
+* That wsgi.input is used properly:
+
+ - .read() is called with zero or one argument
+
+ - That it returns a string
+
+ - That readline, readlines, and __iter__ return strings
+
+ - That .close() is not called
+
+ - No other methods are provided
+
+* That wsgi.errors is used properly:
+
+ - .write() and .writelines() is called with a string
+
+ - That .close() is not called, and no other methods are provided.
+
+* The response iterator:
+
+ - That it is not a string (it should be a list of a single string; a
+ string will work, but perform horribly).
+
+ - That .next() returns a string
+
+ - That the iterator is not iterated over until start_response has
+ been called (that can signal either a server or application
+ error).
+
+ - That .close() is called (doesn't raise exception, only prints to
+ sys.stderr, because we only know it isn't called when the object
+ is garbage collected).
+"""
+__all__ = ['validator']
+
+
+import re
+import sys
+from types import DictType, StringType, TupleType, ListType
+import warnings
+
+header_re = re.compile(r'^[a-zA-Z][a-zA-Z0-9\-_]*$')
+bad_header_value_re = re.compile(r'[\000-\037]')
+
+class WSGIWarning(Warning):
+ """
+ Raised in response to WSGI-spec-related warnings
+ """
+
+def assert_(cond, *args):
+ if not cond:
+ raise AssertionError(*args)
+
+def validator(application):
+
+ """
+ When applied between a WSGI server and a WSGI application, this
+ middleware will check for WSGI compliancy on a number of levels.
+ This middleware does not modify the request or response in any
+ way, but will raise an AssertionError if anything seems off
+ (except for a failure to close the application iterator, which
+ will be printed to stderr -- there's no way to raise an exception
+ at that point).
+ """
+
+ def lint_app(*args, **kw):
+ assert_(len(args) == 2, "Two arguments required")
+ assert_(not kw, "No keyword arguments allowed")
+ environ, start_response = args
+
+ check_environ(environ)
+
+ # We use this to check if the application returns without
+ # calling start_response:
+ start_response_started = []
+
+ def start_response_wrapper(*args, **kw):
+ assert_(len(args) == 2 or len(args) == 3, (
+ "Invalid number of arguments: %s" % (args,)))
+ assert_(not kw, "No keyword arguments allowed")
+ status = args[0]
+ headers = args[1]
+ if len(args) == 3:
+ exc_info = args[2]
+ else:
+ exc_info = None
+
+ check_status(status)
+ check_headers(headers)
+ check_content_type(status, headers)
+ check_exc_info(exc_info)
+
+ start_response_started.append(None)
+ return WriteWrapper(start_response(*args))
+
+ environ['wsgi.input'] = InputWrapper(environ['wsgi.input'])
+ environ['wsgi.errors'] = ErrorWrapper(environ['wsgi.errors'])
+
+ iterator = application(environ, start_response_wrapper)
+ assert_(iterator is not None and iterator != False,
+ "The application must return an iterator, if only an empty list")
+
+ check_iterator(iterator)
+
+ return IteratorWrapper(iterator, start_response_started)
+
+ return lint_app
+
+class InputWrapper:
+
+ def __init__(self, wsgi_input):
+ self.input = wsgi_input
+
+ def read(self, *args):
+ assert_(len(args) <= 1)
+ v = self.input.read(*args)
+ assert_(type(v) is type(""))
+ return v
+
+ def readline(self):
+ v = self.input.readline()
+ assert_(type(v) is type(""))
+ return v
+
+ def readlines(self, *args):
+ assert_(len(args) <= 1)
+ lines = self.input.readlines(*args)
+ assert_(type(lines) is type([]))
+ for line in lines:
+ assert_(type(line) is type(""))
+ return lines
+
+ def __iter__(self):
+ while 1:
+ line = self.readline()
+ if not line:
+ return
+ yield line
+
+ def close(self):
+ assert_(0, "input.close() must not be called")
+
+class ErrorWrapper:
+
+ def __init__(self, wsgi_errors):
+ self.errors = wsgi_errors
+
+ def write(self, s):
+ assert_(type(s) is type(""))
+ self.errors.write(s)
+
+ def flush(self):
+ self.errors.flush()
+
+ def writelines(self, seq):
+ for line in seq:
+ self.write(line)
+
+ def close(self):
+ assert_(0, "errors.close() must not be called")
+
+class WriteWrapper:
+
+ def __init__(self, wsgi_writer):
+ self.writer = wsgi_writer
+
+ def __call__(self, s):
+ assert_(type(s) is type(""))
+ self.writer(s)
+
+class PartialIteratorWrapper:
+
+ def __init__(self, wsgi_iterator):
+ self.iterator = wsgi_iterator
+
+ def __iter__(self):
+ # We want to make sure __iter__ is called
+ return IteratorWrapper(self.iterator, None)
+
+class IteratorWrapper:
+
+ def __init__(self, wsgi_iterator, check_start_response):
+ self.original_iterator = wsgi_iterator
+ self.iterator = iter(wsgi_iterator)
+ self.closed = False
+ self.check_start_response = check_start_response
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ assert_(not self.closed,
+ "Iterator read after closed")
+ v = self.iterator.next()
+ if self.check_start_response is not None:
+ assert_(self.check_start_response,
+ "The application returns and we started iterating over its body, but start_response has not yet been called")
+ self.check_start_response = None
+ return v
+
+ def close(self):
+ self.closed = True
+ if hasattr(self.original_iterator, 'close'):
+ self.original_iterator.close()
+
+ def __del__(self):
+ if not self.closed:
+ sys.stderr.write(
+ "Iterator garbage collected without being closed")
+ assert_(self.closed,
+ "Iterator garbage collected without being closed")
+
+def check_environ(environ):
+ assert_(type(environ) is DictType,
+ "Environment is not of the right type: %r (environment: %r)"
+ % (type(environ), environ))
+
+ for key in ['REQUEST_METHOD', 'SERVER_NAME', 'SERVER_PORT',
+ 'wsgi.version', 'wsgi.input', 'wsgi.errors',
+ 'wsgi.multithread', 'wsgi.multiprocess',
+ 'wsgi.run_once']:
+ assert_(key in environ,
+ "Environment missing required key: %r" % (key,))
+
+ for key in ['HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH']:
+ assert_(key not in environ,
+ "Environment should not have the key: %s "
+ "(use %s instead)" % (key, key[5:]))
+
+ if 'QUERY_STRING' not in environ:
+ warnings.warn(
+ 'QUERY_STRING is not in the WSGI environment; the cgi '
+ 'module will use sys.argv when this variable is missing, '
+ 'so application errors are more likely',
+ WSGIWarning)
+
+ for key in environ.keys():
+ if '.' in key:
+ # Extension, we don't care about its type
+ continue
+ assert_(type(environ[key]) is StringType,
+ "Environmental variable %s is not a string: %r (value: %r)"
+ % (key, type(environ[key]), environ[key]))
+
+ assert_(type(environ['wsgi.version']) is TupleType,
+ "wsgi.version should be a tuple (%r)" % (environ['wsgi.version'],))
+ assert_(environ['wsgi.url_scheme'] in ('http', 'https'),
+ "wsgi.url_scheme unknown: %r" % environ['wsgi.url_scheme'])
+
+ check_input(environ['wsgi.input'])
+ check_errors(environ['wsgi.errors'])
+
+ # @@: these need filling out:
+ if environ['REQUEST_METHOD'] not in (
+ 'GET', 'HEAD', 'POST', 'OPTIONS', 'PATCH', 'PUT', 'DELETE', 'TRACE'):
+ warnings.warn(
+ "Unknown REQUEST_METHOD: %r" % environ['REQUEST_METHOD'],
+ WSGIWarning)
+
+ assert_(not environ.get('SCRIPT_NAME')
+ or environ['SCRIPT_NAME'].startswith('/'),
+ "SCRIPT_NAME doesn't start with /: %r" % environ['SCRIPT_NAME'])
+ assert_(not environ.get('PATH_INFO')
+ or environ['PATH_INFO'].startswith('/'),
+ "PATH_INFO doesn't start with /: %r" % environ['PATH_INFO'])
+ if environ.get('CONTENT_LENGTH'):
+ assert_(int(environ['CONTENT_LENGTH']) >= 0,
+ "Invalid CONTENT_LENGTH: %r" % environ['CONTENT_LENGTH'])
+
+ if not environ.get('SCRIPT_NAME'):
+ assert_('PATH_INFO' in environ,
+ "One of SCRIPT_NAME or PATH_INFO are required (PATH_INFO "
+ "should at least be '/' if SCRIPT_NAME is empty)")
+ assert_(environ.get('SCRIPT_NAME') != '/',
+ "SCRIPT_NAME cannot be '/'; it should instead be '', and "
+ "PATH_INFO should be '/'")
+
+def check_input(wsgi_input):
+ for attr in ['read', 'readline', 'readlines', '__iter__']:
+ assert_(hasattr(wsgi_input, attr),
+ "wsgi.input (%r) doesn't have the attribute %s"
+ % (wsgi_input, attr))
+
+def check_errors(wsgi_errors):
+ for attr in ['flush', 'write', 'writelines']:
+ assert_(hasattr(wsgi_errors, attr),
+ "wsgi.errors (%r) doesn't have the attribute %s"
+ % (wsgi_errors, attr))
+
+def check_status(status):
+ assert_(type(status) is StringType,
+ "Status must be a string (not %r)" % status)
+ # Implicitly check that we can turn it into an integer:
+ status_code = status.split(None, 1)[0]
+ assert_(len(status_code) == 3,
+ "Status codes must be three characters: %r" % status_code)
+ status_int = int(status_code)
+ assert_(status_int >= 100, "Status code is invalid: %r" % status_int)
+ if len(status) < 4 or status[3] != ' ':
+ warnings.warn(
+ "The status string (%r) should be a three-digit integer "
+ "followed by a single space and a status explanation"
+ % status, WSGIWarning)
+
+def check_headers(headers):
+ assert_(type(headers) is ListType,
+ "Headers (%r) must be of type list: %r"
+ % (headers, type(headers)))
+ header_names = {}
+ for item in headers:
+ assert_(type(item) is TupleType,
+ "Individual headers (%r) must be of type tuple: %r"
+ % (item, type(item)))
+ assert_(len(item) == 2)
+ name, value = item
+ assert_(name.lower() != 'status',
+ "The Status header cannot be used; it conflicts with CGI "
+ "script, and HTTP status is not given through headers "
+ "(value: %r)." % value)
+ header_names[name.lower()] = None
+ assert_('\n' not in name and ':' not in name,
+ "Header names may not contain ':' or '\\n': %r" % name)
+ assert_(header_re.search(name), "Bad header name: %r" % name)
+ assert_(not name.endswith('-') and not name.endswith('_'),
+ "Names may not end in '-' or '_': %r" % name)
+ if bad_header_value_re.search(value):
+ assert_(0, "Bad header value: %r (bad char: %r)"
+ % (value, bad_header_value_re.search(value).group(0)))
+
+def check_content_type(status, headers):
+ code = int(status.split(None, 1)[0])
+ # @@: need one more person to verify this interpretation of RFC 2616
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ NO_MESSAGE_BODY = (204, 304)
+ for name, value in headers:
+ if name.lower() == 'content-type':
+ if code not in NO_MESSAGE_BODY:
+ return
+ assert_(0, ("Content-Type header found in a %s response, "
+ "which must not return content.") % code)
+ if code not in NO_MESSAGE_BODY:
+ assert_(0, "No Content-Type header found in headers (%s)" % headers)
+
+def check_exc_info(exc_info):
+ assert_(exc_info is None or type(exc_info) is type(()),
+ "exc_info (%r) is not a tuple: %r" % (exc_info, type(exc_info)))
+ # More exc_info checks?
+
+def check_iterator(iterator):
+ # Technically a string is legal, which is why it's a really bad
+ # idea, because it may cause the response to be returned
+ # character-by-character
+ assert_(not isinstance(iterator, str),
+ "You should not return a string as your application iterator, "
+ "instead return a single-item list containing that string.")
diff --git a/service2/__init__.py b/service2/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/service2/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/service2/app.py b/service2/app.py
new file mode 100644
index 0000000..7bd814a
--- /dev/null
+++ b/service2/app.py
@@ -0,0 +1,14 @@
+"""
+Application stub
+"""
+
+
+def initialize():
+ # perform heavy stuff here
+ return True
+
+
+def do_stuff():
+ # do whatever you need to do
+ response = "This is response from Python backend"
+ return response
\ No newline at end of file
diff --git a/service2/flask/__init__.py b/service2/flask/__init__.py
new file mode 100644
index 0000000..a9a873f
--- /dev/null
+++ b/service2/flask/__init__.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""
+ flask
+ ~~~~~
+
+ A microframework based on Werkzeug. It's extensively documented
+ and follows best practice patterns.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+__version__ = '0.12.2'
+
+# utilities we import from Werkzeug and Jinja2 that are unused
+# in the module but are exported as public interface.
+from werkzeug.exceptions import abort
+from werkzeug.utils import redirect
+from jinja2 import Markup, escape
+
+from .app import Flask, Request, Response
+from .config import Config
+from .helpers import url_for, flash, send_file, send_from_directory, \
+ get_flashed_messages, get_template_attribute, make_response, safe_join, \
+ stream_with_context
+from .globals import current_app, g, request, session, _request_ctx_stack, \
+ _app_ctx_stack
+from .ctx import has_request_context, has_app_context, \
+ after_this_request, copy_current_request_context
+from .blueprints import Blueprint
+from .templating import render_template, render_template_string
+
+# the signals
+from .signals import signals_available, template_rendered, request_started, \
+ request_finished, got_request_exception, request_tearing_down, \
+ appcontext_tearing_down, appcontext_pushed, \
+ appcontext_popped, message_flashed, before_render_template
+
+# We're not exposing the actual json module but a convenient wrapper around
+# it.
+from . import json
+
+# This was the only thing that Flask used to export at one point and it had
+# a more generic name.
+jsonify = json.jsonify
+
+# backwards compat, goes away in 1.0
+from .sessions import SecureCookieSession as Session
+json_available = True
diff --git a/service2/flask/__main__.py b/service2/flask/__main__.py
new file mode 100644
index 0000000..cbefccd
--- /dev/null
+++ b/service2/flask/__main__.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.__main__
+ ~~~~~~~~~~~~~~
+
+ Alias for flask.run for the command line.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+
+if __name__ == '__main__':
+ from .cli import main
+ main(as_module=True)
diff --git a/service2/flask/_compat.py b/service2/flask/_compat.py
new file mode 100644
index 0000000..071628f
--- /dev/null
+++ b/service2/flask/_compat.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+"""
+ flask._compat
+ ~~~~~~~~~~~~~
+
+ Some py2/py3 compatibility support based on a stripped down
+ version of six so we don't have to depend on a specific version
+ of it.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+
+PY2 = sys.version_info[0] == 2
+_identity = lambda x: x
+
+
+if not PY2:
+ text_type = str
+ string_types = (str,)
+ integer_types = (int,)
+
+ iterkeys = lambda d: iter(d.keys())
+ itervalues = lambda d: iter(d.values())
+ iteritems = lambda d: iter(d.items())
+
+ from io import StringIO
+
+ def reraise(tp, value, tb=None):
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+
+ implements_to_string = _identity
+
+else:
+ text_type = unicode
+ string_types = (str, unicode)
+ integer_types = (int, long)
+
+ iterkeys = lambda d: d.iterkeys()
+ itervalues = lambda d: d.itervalues()
+ iteritems = lambda d: d.iteritems()
+
+ from cStringIO import StringIO
+
+ exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
+
+ def implements_to_string(cls):
+ cls.__unicode__ = cls.__str__
+ cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
+ return cls
+
+
+def with_metaclass(meta, *bases):
+ """Create a base class with a metaclass."""
+ # This requires a bit of explanation: the basic idea is to make a
+ # dummy metaclass for one level of class instantiation that replaces
+ # itself with the actual metaclass.
+ class metaclass(type):
+ def __new__(cls, name, this_bases, d):
+ return meta(name, bases, d)
+ return type.__new__(metaclass, 'temporary_class', (), {})
+
+
+# Certain versions of pypy have a bug where clearing the exception stack
+# breaks the __exit__ function in a very peculiar way. The second level of
+# exception blocks is necessary because pypy seems to forget to check if an
+# exception happened until the next bytecode instruction?
+#
+# Relevant PyPy bugfix commit:
+# https://bitbucket.org/pypy/pypy/commits/77ecf91c635a287e88e60d8ddb0f4e9df4003301
+# According to ronan on #pypy IRC, it is released in PyPy2 2.3 and later
+# versions.
+#
+# Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug.
+BROKEN_PYPY_CTXMGR_EXIT = False
+if hasattr(sys, 'pypy_version_info'):
+ class _Mgr(object):
+ def __enter__(self):
+ return self
+ def __exit__(self, *args):
+ if hasattr(sys, 'exc_clear'):
+ # Python 3 (PyPy3) doesn't have exc_clear
+ sys.exc_clear()
+ try:
+ try:
+ with _Mgr():
+ raise AssertionError()
+ except:
+ raise
+ except TypeError:
+ BROKEN_PYPY_CTXMGR_EXIT = True
+ except AssertionError:
+ pass
diff --git a/service2/flask/app.py b/service2/flask/app.py
new file mode 100644
index 0000000..1404e17
--- /dev/null
+++ b/service2/flask/app.py
@@ -0,0 +1,2003 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.app
+ ~~~~~~~~~
+
+ This module implements the central WSGI application object.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+from threading import Lock
+from datetime import timedelta
+from itertools import chain
+from functools import update_wrapper
+
+from werkzeug.datastructures import ImmutableDict
+from werkzeug.routing import Map, Rule, RequestRedirect, BuildError
+from werkzeug.exceptions import HTTPException, InternalServerError, \
+ MethodNotAllowed, BadRequest, default_exceptions
+
+from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
+ locked_cached_property, _endpoint_from_view_func, find_package, \
+ get_debug_flag
+from . import json, cli
+from .wrappers import Request, Response
+from .config import ConfigAttribute, Config
+from .ctx import RequestContext, AppContext, _AppCtxGlobals
+from .globals import _request_ctx_stack, request, session, g
+from .sessions import SecureCookieSessionInterface
+from .templating import DispatchingJinjaLoader, Environment, \
+ _default_template_ctx_processor
+from .signals import request_started, request_finished, got_request_exception, \
+ request_tearing_down, appcontext_tearing_down
+from ._compat import reraise, string_types, text_type, integer_types
+
+# a lock used for logger initialization
+_logger_lock = Lock()
+
+# a singleton sentinel value for parameter defaults
+_sentinel = object()
+
+
+def _make_timedelta(value):
+ if not isinstance(value, timedelta):
+ return timedelta(seconds=value)
+ return value
+
+
+def setupmethod(f):
+ """Wraps a method so that it performs a check in debug mode if the
+ first request was already handled.
+ """
+ def wrapper_func(self, *args, **kwargs):
+ if self.debug and self._got_first_request:
+ raise AssertionError('A setup function was called after the '
+ 'first request was handled. This usually indicates a bug '
+ 'in the application where a module was not imported '
+ 'and decorators or other functionality was called too late.\n'
+ 'To fix this make sure to import all your view modules, '
+ 'database models and everything related at a central place '
+ 'before the application starts serving requests.')
+ return f(self, *args, **kwargs)
+ return update_wrapper(wrapper_func, f)
+
+
+class Flask(_PackageBoundObject):
+ """The flask object implements a WSGI application and acts as the central
+ object. It is passed the name of the module or package of the
+ application. Once it is created it will act as a central registry for
+ the view functions, the URL rules, template configuration and much more.
+
+ The name of the package is used to resolve resources from inside the
+ package or the folder the module is contained in depending on if the
+ package parameter resolves to an actual python package (a folder with
+ an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file).
+
+ For more information about resource loading, see :func:`open_resource`.
+
+ Usually you create a :class:`Flask` instance in your main module or
+ in the :file:`__init__.py` file of your package like this::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ .. admonition:: About the First Parameter
+
+ The idea of the first parameter is to give Flask an idea of what
+ belongs to your application. This name is used to find resources
+ on the filesystem, can be used by extensions to improve debugging
+ information and a lot more.
+
+ So it's important what you provide there. If you are using a single
+ module, `__name__` is always the correct value. If you however are
+ using a package, it's usually recommended to hardcode the name of
+ your package there.
+
+ For example if your application is defined in :file:`yourapplication/app.py`
+ you should create it with one of the two versions below::
+
+ app = Flask('yourapplication')
+ app = Flask(__name__.split('.')[0])
+
+ Why is that? The application will work even with `__name__`, thanks
+ to how resources are looked up. However it will make debugging more
+ painful. Certain extensions can make assumptions based on the
+ import name of your application. For example the Flask-SQLAlchemy
+ extension will look for the code in your application that triggered
+ an SQL query in debug mode. If the import name is not properly set
+ up, that debugging information is lost. (For example it would only
+ pick up SQL queries in `yourapplication.app` and not
+ `yourapplication.views.frontend`)
+
+ .. versionadded:: 0.7
+ The `static_url_path`, `static_folder`, and `template_folder`
+ parameters were added.
+
+ .. versionadded:: 0.8
+ The `instance_path` and `instance_relative_config` parameters were
+ added.
+
+ .. versionadded:: 0.11
+ The `root_path` parameter was added.
+
+ :param import_name: the name of the application package
+ :param static_url_path: can be used to specify a different path for the
+ static files on the web. Defaults to the name
+ of the `static_folder` folder.
+ :param static_folder: the folder with static files that should be served
+ at `static_url_path`. Defaults to the ``'static'``
+ folder in the root path of the application.
+ :param template_folder: the folder that contains the templates that should
+ be used by the application. Defaults to
+ ``'templates'`` folder in the root path of the
+ application.
+ :param instance_path: An alternative instance path for the application.
+ By default the folder ``'instance'`` next to the
+ package or module is assumed to be the instance
+ path.
+ :param instance_relative_config: if set to ``True`` relative filenames
+ for loading the config are assumed to
+ be relative to the instance path instead
+ of the application root.
+ :param root_path: Flask by default will automatically calculate the path
+ to the root of the application. In certain situations
+ this cannot be achieved (for instance if the package
+ is a Python 3 namespace package) and needs to be
+ manually defined.
+ """
+
+ #: The class that is used for request objects. See :class:`~flask.Request`
+ #: for more information.
+ request_class = Request
+
+ #: The class that is used for response objects. See
+ #: :class:`~flask.Response` for more information.
+ response_class = Response
+
+ #: The class that is used for the Jinja environment.
+ #:
+ #: .. versionadded:: 0.11
+ jinja_environment = Environment
+
+ #: The class that is used for the :data:`~flask.g` instance.
+ #:
+ #: Example use cases for a custom class:
+ #:
+ #: 1. Store arbitrary attributes on flask.g.
+ #: 2. Add a property for lazy per-request database connectors.
+ #: 3. Return None instead of AttributeError on unexpected attributes.
+ #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g.
+ #:
+ #: In Flask 0.9 this property was called `request_globals_class` but it
+ #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the
+ #: flask.g object is now application context scoped.
+ #:
+ #: .. versionadded:: 0.10
+ app_ctx_globals_class = _AppCtxGlobals
+
+ # Backwards compatibility support
+ def _get_request_globals_class(self):
+ return self.app_ctx_globals_class
+ def _set_request_globals_class(self, value):
+ from warnings import warn
+ warn(DeprecationWarning('request_globals_class attribute is now '
+ 'called app_ctx_globals_class'))
+ self.app_ctx_globals_class = value
+ request_globals_class = property(_get_request_globals_class,
+ _set_request_globals_class)
+ del _get_request_globals_class, _set_request_globals_class
+
+ #: The class that is used for the ``config`` attribute of this app.
+ #: Defaults to :class:`~flask.Config`.
+ #:
+ #: Example use cases for a custom class:
+ #:
+ #: 1. Default values for certain config options.
+ #: 2. Access to config values through attributes in addition to keys.
+ #:
+ #: .. versionadded:: 0.11
+ config_class = Config
+
+ #: The debug flag. Set this to ``True`` to enable debugging of the
+ #: application. In debug mode the debugger will kick in when an unhandled
+ #: exception occurs and the integrated server will automatically reload
+ #: the application if changes in the code are detected.
+ #:
+ #: This attribute can also be configured from the config with the ``DEBUG``
+ #: configuration key. Defaults to ``False``.
+ debug = ConfigAttribute('DEBUG')
+
+ #: The testing flag. Set this to ``True`` to enable the test mode of
+ #: Flask extensions (and in the future probably also Flask itself).
+ #: For example this might activate unittest helpers that have an
+ #: additional runtime cost which should not be enabled by default.
+ #:
+ #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the
+ #: default it's implicitly enabled.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``TESTING`` configuration key. Defaults to ``False``.
+ testing = ConfigAttribute('TESTING')
+
+ #: If a secret key is set, cryptographic components can use this to
+ #: sign cookies and other things. Set this to a complex random value
+ #: when you want to use the secure cookie for instance.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``SECRET_KEY`` configuration key. Defaults to ``None``.
+ secret_key = ConfigAttribute('SECRET_KEY')
+
+ #: The secure cookie uses this for the name of the session cookie.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``SESSION_COOKIE_NAME`` configuration key. Defaults to ``'session'``
+ session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
+
+ #: A :class:`~datetime.timedelta` which is used to set the expiration
+ #: date of a permanent session. The default is 31 days which makes a
+ #: permanent session survive for roughly one month.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to
+ #: ``timedelta(days=31)``
+ permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME',
+ get_converter=_make_timedelta)
+
+ #: A :class:`~datetime.timedelta` which is used as default cache_timeout
+ #: for the :func:`send_file` functions. The default is 12 hours.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``SEND_FILE_MAX_AGE_DEFAULT`` configuration key. This configuration
+ #: variable can also be set with an integer value used as seconds.
+ #: Defaults to ``timedelta(hours=12)``
+ send_file_max_age_default = ConfigAttribute('SEND_FILE_MAX_AGE_DEFAULT',
+ get_converter=_make_timedelta)
+
+ #: Enable this if you want to use the X-Sendfile feature. Keep in
+ #: mind that the server has to support this. This only affects files
+ #: sent with the :func:`send_file` method.
+ #:
+ #: .. versionadded:: 0.2
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``.
+ use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
+
+ #: The name of the logger to use. By default the logger name is the
+ #: package name passed to the constructor.
+ #:
+ #: .. versionadded:: 0.4
+ logger_name = ConfigAttribute('LOGGER_NAME')
+
+ #: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`.
+ #:
+ #: .. versionadded:: 0.10
+ json_encoder = json.JSONEncoder
+
+ #: The JSON decoder class to use. Defaults to :class:`~flask.json.JSONDecoder`.
+ #:
+ #: .. versionadded:: 0.10
+ json_decoder = json.JSONDecoder
+
+ #: Options that are passed directly to the Jinja2 environment.
+ jinja_options = ImmutableDict(
+ extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
+ )
+
+ #: Default configuration parameters.
+ default_config = ImmutableDict({
+ 'DEBUG': get_debug_flag(default=False),
+ 'TESTING': False,
+ 'PROPAGATE_EXCEPTIONS': None,
+ 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
+ 'SECRET_KEY': None,
+ 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
+ 'USE_X_SENDFILE': False,
+ 'LOGGER_NAME': None,
+ 'LOGGER_HANDLER_POLICY': 'always',
+ 'SERVER_NAME': None,
+ 'APPLICATION_ROOT': None,
+ 'SESSION_COOKIE_NAME': 'session',
+ 'SESSION_COOKIE_DOMAIN': None,
+ 'SESSION_COOKIE_PATH': None,
+ 'SESSION_COOKIE_HTTPONLY': True,
+ 'SESSION_COOKIE_SECURE': False,
+ 'SESSION_REFRESH_EACH_REQUEST': True,
+ 'MAX_CONTENT_LENGTH': None,
+ 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
+ 'TRAP_BAD_REQUEST_ERRORS': False,
+ 'TRAP_HTTP_EXCEPTIONS': False,
+ 'EXPLAIN_TEMPLATE_LOADING': False,
+ 'PREFERRED_URL_SCHEME': 'http',
+ 'JSON_AS_ASCII': True,
+ 'JSON_SORT_KEYS': True,
+ 'JSONIFY_PRETTYPRINT_REGULAR': True,
+ 'JSONIFY_MIMETYPE': 'application/json',
+ 'TEMPLATES_AUTO_RELOAD': None,
+ })
+
+ #: The rule object to use for URL rules created. This is used by
+ #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`.
+ #:
+ #: .. versionadded:: 0.7
+ url_rule_class = Rule
+
+ #: the test client that is used with when `test_client` is used.
+ #:
+ #: .. versionadded:: 0.7
+ test_client_class = None
+
+ #: the session interface to use. By default an instance of
+ #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
+ #:
+ #: .. versionadded:: 0.8
+ session_interface = SecureCookieSessionInterface()
+
+ def __init__(self, import_name, static_path=None, static_url_path=None,
+ static_folder='static', template_folder='templates',
+ instance_path=None, instance_relative_config=False,
+ root_path=None):
+ _PackageBoundObject.__init__(self, import_name,
+ template_folder=template_folder,
+ root_path=root_path)
+ if static_path is not None:
+ from warnings import warn
+ warn(DeprecationWarning('static_path is now called '
+ 'static_url_path'), stacklevel=2)
+ static_url_path = static_path
+
+ if static_url_path is not None:
+ self.static_url_path = static_url_path
+ if static_folder is not None:
+ self.static_folder = static_folder
+ if instance_path is None:
+ instance_path = self.auto_find_instance_path()
+ elif not os.path.isabs(instance_path):
+ raise ValueError('If an instance path is provided it must be '
+ 'absolute. A relative path was given instead.')
+
+ #: Holds the path to the instance folder.
+ #:
+ #: .. versionadded:: 0.8
+ self.instance_path = instance_path
+
+ #: The configuration dictionary as :class:`Config`. This behaves
+ #: exactly like a regular dictionary but supports additional methods
+ #: to load a config from files.
+ self.config = self.make_config(instance_relative_config)
+
+ # Prepare the deferred setup of the logger.
+ self._logger = None
+ self.logger_name = self.import_name
+
+ #: A dictionary of all view functions registered. The keys will
+ #: be function names which are also used to generate URLs and
+ #: the values are the function objects themselves.
+ #: To register a view function, use the :meth:`route` decorator.
+ self.view_functions = {}
+
+ # support for the now deprecated `error_handlers` attribute. The
+ # :attr:`error_handler_spec` shall be used now.
+ self._error_handlers = {}
+
+ #: A dictionary of all registered error handlers. The key is ``None``
+ #: for error handlers active on the application, otherwise the key is
+ #: the name of the blueprint. Each key points to another dictionary
+ #: where the key is the status code of the http exception. The
+ #: special key ``None`` points to a list of tuples where the first item
+ #: is the class for the instance check and the second the error handler
+ #: function.
+ #:
+ #: To register a error handler, use the :meth:`errorhandler`
+ #: decorator.
+ self.error_handler_spec = {None: self._error_handlers}
+
+ #: A list of functions that are called when :meth:`url_for` raises a
+ #: :exc:`~werkzeug.routing.BuildError`. Each function registered here
+ #: is called with `error`, `endpoint` and `values`. If a function
+ #: returns ``None`` or raises a :exc:`BuildError` the next function is
+ #: tried.
+ #:
+ #: .. versionadded:: 0.9
+ self.url_build_error_handlers = []
+
+ #: A dictionary with lists of functions that should be called at the
+ #: beginning of the request. The key of the dictionary is the name of
+ #: the blueprint this function is active for, ``None`` for all requests.
+ #: This can for example be used to open database connections or
+ #: getting hold of the currently logged in user. To register a
+ #: function here, use the :meth:`before_request` decorator.
+ self.before_request_funcs = {}
+
+ #: A lists of functions that should be called at the beginning of the
+ #: first request to this instance. To register a function here, use
+ #: the :meth:`before_first_request` decorator.
+ #:
+ #: .. versionadded:: 0.8
+ self.before_first_request_funcs = []
+
+ #: A dictionary with lists of functions that should be called after
+ #: each request. The key of the dictionary is the name of the blueprint
+ #: this function is active for, ``None`` for all requests. This can for
+ #: example be used to close database connections. To register a function
+ #: here, use the :meth:`after_request` decorator.
+ self.after_request_funcs = {}
+
+ #: A dictionary with lists of functions that are called after
+ #: each request, even if an exception has occurred. The key of the
+ #: dictionary is the name of the blueprint this function is active for,
+ #: ``None`` for all requests. These functions are not allowed to modify
+ #: the request, and their return values are ignored. If an exception
+ #: occurred while processing the request, it gets passed to each
+ #: teardown_request function. To register a function here, use the
+ #: :meth:`teardown_request` decorator.
+ #:
+ #: .. versionadded:: 0.7
+ self.teardown_request_funcs = {}
+
+ #: A list of functions that are called when the application context
+ #: is destroyed. Since the application context is also torn down
+ #: if the request ends this is the place to store code that disconnects
+ #: from databases.
+ #:
+ #: .. versionadded:: 0.9
+ self.teardown_appcontext_funcs = []
+
+ #: A dictionary with lists of functions that can be used as URL
+ #: value processor functions. Whenever a URL is built these functions
+ #: are called to modify the dictionary of values in place. The key
+ #: ``None`` here is used for application wide
+ #: callbacks, otherwise the key is the name of the blueprint.
+ #: Each of these functions has the chance to modify the dictionary
+ #:
+ #: .. versionadded:: 0.7
+ self.url_value_preprocessors = {}
+
+ #: A dictionary with lists of functions that can be used as URL value
+ #: preprocessors. The key ``None`` here is used for application wide
+ #: callbacks, otherwise the key is the name of the blueprint.
+ #: Each of these functions has the chance to modify the dictionary
+ #: of URL values before they are used as the keyword arguments of the
+ #: view function. For each function registered this one should also
+ #: provide a :meth:`url_defaults` function that adds the parameters
+ #: automatically again that were removed that way.
+ #:
+ #: .. versionadded:: 0.7
+ self.url_default_functions = {}
+
+ #: A dictionary with list of functions that are called without argument
+ #: to populate the template context. The key of the dictionary is the
+ #: name of the blueprint this function is active for, ``None`` for all
+ #: requests. Each returns a dictionary that the template context is
+ #: updated with. To register a function here, use the
+ #: :meth:`context_processor` decorator.
+ self.template_context_processors = {
+ None: [_default_template_ctx_processor]
+ }
+
+ #: A list of shell context processor functions that should be run
+ #: when a shell context is created.
+ #:
+ #: .. versionadded:: 0.11
+ self.shell_context_processors = []
+
+ #: all the attached blueprints in a dictionary by name. Blueprints
+ #: can be attached multiple times so this dictionary does not tell
+ #: you how often they got attached.
+ #:
+ #: .. versionadded:: 0.7
+ self.blueprints = {}
+ self._blueprint_order = []
+
+ #: a place where extensions can store application specific state. For
+ #: example this is where an extension could store database engines and
+ #: similar things. For backwards compatibility extensions should register
+ #: themselves like this::
+ #:
+ #: if not hasattr(app, 'extensions'):
+ #: app.extensions = {}
+ #: app.extensions['extensionname'] = SomeObject()
+ #:
+ #: The key must match the name of the extension module. For example in
+ #: case of a "Flask-Foo" extension in `flask_foo`, the key would be
+ #: ``'foo'``.
+ #:
+ #: .. versionadded:: 0.7
+ self.extensions = {}
+
+ #: The :class:`~werkzeug.routing.Map` for this instance. You can use
+ #: this to change the routing converters after the class was created
+ #: but before any routes are connected. Example::
+ #:
+ #: from werkzeug.routing import BaseConverter
+ #:
+ #: class ListConverter(BaseConverter):
+ #: def to_python(self, value):
+ #: return value.split(',')
+ #: def to_url(self, values):
+ #: return ','.join(super(ListConverter, self).to_url(value)
+ #: for value in values)
+ #:
+ #: app = Flask(__name__)
+ #: app.url_map.converters['list'] = ListConverter
+ self.url_map = Map()
+
+ # tracks internally if the application already handled at least one
+ # request.
+ self._got_first_request = False
+ self._before_request_lock = Lock()
+
+ # register the static folder for the application. Do that even
+ # if the folder does not exist. First of all it might be created
+ # while the server is running (usually happens during development)
+ # but also because google appengine stores static files somewhere
+ # else when mapped with the .yml file.
+ if self.has_static_folder:
+ self.add_url_rule(self.static_url_path + '/',
+ endpoint='static',
+ view_func=self.send_static_file)
+
+ #: The click command line context for this application. Commands
+ #: registered here show up in the :command:`flask` command once the
+ #: application has been discovered. The default commands are
+ #: provided by Flask itself and can be overridden.
+ #:
+ #: This is an instance of a :class:`click.Group` object.
+ self.cli = cli.AppGroup(self.name)
+
+ def _get_error_handlers(self):
+ from warnings import warn
+ warn(DeprecationWarning('error_handlers is deprecated, use the '
+ 'new error_handler_spec attribute instead.'), stacklevel=1)
+ return self._error_handlers
+ def _set_error_handlers(self, value):
+ self._error_handlers = value
+ self.error_handler_spec[None] = value
+ error_handlers = property(_get_error_handlers, _set_error_handlers)
+ del _get_error_handlers, _set_error_handlers
+
+ @locked_cached_property
+ def name(self):
+ """The name of the application. This is usually the import name
+ with the difference that it's guessed from the run file if the
+ import name is main. This name is used as a display name when
+ Flask needs the name of the application. It can be set and overridden
+ to change the value.
+
+ .. versionadded:: 0.8
+ """
+ if self.import_name == '__main__':
+ fn = getattr(sys.modules['__main__'], '__file__', None)
+ if fn is None:
+ return '__main__'
+ return os.path.splitext(os.path.basename(fn))[0]
+ return self.import_name
+
+ @property
+ def propagate_exceptions(self):
+ """Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration
+ value in case it's set, otherwise a sensible default is returned.
+
+ .. versionadded:: 0.7
+ """
+ rv = self.config['PROPAGATE_EXCEPTIONS']
+ if rv is not None:
+ return rv
+ return self.testing or self.debug
+
+ @property
+ def preserve_context_on_exception(self):
+ """Returns the value of the ``PRESERVE_CONTEXT_ON_EXCEPTION``
+ configuration value in case it's set, otherwise a sensible default
+ is returned.
+
+ .. versionadded:: 0.7
+ """
+ rv = self.config['PRESERVE_CONTEXT_ON_EXCEPTION']
+ if rv is not None:
+ return rv
+ return self.debug
+
+ @property
+ def logger(self):
+ """A :class:`logging.Logger` object for this application. The
+ default configuration is to log to stderr if the application is
+ in debug mode. This logger can be used to (surprise) log messages.
+ Here some examples::
+
+ app.logger.debug('A value for debugging')
+ app.logger.warning('A warning occurred (%d apples)', 42)
+ app.logger.error('An error occurred')
+
+ .. versionadded:: 0.3
+ """
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ with _logger_lock:
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ from flask.logging import create_logger
+ self._logger = rv = create_logger(self)
+ return rv
+
+ @locked_cached_property
+ def jinja_env(self):
+ """The Jinja2 environment used to load templates."""
+ return self.create_jinja_environment()
+
+ @property
+ def got_first_request(self):
+ """This attribute is set to ``True`` if the application started
+ handling the first request.
+
+ .. versionadded:: 0.8
+ """
+ return self._got_first_request
+
+ def make_config(self, instance_relative=False):
+ """Used to create the config attribute by the Flask constructor.
+ The `instance_relative` parameter is passed in from the constructor
+ of Flask (there named `instance_relative_config`) and indicates if
+ the config should be relative to the instance path or the root path
+ of the application.
+
+ .. versionadded:: 0.8
+ """
+ root_path = self.root_path
+ if instance_relative:
+ root_path = self.instance_path
+ return self.config_class(root_path, self.default_config)
+
+ def auto_find_instance_path(self):
+ """Tries to locate the instance path if it was not provided to the
+ constructor of the application class. It will basically calculate
+ the path to a folder named ``instance`` next to your main file or
+ the package.
+
+ .. versionadded:: 0.8
+ """
+ prefix, package_path = find_package(self.import_name)
+ if prefix is None:
+ return os.path.join(package_path, 'instance')
+ return os.path.join(prefix, 'var', self.name + '-instance')
+
+ def open_instance_resource(self, resource, mode='rb'):
+ """Opens a resource from the application's instance folder
+ (:attr:`instance_path`). Otherwise works like
+ :meth:`open_resource`. Instance resources can also be opened for
+ writing.
+
+ :param resource: the name of the resource. To access resources within
+ subfolders use forward slashes as separator.
+ :param mode: resource file opening mode, default is 'rb'.
+ """
+ return open(os.path.join(self.instance_path, resource), mode)
+
+ def create_jinja_environment(self):
+ """Creates the Jinja2 environment based on :attr:`jinja_options`
+ and :meth:`select_jinja_autoescape`. Since 0.7 this also adds
+ the Jinja2 globals and filters after initialization. Override
+ this function to customize the behavior.
+
+ .. versionadded:: 0.5
+ .. versionchanged:: 0.11
+ ``Environment.auto_reload`` set in accordance with
+ ``TEMPLATES_AUTO_RELOAD`` configuration option.
+ """
+ options = dict(self.jinja_options)
+ if 'autoescape' not in options:
+ options['autoescape'] = self.select_jinja_autoescape
+ if 'auto_reload' not in options:
+ if self.config['TEMPLATES_AUTO_RELOAD'] is not None:
+ options['auto_reload'] = self.config['TEMPLATES_AUTO_RELOAD']
+ else:
+ options['auto_reload'] = self.debug
+ rv = self.jinja_environment(self, **options)
+ rv.globals.update(
+ url_for=url_for,
+ get_flashed_messages=get_flashed_messages,
+ config=self.config,
+ # request, session and g are normally added with the
+ # context processor for efficiency reasons but for imported
+ # templates we also want the proxies in there.
+ request=request,
+ session=session,
+ g=g
+ )
+ rv.filters['tojson'] = json.tojson_filter
+ return rv
+
+ def create_global_jinja_loader(self):
+ """Creates the loader for the Jinja2 environment. Can be used to
+ override just the loader and keeping the rest unchanged. It's
+ discouraged to override this function. Instead one should override
+ the :meth:`jinja_loader` function instead.
+
+ The global loader dispatches between the loaders of the application
+ and the individual blueprints.
+
+ .. versionadded:: 0.7
+ """
+ return DispatchingJinjaLoader(self)
+
+ def init_jinja_globals(self):
+ """Deprecated. Used to initialize the Jinja2 globals.
+
+ .. versionadded:: 0.5
+ .. versionchanged:: 0.7
+ This method is deprecated with 0.7. Override
+ :meth:`create_jinja_environment` instead.
+ """
+
+ def select_jinja_autoescape(self, filename):
+ """Returns ``True`` if autoescaping should be active for the given
+ template name. If no template name is given, returns `True`.
+
+ .. versionadded:: 0.5
+ """
+ if filename is None:
+ return True
+ return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))
+
+ def update_template_context(self, context):
+ """Update the template context with some commonly used variables.
+ This injects request, session, config and g into the template
+ context as well as everything template context processors want
+ to inject. Note that the as of Flask 0.6, the original values
+ in the context will not be overridden if a context processor
+ decides to return a value with the same key.
+
+ :param context: the context as a dictionary that is updated in place
+ to add extra variables.
+ """
+ funcs = self.template_context_processors[None]
+ reqctx = _request_ctx_stack.top
+ if reqctx is not None:
+ bp = reqctx.request.blueprint
+ if bp is not None and bp in self.template_context_processors:
+ funcs = chain(funcs, self.template_context_processors[bp])
+ orig_ctx = context.copy()
+ for func in funcs:
+ context.update(func())
+ # make sure the original values win. This makes it possible to
+ # easier add new variables in context processors without breaking
+ # existing views.
+ context.update(orig_ctx)
+
+ def make_shell_context(self):
+ """Returns the shell context for an interactive shell for this
+ application. This runs all the registered shell context
+ processors.
+
+ .. versionadded:: 0.11
+ """
+ rv = {'app': self, 'g': g}
+ for processor in self.shell_context_processors:
+ rv.update(processor())
+ return rv
+
+ def run(self, host=None, port=None, debug=None, **options):
+ """Runs the application on a local development server.
+
+ Do not use ``run()`` in a production setting. It is not intended to
+ meet security and performance requirements for a production server.
+ Instead, see :ref:`deployment` for WSGI server recommendations.
+
+ If the :attr:`debug` flag is set the server will automatically reload
+ for code changes and show a debugger in case an exception happened.
+
+ If you want to run the application in debug mode, but disable the
+ code execution on the interactive debugger, you can pass
+ ``use_evalex=False`` as parameter. This will keep the debugger's
+ traceback screen active, but disable code execution.
+
+ It is not recommended to use this function for development with
+ automatic reloading as this is badly supported. Instead you should
+ be using the :command:`flask` command line script's ``run`` support.
+
+ .. admonition:: Keep in Mind
+
+ Flask will suppress any server error with a generic error page
+ unless it is in debug mode. As such to enable just the
+ interactive debugger without the code reloading, you have to
+ invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.
+ Setting ``use_debugger`` to ``True`` without being in debug mode
+ won't catch any exceptions because there won't be any to
+ catch.
+
+ .. versionchanged:: 0.10
+ The default port is now picked from the ``SERVER_NAME`` variable.
+
+ :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to
+ have the server available externally as well. Defaults to
+ ``'127.0.0.1'``.
+ :param port: the port of the webserver. Defaults to ``5000`` or the
+ port defined in the ``SERVER_NAME`` config variable if
+ present.
+ :param debug: if given, enable or disable debug mode.
+ See :attr:`debug`.
+ :param options: the options to be forwarded to the underlying
+ Werkzeug server. See
+ :func:`werkzeug.serving.run_simple` for more
+ information.
+ """
+ from werkzeug.serving import run_simple
+ if host is None:
+ host = '127.0.0.1'
+ if port is None:
+ server_name = self.config['SERVER_NAME']
+ if server_name and ':' in server_name:
+ port = int(server_name.rsplit(':', 1)[1])
+ else:
+ port = 5000
+ if debug is not None:
+ self.debug = bool(debug)
+ options.setdefault('use_reloader', self.debug)
+ options.setdefault('use_debugger', self.debug)
+ try:
+ run_simple(host, port, self, **options)
+ finally:
+ # reset the first request information if the development server
+ # reset normally. This makes it possible to restart the server
+ # without reloader and that stuff from an interactive shell.
+ self._got_first_request = False
+
+ def test_client(self, use_cookies=True, **kwargs):
+ """Creates a test client for this application. For information
+ about unit testing head over to :ref:`testing`.
+
+ Note that if you are testing for assertions or exceptions in your
+ application code, you must set ``app.testing = True`` in order for the
+ exceptions to propagate to the test client. Otherwise, the exception
+ will be handled by the application (not visible to the test client) and
+ the only indication of an AssertionError or other exception will be a
+ 500 status code response to the test client. See the :attr:`testing`
+ attribute. For example::
+
+ app.testing = True
+ client = app.test_client()
+
+ The test client can be used in a ``with`` block to defer the closing down
+ of the context until the end of the ``with`` block. This is useful if
+ you want to access the context locals for testing::
+
+ with app.test_client() as c:
+ rv = c.get('/?vodka=42')
+ assert request.args['vodka'] == '42'
+
+ Additionally, you may pass optional keyword arguments that will then
+ be passed to the application's :attr:`test_client_class` constructor.
+ For example::
+
+ from flask.testing import FlaskClient
+
+ class CustomClient(FlaskClient):
+ def __init__(self, *args, **kwargs):
+ self._authentication = kwargs.pop("authentication")
+ super(CustomClient,self).__init__( *args, **kwargs)
+
+ app.test_client_class = CustomClient
+ client = app.test_client(authentication='Basic ....')
+
+ See :class:`~flask.testing.FlaskClient` for more information.
+
+ .. versionchanged:: 0.4
+ added support for ``with`` block usage for the client.
+
+ .. versionadded:: 0.7
+ The `use_cookies` parameter was added as well as the ability
+ to override the client to be used by setting the
+ :attr:`test_client_class` attribute.
+
+ .. versionchanged:: 0.11
+ Added `**kwargs` to support passing additional keyword arguments to
+ the constructor of :attr:`test_client_class`.
+ """
+ cls = self.test_client_class
+ if cls is None:
+ from flask.testing import FlaskClient as cls
+ return cls(self, self.response_class, use_cookies=use_cookies, **kwargs)
+
+ def open_session(self, request):
+ """Creates or opens a new session. Default implementation stores all
+ session data in a signed cookie. This requires that the
+ :attr:`secret_key` is set. Instead of overriding this method
+ we recommend replacing the :class:`session_interface`.
+
+ :param request: an instance of :attr:`request_class`.
+ """
+ return self.session_interface.open_session(self, request)
+
+ def save_session(self, session, response):
+ """Saves the session if it needs updates. For the default
+ implementation, check :meth:`open_session`. Instead of overriding this
+ method we recommend replacing the :class:`session_interface`.
+
+ :param session: the session to be saved (a
+ :class:`~werkzeug.contrib.securecookie.SecureCookie`
+ object)
+ :param response: an instance of :attr:`response_class`
+ """
+ return self.session_interface.save_session(self, session, response)
+
+ def make_null_session(self):
+ """Creates a new instance of a missing session. Instead of overriding
+ this method we recommend replacing the :class:`session_interface`.
+
+ .. versionadded:: 0.7
+ """
+ return self.session_interface.make_null_session(self)
+
+ @setupmethod
+ def register_blueprint(self, blueprint, **options):
+ """Registers a blueprint on the application.
+
+ .. versionadded:: 0.7
+ """
+ first_registration = False
+ if blueprint.name in self.blueprints:
+ assert self.blueprints[blueprint.name] is blueprint, \
+ 'A blueprint\'s name collision occurred between %r and ' \
+ '%r. Both share the same name "%s". Blueprints that ' \
+ 'are created on the fly need unique names.' % \
+ (blueprint, self.blueprints[blueprint.name], blueprint.name)
+ else:
+ self.blueprints[blueprint.name] = blueprint
+ self._blueprint_order.append(blueprint)
+ first_registration = True
+ blueprint.register(self, options, first_registration)
+
+ def iter_blueprints(self):
+ """Iterates over all blueprints by the order they were registered.
+
+ .. versionadded:: 0.11
+ """
+ return iter(self._blueprint_order)
+
+ @setupmethod
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Connects a URL rule. Works exactly like the :meth:`route`
+ decorator. If a view_func is provided it will be registered with the
+ endpoint.
+
+ Basically this example::
+
+ @app.route('/')
+ def index():
+ pass
+
+ Is equivalent to the following::
+
+ def index():
+ pass
+ app.add_url_rule('/', 'index', index)
+
+ If the view_func is not provided you will need to connect the endpoint
+ to a view function like so::
+
+ app.view_functions['index'] = index
+
+ Internally :meth:`route` invokes :meth:`add_url_rule` so if you want
+ to customize the behavior via subclassing you only need to change
+ this method.
+
+ For more information refer to :ref:`url-route-registrations`.
+
+ .. versionchanged:: 0.2
+ `view_func` parameter added.
+
+ .. versionchanged:: 0.6
+ ``OPTIONS`` is added automatically as method.
+
+ :param rule: the URL rule as string
+ :param endpoint: the endpoint for the registered URL rule. Flask
+ itself assumes the name of the view function as
+ endpoint
+ :param view_func: the function to call when serving a request to the
+ provided endpoint
+ :param options: the options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object. A change
+ to Werkzeug is handling of method options. methods
+ is a list of methods this rule should be limited
+ to (``GET``, ``POST`` etc.). By default a rule
+ just listens for ``GET`` (and implicitly ``HEAD``).
+ Starting with Flask 0.6, ``OPTIONS`` is implicitly
+ added and handled by the standard request handling.
+ """
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func)
+ options['endpoint'] = endpoint
+ methods = options.pop('methods', None)
+
+ # if the methods are not given and the view_func object knows its
+ # methods we can use that instead. If neither exists, we go with
+ # a tuple of only ``GET`` as default.
+ if methods is None:
+ methods = getattr(view_func, 'methods', None) or ('GET',)
+ if isinstance(methods, string_types):
+ raise TypeError('Allowed methods have to be iterables of strings, '
+ 'for example: @app.route(..., methods=["POST"])')
+ methods = set(item.upper() for item in methods)
+
+ # Methods that should always be added
+ required_methods = set(getattr(view_func, 'required_methods', ()))
+
+ # starting with Flask 0.8 the view_func object can disable and
+ # force-enable the automatic options handling.
+ provide_automatic_options = getattr(view_func,
+ 'provide_automatic_options', None)
+
+ if provide_automatic_options is None:
+ if 'OPTIONS' not in methods:
+ provide_automatic_options = True
+ required_methods.add('OPTIONS')
+ else:
+ provide_automatic_options = False
+
+ # Add the required methods now.
+ methods |= required_methods
+
+ rule = self.url_rule_class(rule, methods=methods, **options)
+ rule.provide_automatic_options = provide_automatic_options
+
+ self.url_map.add(rule)
+ if view_func is not None:
+ old_func = self.view_functions.get(endpoint)
+ if old_func is not None and old_func != view_func:
+ raise AssertionError('View function mapping is overwriting an '
+ 'existing endpoint function: %s' % endpoint)
+ self.view_functions[endpoint] = view_func
+
+ def route(self, rule, **options):
+ """A decorator that is used to register a view function for a
+ given URL rule. This does the same thing as :meth:`add_url_rule`
+ but is intended for decorator usage::
+
+ @app.route('/')
+ def index():
+ return 'Hello World'
+
+ For more information refer to :ref:`url-route-registrations`.
+
+ :param rule: the URL rule as string
+ :param endpoint: the endpoint for the registered URL rule. Flask
+ itself assumes the name of the view function as
+ endpoint
+ :param options: the options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object. A change
+ to Werkzeug is handling of method options. methods
+ is a list of methods this rule should be limited
+ to (``GET``, ``POST`` etc.). By default a rule
+ just listens for ``GET`` (and implicitly ``HEAD``).
+ Starting with Flask 0.6, ``OPTIONS`` is implicitly
+ added and handled by the standard request handling.
+ """
+ def decorator(f):
+ endpoint = options.pop('endpoint', None)
+ self.add_url_rule(rule, endpoint, f, **options)
+ return f
+ return decorator
+
+ @setupmethod
+ def endpoint(self, endpoint):
+ """A decorator to register a function as an endpoint.
+ Example::
+
+ @app.endpoint('example.endpoint')
+ def example():
+ return "example"
+
+ :param endpoint: the name of the endpoint
+ """
+ def decorator(f):
+ self.view_functions[endpoint] = f
+ return f
+ return decorator
+
+ @staticmethod
+ def _get_exc_class_and_code(exc_class_or_code):
+ """Ensure that we register only exceptions as handler keys"""
+ if isinstance(exc_class_or_code, integer_types):
+ exc_class = default_exceptions[exc_class_or_code]
+ else:
+ exc_class = exc_class_or_code
+
+ assert issubclass(exc_class, Exception)
+
+ if issubclass(exc_class, HTTPException):
+ return exc_class, exc_class.code
+ else:
+ return exc_class, None
+
+ @setupmethod
+ def errorhandler(self, code_or_exception):
+ """A decorator that is used to register a function given an
+ error code. Example::
+
+ @app.errorhandler(404)
+ def page_not_found(error):
+ return 'This page does not exist', 404
+
+ You can also register handlers for arbitrary exceptions::
+
+ @app.errorhandler(DatabaseError)
+ def special_exception_handler(error):
+ return 'Database connection failed', 500
+
+ You can also register a function as error handler without using
+ the :meth:`errorhandler` decorator. The following example is
+ equivalent to the one above::
+
+ def page_not_found(error):
+ return 'This page does not exist', 404
+ app.error_handler_spec[None][404] = page_not_found
+
+ Setting error handlers via assignments to :attr:`error_handler_spec`
+ however is discouraged as it requires fiddling with nested dictionaries
+ and the special case for arbitrary exception types.
+
+ The first ``None`` refers to the active blueprint. If the error
+ handler should be application wide ``None`` shall be used.
+
+ .. versionadded:: 0.7
+ Use :meth:`register_error_handler` instead of modifying
+ :attr:`error_handler_spec` directly, for application wide error
+ handlers.
+
+ .. versionadded:: 0.7
+ One can now additionally also register custom exception types
+ that do not necessarily have to be a subclass of the
+ :class:`~werkzeug.exceptions.HTTPException` class.
+
+ :param code_or_exception: the code as integer for the handler, or
+ an arbitrary exception
+ """
+ def decorator(f):
+ self._register_error_handler(None, code_or_exception, f)
+ return f
+ return decorator
+
+ def register_error_handler(self, code_or_exception, f):
+ """Alternative error attach function to the :meth:`errorhandler`
+ decorator that is more straightforward to use for non decorator
+ usage.
+
+ .. versionadded:: 0.7
+ """
+ self._register_error_handler(None, code_or_exception, f)
+
+ @setupmethod
+ def _register_error_handler(self, key, code_or_exception, f):
+ """
+ :type key: None|str
+ :type code_or_exception: int|T<=Exception
+ :type f: callable
+ """
+ if isinstance(code_or_exception, HTTPException): # old broken behavior
+ raise ValueError(
+ 'Tried to register a handler for an exception instance {0!r}. '
+ 'Handlers can only be registered for exception classes or HTTP error codes.'
+ .format(code_or_exception))
+
+ exc_class, code = self._get_exc_class_and_code(code_or_exception)
+
+ handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {})
+ handlers[exc_class] = f
+
+ @setupmethod
+ def template_filter(self, name=None):
+ """A decorator that is used to register custom template filter.
+ You can specify a name for the filter, otherwise the function
+ name will be used. Example::
+
+ @app.template_filter()
+ def reverse(s):
+ return s[::-1]
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_template_filter(f, name=name)
+ return f
+ return decorator
+
+ @setupmethod
+ def add_template_filter(self, f, name=None):
+ """Register a custom template filter. Works exactly like the
+ :meth:`template_filter` decorator.
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ self.jinja_env.filters[name or f.__name__] = f
+
+ @setupmethod
+ def template_test(self, name=None):
+ """A decorator that is used to register custom template test.
+ You can specify a name for the test, otherwise the function
+ name will be used. Example::
+
+ @app.template_test()
+ def is_prime(n):
+ if n == 2:
+ return True
+ for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
+ if n % i == 0:
+ return False
+ return True
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_template_test(f, name=name)
+ return f
+ return decorator
+
+ @setupmethod
+ def add_template_test(self, f, name=None):
+ """Register a custom template test. Works exactly like the
+ :meth:`template_test` decorator.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+ self.jinja_env.tests[name or f.__name__] = f
+
+ @setupmethod
+ def template_global(self, name=None):
+ """A decorator that is used to register a custom template global function.
+ You can specify a name for the global function, otherwise the function
+ name will be used. Example::
+
+ @app.template_global()
+ def double(n):
+ return 2 * n
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global function, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_template_global(f, name=name)
+ return f
+ return decorator
+
+ @setupmethod
+ def add_template_global(self, f, name=None):
+ """Register a custom template global function. Works exactly like the
+ :meth:`template_global` decorator.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global function, otherwise the
+ function name will be used.
+ """
+ self.jinja_env.globals[name or f.__name__] = f
+
+ @setupmethod
+ def before_request(self, f):
+ """Registers a function to run before each request.
+
+ The function will be called without any arguments.
+ If the function returns a non-None value, it's handled as
+ if it was the return value from the view and further
+ request handling is stopped.
+ """
+ self.before_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def before_first_request(self, f):
+ """Registers a function to be run before the first request to this
+ instance of the application.
+
+ The function will be called without any arguments and its return
+ value is ignored.
+
+ .. versionadded:: 0.8
+ """
+ self.before_first_request_funcs.append(f)
+ return f
+
+ @setupmethod
+ def after_request(self, f):
+ """Register a function to be run after each request.
+
+ Your function must take one parameter, an instance of
+ :attr:`response_class` and return a new response object or the
+ same (see :meth:`process_response`).
+
+ As of Flask 0.7 this function might not be executed at the end of the
+ request in case an unhandled exception occurred.
+ """
+ self.after_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def teardown_request(self, f):
+ """Register a function to be run at the end of each request,
+ regardless of whether there was an exception or not. These functions
+ are executed when the request context is popped, even if not an
+ actual request was performed.
+
+ Example::
+
+ ctx = app.test_request_context()
+ ctx.push()
+ ...
+ ctx.pop()
+
+ When ``ctx.pop()`` is executed in the above example, the teardown
+ functions are called just before the request context moves from the
+ stack of active contexts. This becomes relevant if you are using
+ such constructs in tests.
+
+ Generally teardown functions must take every necessary step to avoid
+ that they will fail. If they do execute code that might fail they
+ will have to surround the execution of these code by try/except
+ statements and log occurring errors.
+
+ When a teardown function was called because of a exception it will
+ be passed an error object.
+
+ The return values of teardown functions are ignored.
+
+ .. admonition:: Debug Note
+
+ In debug mode Flask will not tear down a request on an exception
+ immediately. Instead it will keep it alive so that the interactive
+ debugger can still access it. This behavior can be controlled
+ by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable.
+ """
+ self.teardown_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def teardown_appcontext(self, f):
+ """Registers a function to be called when the application context
+ ends. These functions are typically also called when the request
+ context is popped.
+
+ Example::
+
+ ctx = app.app_context()
+ ctx.push()
+ ...
+ ctx.pop()
+
+ When ``ctx.pop()`` is executed in the above example, the teardown
+ functions are called just before the app context moves from the
+ stack of active contexts. This becomes relevant if you are using
+ such constructs in tests.
+
+ Since a request context typically also manages an application
+ context it would also be called when you pop a request context.
+
+ When a teardown function was called because of an exception it will
+ be passed an error object.
+
+ The return values of teardown functions are ignored.
+
+ .. versionadded:: 0.9
+ """
+ self.teardown_appcontext_funcs.append(f)
+ return f
+
+ @setupmethod
+ def context_processor(self, f):
+ """Registers a template context processor function."""
+ self.template_context_processors[None].append(f)
+ return f
+
+ @setupmethod
+ def shell_context_processor(self, f):
+ """Registers a shell context processor function.
+
+ .. versionadded:: 0.11
+ """
+ self.shell_context_processors.append(f)
+ return f
+
+ @setupmethod
+ def url_value_preprocessor(self, f):
+ """Registers a function as URL value preprocessor for all view
+ functions of the application. It's called before the view functions
+ are called and can modify the url values provided.
+ """
+ self.url_value_preprocessors.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def url_defaults(self, f):
+ """Callback function for URL defaults for all view functions of the
+ application. It's called with the endpoint and values and should
+ update the values passed in place.
+ """
+ self.url_default_functions.setdefault(None, []).append(f)
+ return f
+
+ def _find_error_handler(self, e):
+ """Finds a registered error handler for the request’s blueprint.
+ Otherwise falls back to the app, returns None if not a suitable
+ handler is found.
+ """
+ exc_class, code = self._get_exc_class_and_code(type(e))
+
+ def find_handler(handler_map):
+ if not handler_map:
+ return
+ for cls in exc_class.__mro__:
+ handler = handler_map.get(cls)
+ if handler is not None:
+ # cache for next time exc_class is raised
+ handler_map[exc_class] = handler
+ return handler
+
+ # try blueprint handlers
+ handler = find_handler(self.error_handler_spec
+ .get(request.blueprint, {})
+ .get(code))
+ if handler is not None:
+ return handler
+
+ # fall back to app handlers
+ return find_handler(self.error_handler_spec[None].get(code))
+
+ def handle_http_exception(self, e):
+ """Handles an HTTP exception. By default this will invoke the
+ registered error handlers and fall back to returning the
+ exception as response.
+
+ .. versionadded:: 0.3
+ """
+ # Proxy exceptions don't have error codes. We want to always return
+ # those unchanged as errors
+ if e.code is None:
+ return e
+
+ handler = self._find_error_handler(e)
+ if handler is None:
+ return e
+ return handler(e)
+
+ def trap_http_exception(self, e):
+ """Checks if an HTTP exception should be trapped or not. By default
+ this will return ``False`` for all exceptions except for a bad request
+ key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It
+ also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``.
+
+ This is called for all HTTP exceptions raised by a view function.
+ If it returns ``True`` for any exception the error handler for this
+ exception is not called and it shows up as regular exception in the
+ traceback. This is helpful for debugging implicitly raised HTTP
+ exceptions.
+
+ .. versionadded:: 0.8
+ """
+ if self.config['TRAP_HTTP_EXCEPTIONS']:
+ return True
+ if self.config['TRAP_BAD_REQUEST_ERRORS']:
+ return isinstance(e, BadRequest)
+ return False
+
+ def handle_user_exception(self, e):
+ """This method is called whenever an exception occurs that should be
+ handled. A special case are
+ :class:`~werkzeug.exception.HTTPException`\s which are forwarded by
+ this function to the :meth:`handle_http_exception` method. This
+ function will either return a response value or reraise the
+ exception with the same traceback.
+
+ .. versionadded:: 0.7
+ """
+ exc_type, exc_value, tb = sys.exc_info()
+ assert exc_value is e
+
+ # ensure not to trash sys.exc_info() at that point in case someone
+ # wants the traceback preserved in handle_http_exception. Of course
+ # we cannot prevent users from trashing it themselves in a custom
+ # trap_http_exception method so that's their fault then.
+
+ if isinstance(e, HTTPException) and not self.trap_http_exception(e):
+ return self.handle_http_exception(e)
+
+ handler = self._find_error_handler(e)
+
+ if handler is None:
+ reraise(exc_type, exc_value, tb)
+ return handler(e)
+
+ def handle_exception(self, e):
+ """Default exception handling that kicks in when an exception
+ occurs that is not caught. In debug mode the exception will
+ be re-raised immediately, otherwise it is logged and the handler
+ for a 500 internal server error is used. If no such handler
+ exists, a default 500 internal server error message is displayed.
+
+ .. versionadded:: 0.3
+ """
+ exc_type, exc_value, tb = sys.exc_info()
+
+ got_request_exception.send(self, exception=e)
+ handler = self._find_error_handler(InternalServerError())
+
+ if self.propagate_exceptions:
+ # if we want to repropagate the exception, we can attempt to
+ # raise it with the whole traceback in case we can do that
+ # (the function was actually called from the except part)
+ # otherwise, we just raise the error again
+ if exc_value is e:
+ reraise(exc_type, exc_value, tb)
+ else:
+ raise e
+
+ self.log_exception((exc_type, exc_value, tb))
+ if handler is None:
+ return InternalServerError()
+ return self.finalize_request(handler(e), from_error_handler=True)
+
+ def log_exception(self, exc_info):
+ """Logs an exception. This is called by :meth:`handle_exception`
+ if debugging is disabled and right before the handler is called.
+ The default implementation logs the exception as error on the
+ :attr:`logger`.
+
+ .. versionadded:: 0.8
+ """
+ self.logger.error('Exception on %s [%s]' % (
+ request.path,
+ request.method
+ ), exc_info=exc_info)
+
+ def raise_routing_exception(self, request):
+ """Exceptions that are recording during routing are reraised with
+ this method. During debug we are not reraising redirect requests
+ for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising
+ a different error instead to help debug situations.
+
+ :internal:
+ """
+ if not self.debug \
+ or not isinstance(request.routing_exception, RequestRedirect) \
+ or request.method in ('GET', 'HEAD', 'OPTIONS'):
+ raise request.routing_exception
+
+ from .debughelpers import FormDataRoutingRedirect
+ raise FormDataRoutingRedirect(request)
+
+ def dispatch_request(self):
+ """Does the request dispatching. Matches the URL and returns the
+ return value of the view or error handler. This does not have to
+ be a response object. In order to convert the return value to a
+ proper response object, call :func:`make_response`.
+
+ .. versionchanged:: 0.7
+ This no longer does the exception handling, this code was
+ moved to the new :meth:`full_dispatch_request`.
+ """
+ req = _request_ctx_stack.top.request
+ if req.routing_exception is not None:
+ self.raise_routing_exception(req)
+ rule = req.url_rule
+ # if we provide automatic options for this URL and the
+ # request came with the OPTIONS method, reply automatically
+ if getattr(rule, 'provide_automatic_options', False) \
+ and req.method == 'OPTIONS':
+ return self.make_default_options_response()
+ # otherwise dispatch to the handler for that endpoint
+ return self.view_functions[rule.endpoint](**req.view_args)
+
+ def full_dispatch_request(self):
+ """Dispatches the request and on top of that performs request
+ pre and postprocessing as well as HTTP exception catching and
+ error handling.
+
+ .. versionadded:: 0.7
+ """
+ self.try_trigger_before_first_request_functions()
+ try:
+ request_started.send(self)
+ rv = self.preprocess_request()
+ if rv is None:
+ rv = self.dispatch_request()
+ except Exception as e:
+ rv = self.handle_user_exception(e)
+ return self.finalize_request(rv)
+
+ def finalize_request(self, rv, from_error_handler=False):
+ """Given the return value from a view function this finalizes
+ the request by converting it into a response and invoking the
+ postprocessing functions. This is invoked for both normal
+ request dispatching as well as error handlers.
+
+ Because this means that it might be called as a result of a
+ failure a special safe mode is available which can be enabled
+ with the `from_error_handler` flag. If enabled, failures in
+ response processing will be logged and otherwise ignored.
+
+ :internal:
+ """
+ response = self.make_response(rv)
+ try:
+ response = self.process_response(response)
+ request_finished.send(self, response=response)
+ except Exception:
+ if not from_error_handler:
+ raise
+ self.logger.exception('Request finalizing failed with an '
+ 'error while handling an error')
+ return response
+
+ def try_trigger_before_first_request_functions(self):
+ """Called before each request and will ensure that it triggers
+ the :attr:`before_first_request_funcs` and only exactly once per
+ application instance (which means process usually).
+
+ :internal:
+ """
+ if self._got_first_request:
+ return
+ with self._before_request_lock:
+ if self._got_first_request:
+ return
+ for func in self.before_first_request_funcs:
+ func()
+ self._got_first_request = True
+
+ def make_default_options_response(self):
+ """This method is called to create the default ``OPTIONS`` response.
+ This can be changed through subclassing to change the default
+ behavior of ``OPTIONS`` responses.
+
+ .. versionadded:: 0.7
+ """
+ adapter = _request_ctx_stack.top.url_adapter
+ if hasattr(adapter, 'allowed_methods'):
+ methods = adapter.allowed_methods()
+ else:
+ # fallback for Werkzeug < 0.7
+ methods = []
+ try:
+ adapter.match(method='--')
+ except MethodNotAllowed as e:
+ methods = e.valid_methods
+ except HTTPException as e:
+ pass
+ rv = self.response_class()
+ rv.allow.update(methods)
+ return rv
+
+ def should_ignore_error(self, error):
+ """This is called to figure out if an error should be ignored
+ or not as far as the teardown system is concerned. If this
+ function returns ``True`` then the teardown handlers will not be
+ passed the error.
+
+ .. versionadded:: 0.10
+ """
+ return False
+
+ def make_response(self, rv):
+ """Converts the return value from a view function to a real
+ response object that is an instance of :attr:`response_class`.
+
+ The following types are allowed for `rv`:
+
+ .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
+
+ ======================= ===========================================
+ :attr:`response_class` the object is returned unchanged
+ :class:`str` a response object is created with the
+ string as body
+ :class:`unicode` a response object is created with the
+ string encoded to utf-8 as body
+ a WSGI function the function is called as WSGI application
+ and buffered as response object
+ :class:`tuple` A tuple in the form ``(response, status,
+ headers)`` or ``(response, headers)``
+ where `response` is any of the
+ types defined here, `status` is a string
+ or an integer and `headers` is a list or
+ a dictionary with header values.
+ ======================= ===========================================
+
+ :param rv: the return value from the view function
+
+ .. versionchanged:: 0.9
+ Previously a tuple was interpreted as the arguments for the
+ response object.
+ """
+ status_or_headers = headers = None
+ if isinstance(rv, tuple):
+ rv, status_or_headers, headers = rv + (None,) * (3 - len(rv))
+
+ if rv is None:
+ raise ValueError('View function did not return a response')
+
+ if isinstance(status_or_headers, (dict, list)):
+ headers, status_or_headers = status_or_headers, None
+
+ if not isinstance(rv, self.response_class):
+ # When we create a response object directly, we let the constructor
+ # set the headers and status. We do this because there can be
+ # some extra logic involved when creating these objects with
+ # specific values (like default content type selection).
+ if isinstance(rv, (text_type, bytes, bytearray)):
+ rv = self.response_class(rv, headers=headers,
+ status=status_or_headers)
+ headers = status_or_headers = None
+ else:
+ rv = self.response_class.force_type(rv, request.environ)
+
+ if status_or_headers is not None:
+ if isinstance(status_or_headers, string_types):
+ rv.status = status_or_headers
+ else:
+ rv.status_code = status_or_headers
+ if headers:
+ rv.headers.extend(headers)
+
+ return rv
+
+ def create_url_adapter(self, request):
+ """Creates a URL adapter for the given request. The URL adapter
+ is created at a point where the request context is not yet set up
+ so the request is passed explicitly.
+
+ .. versionadded:: 0.6
+
+ .. versionchanged:: 0.9
+ This can now also be called without a request object when the
+ URL adapter is created for the application context.
+ """
+ if request is not None:
+ return self.url_map.bind_to_environ(request.environ,
+ server_name=self.config['SERVER_NAME'])
+ # We need at the very least the server name to be set for this
+ # to work.
+ if self.config['SERVER_NAME'] is not None:
+ return self.url_map.bind(
+ self.config['SERVER_NAME'],
+ script_name=self.config['APPLICATION_ROOT'] or '/',
+ url_scheme=self.config['PREFERRED_URL_SCHEME'])
+
+ def inject_url_defaults(self, endpoint, values):
+ """Injects the URL defaults for the given endpoint directly into
+ the values dictionary passed. This is used internally and
+ automatically called on URL building.
+
+ .. versionadded:: 0.7
+ """
+ funcs = self.url_default_functions.get(None, ())
+ if '.' in endpoint:
+ bp = endpoint.rsplit('.', 1)[0]
+ funcs = chain(funcs, self.url_default_functions.get(bp, ()))
+ for func in funcs:
+ func(endpoint, values)
+
+ def handle_url_build_error(self, error, endpoint, values):
+ """Handle :class:`~werkzeug.routing.BuildError` on :meth:`url_for`.
+ """
+ exc_type, exc_value, tb = sys.exc_info()
+ for handler in self.url_build_error_handlers:
+ try:
+ rv = handler(error, endpoint, values)
+ if rv is not None:
+ return rv
+ except BuildError as e:
+ # make error available outside except block (py3)
+ error = e
+
+ # At this point we want to reraise the exception. If the error is
+ # still the same one we can reraise it with the original traceback,
+ # otherwise we raise it from here.
+ if error is exc_value:
+ reraise(exc_type, exc_value, tb)
+ raise error
+
+ def preprocess_request(self):
+ """Called before the actual request dispatching and will
+ call each :meth:`before_request` decorated function, passing no
+ arguments.
+ If any of these functions returns a value, it's handled as
+ if it was the return value from the view and further
+ request handling is stopped.
+
+ This also triggers the :meth:`url_value_preprocessor` functions before
+ the actual :meth:`before_request` functions are called.
+ """
+ bp = _request_ctx_stack.top.request.blueprint
+
+ funcs = self.url_value_preprocessors.get(None, ())
+ if bp is not None and bp in self.url_value_preprocessors:
+ funcs = chain(funcs, self.url_value_preprocessors[bp])
+ for func in funcs:
+ func(request.endpoint, request.view_args)
+
+ funcs = self.before_request_funcs.get(None, ())
+ if bp is not None and bp in self.before_request_funcs:
+ funcs = chain(funcs, self.before_request_funcs[bp])
+ for func in funcs:
+ rv = func()
+ if rv is not None:
+ return rv
+
+ def process_response(self, response):
+ """Can be overridden in order to modify the response object
+ before it's sent to the WSGI server. By default this will
+ call all the :meth:`after_request` decorated functions.
+
+ .. versionchanged:: 0.5
+ As of Flask 0.5 the functions registered for after request
+ execution are called in reverse order of registration.
+
+ :param response: a :attr:`response_class` object.
+ :return: a new response object or the same, has to be an
+ instance of :attr:`response_class`.
+ """
+ ctx = _request_ctx_stack.top
+ bp = ctx.request.blueprint
+ funcs = ctx._after_request_functions
+ if bp is not None and bp in self.after_request_funcs:
+ funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
+ if None in self.after_request_funcs:
+ funcs = chain(funcs, reversed(self.after_request_funcs[None]))
+ for handler in funcs:
+ response = handler(response)
+ if not self.session_interface.is_null_session(ctx.session):
+ self.save_session(ctx.session, response)
+ return response
+
+ def do_teardown_request(self, exc=_sentinel):
+ """Called after the actual request dispatching and will
+ call every as :meth:`teardown_request` decorated function. This is
+ not actually called by the :class:`Flask` object itself but is always
+ triggered when the request context is popped. That way we have a
+ tighter control over certain resources under testing environments.
+
+ .. versionchanged:: 0.9
+ Added the `exc` argument. Previously this was always using the
+ current exception information.
+ """
+ if exc is _sentinel:
+ exc = sys.exc_info()[1]
+ funcs = reversed(self.teardown_request_funcs.get(None, ()))
+ bp = _request_ctx_stack.top.request.blueprint
+ if bp is not None and bp in self.teardown_request_funcs:
+ funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
+ for func in funcs:
+ func(exc)
+ request_tearing_down.send(self, exc=exc)
+
+ def do_teardown_appcontext(self, exc=_sentinel):
+ """Called when an application context is popped. This works pretty
+ much the same as :meth:`do_teardown_request` but for the application
+ context.
+
+ .. versionadded:: 0.9
+ """
+ if exc is _sentinel:
+ exc = sys.exc_info()[1]
+ for func in reversed(self.teardown_appcontext_funcs):
+ func(exc)
+ appcontext_tearing_down.send(self, exc=exc)
+
+ def app_context(self):
+ """Binds the application only. For as long as the application is bound
+ to the current context the :data:`flask.current_app` points to that
+ application. An application context is automatically created when a
+ request context is pushed if necessary.
+
+ Example usage::
+
+ with app.app_context():
+ ...
+
+ .. versionadded:: 0.9
+ """
+ return AppContext(self)
+
+ def request_context(self, environ):
+ """Creates a :class:`~flask.ctx.RequestContext` from the given
+ environment and binds it to the current context. This must be used in
+ combination with the ``with`` statement because the request is only bound
+ to the current context for the duration of the ``with`` block.
+
+ Example usage::
+
+ with app.request_context(environ):
+ do_something_with(request)
+
+ The object returned can also be used without the ``with`` statement
+ which is useful for working in the shell. The example above is
+ doing exactly the same as this code::
+
+ ctx = app.request_context(environ)
+ ctx.push()
+ try:
+ do_something_with(request)
+ finally:
+ ctx.pop()
+
+ .. versionchanged:: 0.3
+ Added support for non-with statement usage and ``with`` statement
+ is now passed the ctx object.
+
+ :param environ: a WSGI environment
+ """
+ return RequestContext(self, environ)
+
+ def test_request_context(self, *args, **kwargs):
+ """Creates a WSGI environment from the given values (see
+ :class:`werkzeug.test.EnvironBuilder` for more information, this
+ function accepts the same arguments).
+ """
+ from flask.testing import make_test_environ_builder
+ builder = make_test_environ_builder(self, *args, **kwargs)
+ try:
+ return self.request_context(builder.get_environ())
+ finally:
+ builder.close()
+
+ def wsgi_app(self, environ, start_response):
+ """The actual WSGI application. This is not implemented in
+ `__call__` so that middlewares can be applied without losing a
+ reference to the class. So instead of doing this::
+
+ app = MyMiddleware(app)
+
+ It's a better idea to do this instead::
+
+ app.wsgi_app = MyMiddleware(app.wsgi_app)
+
+ Then you still have the original application object around and
+ can continue to call methods on it.
+
+ .. versionchanged:: 0.7
+ The behavior of the before and after request callbacks was changed
+ under error conditions and a new callback was added that will
+ always execute at the end of the request, independent on if an
+ error occurred or not. See :ref:`callbacks-and-errors`.
+
+ :param environ: a WSGI environment
+ :param start_response: a callable accepting a status code,
+ a list of headers and an optional
+ exception context to start the response
+ """
+ ctx = self.request_context(environ)
+ ctx.push()
+ error = None
+ try:
+ try:
+ response = self.full_dispatch_request()
+ except Exception as e:
+ error = e
+ response = self.handle_exception(e)
+ except:
+ error = sys.exc_info()[1]
+ raise
+ return response(environ, start_response)
+ finally:
+ if self.should_ignore_error(error):
+ error = None
+ ctx.auto_pop(error)
+
+ def __call__(self, environ, start_response):
+ """Shortcut for :attr:`wsgi_app`."""
+ return self.wsgi_app(environ, start_response)
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self.name,
+ )
diff --git a/service2/flask/blueprints.py b/service2/flask/blueprints.py
new file mode 100644
index 0000000..586a1b0
--- /dev/null
+++ b/service2/flask/blueprints.py
@@ -0,0 +1,413 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.blueprints
+ ~~~~~~~~~~~~~~~~
+
+ Blueprints are the recommended way to implement larger or more
+ pluggable applications in Flask 0.7 and later.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from functools import update_wrapper
+
+from .helpers import _PackageBoundObject, _endpoint_from_view_func
+
+
+class BlueprintSetupState(object):
+ """Temporary holder object for registering a blueprint with the
+ application. An instance of this class is created by the
+ :meth:`~flask.Blueprint.make_setup_state` method and later passed
+ to all register callback functions.
+ """
+
+ def __init__(self, blueprint, app, options, first_registration):
+ #: a reference to the current application
+ self.app = app
+
+ #: a reference to the blueprint that created this setup state.
+ self.blueprint = blueprint
+
+ #: a dictionary with all options that were passed to the
+ #: :meth:`~flask.Flask.register_blueprint` method.
+ self.options = options
+
+ #: as blueprints can be registered multiple times with the
+ #: application and not everything wants to be registered
+ #: multiple times on it, this attribute can be used to figure
+ #: out if the blueprint was registered in the past already.
+ self.first_registration = first_registration
+
+ subdomain = self.options.get('subdomain')
+ if subdomain is None:
+ subdomain = self.blueprint.subdomain
+
+ #: The subdomain that the blueprint should be active for, ``None``
+ #: otherwise.
+ self.subdomain = subdomain
+
+ url_prefix = self.options.get('url_prefix')
+ if url_prefix is None:
+ url_prefix = self.blueprint.url_prefix
+
+ #: The prefix that should be used for all URLs defined on the
+ #: blueprint.
+ self.url_prefix = url_prefix
+
+ #: A dictionary with URL defaults that is added to each and every
+ #: URL that was defined with the blueprint.
+ self.url_defaults = dict(self.blueprint.url_values_defaults)
+ self.url_defaults.update(self.options.get('url_defaults', ()))
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """A helper method to register a rule (and optionally a view function)
+ to the application. The endpoint is automatically prefixed with the
+ blueprint's name.
+ """
+ if self.url_prefix:
+ rule = self.url_prefix + rule
+ options.setdefault('subdomain', self.subdomain)
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func)
+ defaults = self.url_defaults
+ if 'defaults' in options:
+ defaults = dict(defaults, **options.pop('defaults'))
+ self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
+ view_func, defaults=defaults, **options)
+
+
+class Blueprint(_PackageBoundObject):
+ """Represents a blueprint. A blueprint is an object that records
+ functions that will be called with the
+ :class:`~flask.blueprints.BlueprintSetupState` later to register functions
+ or other things on the main application. See :ref:`blueprints` for more
+ information.
+
+ .. versionadded:: 0.7
+ """
+
+ warn_on_modifications = False
+ _got_registered_once = False
+
+ def __init__(self, name, import_name, static_folder=None,
+ static_url_path=None, template_folder=None,
+ url_prefix=None, subdomain=None, url_defaults=None,
+ root_path=None):
+ _PackageBoundObject.__init__(self, import_name, template_folder,
+ root_path=root_path)
+ self.name = name
+ self.url_prefix = url_prefix
+ self.subdomain = subdomain
+ self.static_folder = static_folder
+ self.static_url_path = static_url_path
+ self.deferred_functions = []
+ if url_defaults is None:
+ url_defaults = {}
+ self.url_values_defaults = url_defaults
+
+ def record(self, func):
+ """Registers a function that is called when the blueprint is
+ registered on the application. This function is called with the
+ state as argument as returned by the :meth:`make_setup_state`
+ method.
+ """
+ if self._got_registered_once and self.warn_on_modifications:
+ from warnings import warn
+ warn(Warning('The blueprint was already registered once '
+ 'but is getting modified now. These changes '
+ 'will not show up.'))
+ self.deferred_functions.append(func)
+
+ def record_once(self, func):
+ """Works like :meth:`record` but wraps the function in another
+ function that will ensure the function is only called once. If the
+ blueprint is registered a second time on the application, the
+ function passed is not called.
+ """
+ def wrapper(state):
+ if state.first_registration:
+ func(state)
+ return self.record(update_wrapper(wrapper, func))
+
+ def make_setup_state(self, app, options, first_registration=False):
+ """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
+ object that is later passed to the register callback functions.
+ Subclasses can override this to return a subclass of the setup state.
+ """
+ return BlueprintSetupState(self, app, options, first_registration)
+
+ def register(self, app, options, first_registration=False):
+ """Called by :meth:`Flask.register_blueprint` to register a blueprint
+ on the application. This can be overridden to customize the register
+ behavior. Keyword arguments from
+ :func:`~flask.Flask.register_blueprint` are directly forwarded to this
+ method in the `options` dictionary.
+ """
+ self._got_registered_once = True
+ state = self.make_setup_state(app, options, first_registration)
+ if self.has_static_folder:
+ state.add_url_rule(self.static_url_path + '/',
+ view_func=self.send_static_file,
+ endpoint='static')
+
+ for deferred in self.deferred_functions:
+ deferred(state)
+
+ def route(self, rule, **options):
+ """Like :meth:`Flask.route` but for a blueprint. The endpoint for the
+ :func:`url_for` function is prefixed with the name of the blueprint.
+ """
+ def decorator(f):
+ endpoint = options.pop("endpoint", f.__name__)
+ self.add_url_rule(rule, endpoint, f, **options)
+ return f
+ return decorator
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
+ the :func:`url_for` function is prefixed with the name of the blueprint.
+ """
+ if endpoint:
+ assert '.' not in endpoint, "Blueprint endpoints should not contain dots"
+ self.record(lambda s:
+ s.add_url_rule(rule, endpoint, view_func, **options))
+
+ def endpoint(self, endpoint):
+ """Like :meth:`Flask.endpoint` but for a blueprint. This does not
+ prefix the endpoint with the blueprint name, this has to be done
+ explicitly by the user of this method. If the endpoint is prefixed
+ with a `.` it will be registered to the current blueprint, otherwise
+ it's an application independent endpoint.
+ """
+ def decorator(f):
+ def register_endpoint(state):
+ state.app.view_functions[endpoint] = f
+ self.record_once(register_endpoint)
+ return f
+ return decorator
+
+ def app_template_filter(self, name=None):
+ """Register a custom template filter, available application wide. Like
+ :meth:`Flask.template_filter` but for a blueprint.
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_app_template_filter(f, name=name)
+ return f
+ return decorator
+
+ def add_app_template_filter(self, f, name=None):
+ """Register a custom template filter, available application wide. Like
+ :meth:`Flask.add_template_filter` but for a blueprint. Works exactly
+ like the :meth:`app_template_filter` decorator.
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def register_template(state):
+ state.app.jinja_env.filters[name or f.__name__] = f
+ self.record_once(register_template)
+
+ def app_template_test(self, name=None):
+ """Register a custom template test, available application wide. Like
+ :meth:`Flask.template_test` but for a blueprint.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_app_template_test(f, name=name)
+ return f
+ return decorator
+
+ def add_app_template_test(self, f, name=None):
+ """Register a custom template test, available application wide. Like
+ :meth:`Flask.add_template_test` but for a blueprint. Works exactly
+ like the :meth:`app_template_test` decorator.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+ def register_template(state):
+ state.app.jinja_env.tests[name or f.__name__] = f
+ self.record_once(register_template)
+
+ def app_template_global(self, name=None):
+ """Register a custom template global, available application wide. Like
+ :meth:`Flask.template_global` but for a blueprint.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.add_app_template_global(f, name=name)
+ return f
+ return decorator
+
+ def add_app_template_global(self, f, name=None):
+ """Register a custom template global, available application wide. Like
+ :meth:`Flask.add_template_global` but for a blueprint. Works exactly
+ like the :meth:`app_template_global` decorator.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global, otherwise the
+ function name will be used.
+ """
+ def register_template(state):
+ state.app.jinja_env.globals[name or f.__name__] = f
+ self.record_once(register_template)
+
+ def before_request(self, f):
+ """Like :meth:`Flask.before_request` but for a blueprint. This function
+ is only executed before each request that is handled by a function of
+ that blueprint.
+ """
+ self.record_once(lambda s: s.app.before_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def before_app_request(self, f):
+ """Like :meth:`Flask.before_request`. Such a function is executed
+ before each request, even if outside of a blueprint.
+ """
+ self.record_once(lambda s: s.app.before_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def before_app_first_request(self, f):
+ """Like :meth:`Flask.before_first_request`. Such a function is
+ executed before the first request to the application.
+ """
+ self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
+ return f
+
+ def after_request(self, f):
+ """Like :meth:`Flask.after_request` but for a blueprint. This function
+ is only executed after each request that is handled by a function of
+ that blueprint.
+ """
+ self.record_once(lambda s: s.app.after_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def after_app_request(self, f):
+ """Like :meth:`Flask.after_request` but for a blueprint. Such a function
+ is executed after each request, even if outside of the blueprint.
+ """
+ self.record_once(lambda s: s.app.after_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def teardown_request(self, f):
+ """Like :meth:`Flask.teardown_request` but for a blueprint. This
+ function is only executed when tearing down requests handled by a
+ function of that blueprint. Teardown request functions are executed
+ when the request context is popped, even when no actual request was
+ performed.
+ """
+ self.record_once(lambda s: s.app.teardown_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def teardown_app_request(self, f):
+ """Like :meth:`Flask.teardown_request` but for a blueprint. Such a
+ function is executed when tearing down each request, even if outside of
+ the blueprint.
+ """
+ self.record_once(lambda s: s.app.teardown_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a blueprint. This
+ function is only executed for requests handled by a blueprint.
+ """
+ self.record_once(lambda s: s.app.template_context_processors
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def app_context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a blueprint. Such a
+ function is executed each request, even if outside of the blueprint.
+ """
+ self.record_once(lambda s: s.app.template_context_processors
+ .setdefault(None, []).append(f))
+ return f
+
+ def app_errorhandler(self, code):
+ """Like :meth:`Flask.errorhandler` but for a blueprint. This
+ handler is used for all requests, even if outside of the blueprint.
+ """
+ def decorator(f):
+ self.record_once(lambda s: s.app.errorhandler(code)(f))
+ return f
+ return decorator
+
+ def url_value_preprocessor(self, f):
+ """Registers a function as URL value preprocessor for this
+ blueprint. It's called before the view functions are called and
+ can modify the url values provided.
+ """
+ self.record_once(lambda s: s.app.url_value_preprocessors
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def url_defaults(self, f):
+ """Callback function for URL defaults for this blueprint. It's called
+ with the endpoint and values and should update the values passed
+ in place.
+ """
+ self.record_once(lambda s: s.app.url_default_functions
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def app_url_value_preprocessor(self, f):
+ """Same as :meth:`url_value_preprocessor` but application wide.
+ """
+ self.record_once(lambda s: s.app.url_value_preprocessors
+ .setdefault(None, []).append(f))
+ return f
+
+ def app_url_defaults(self, f):
+ """Same as :meth:`url_defaults` but application wide.
+ """
+ self.record_once(lambda s: s.app.url_default_functions
+ .setdefault(None, []).append(f))
+ return f
+
+ def errorhandler(self, code_or_exception):
+ """Registers an error handler that becomes active for this blueprint
+ only. Please be aware that routing does not happen local to a
+ blueprint so an error handler for 404 usually is not handled by
+ a blueprint unless it is caused inside a view function. Another
+ special case is the 500 internal server error which is always looked
+ up from the application.
+
+ Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator
+ of the :class:`~flask.Flask` object.
+ """
+ def decorator(f):
+ self.record_once(lambda s: s.app._register_error_handler(
+ self.name, code_or_exception, f))
+ return f
+ return decorator
+
+ def register_error_handler(self, code_or_exception, f):
+ """Non-decorator version of the :meth:`errorhandler` error attach
+ function, akin to the :meth:`~flask.Flask.register_error_handler`
+ application-wide function of the :class:`~flask.Flask` object but
+ for error handlers limited to this blueprint.
+
+ .. versionadded:: 0.11
+ """
+ self.record_once(lambda s: s.app._register_error_handler(
+ self.name, code_or_exception, f))
diff --git a/service2/flask/cli.py b/service2/flask/cli.py
new file mode 100644
index 0000000..074ee76
--- /dev/null
+++ b/service2/flask/cli.py
@@ -0,0 +1,517 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.cli
+ ~~~~~~~~~
+
+ A simple command line application to run flask apps.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import sys
+from threading import Lock, Thread
+from functools import update_wrapper
+
+import click
+
+from ._compat import iteritems, reraise
+from .helpers import get_debug_flag
+from . import __version__
+
+class NoAppException(click.UsageError):
+ """Raised if an application cannot be found or loaded."""
+
+
+def find_best_app(module):
+ """Given a module instance this tries to find the best possible
+ application in the module or raises an exception.
+ """
+ from . import Flask
+
+ # Search for the most common names first.
+ for attr_name in 'app', 'application':
+ app = getattr(module, attr_name, None)
+ if app is not None and isinstance(app, Flask):
+ return app
+
+ # Otherwise find the only object that is a Flask instance.
+ matches = [v for k, v in iteritems(module.__dict__)
+ if isinstance(v, Flask)]
+
+ if len(matches) == 1:
+ return matches[0]
+ raise NoAppException('Failed to find application in module "%s". Are '
+ 'you sure it contains a Flask application? Maybe '
+ 'you wrapped it in a WSGI middleware or you are '
+ 'using a factory function.' % module.__name__)
+
+
+def prepare_exec_for_file(filename):
+ """Given a filename this will try to calculate the python path, add it
+ to the search path and return the actual module name that is expected.
+ """
+ module = []
+
+ # Chop off file extensions or package markers
+ if os.path.split(filename)[1] == '__init__.py':
+ filename = os.path.dirname(filename)
+ elif filename.endswith('.py'):
+ filename = filename[:-3]
+ else:
+ raise NoAppException('The file provided (%s) does exist but is not a '
+ 'valid Python file. This means that it cannot '
+ 'be used as application. Please change the '
+ 'extension to .py' % filename)
+ filename = os.path.realpath(filename)
+
+ dirpath = filename
+ while 1:
+ dirpath, extra = os.path.split(dirpath)
+ module.append(extra)
+ if not os.path.isfile(os.path.join(dirpath, '__init__.py')):
+ break
+
+ sys.path.insert(0, dirpath)
+ return '.'.join(module[::-1])
+
+
+def locate_app(app_id):
+ """Attempts to locate the application."""
+ __traceback_hide__ = True
+ if ':' in app_id:
+ module, app_obj = app_id.split(':', 1)
+ else:
+ module = app_id
+ app_obj = None
+
+ try:
+ __import__(module)
+ except ImportError:
+ # Reraise the ImportError if it occurred within the imported module.
+ # Determine this by checking whether the trace has a depth > 1.
+ if sys.exc_info()[-1].tb_next:
+ raise
+ else:
+ raise NoAppException('The file/path provided (%s) does not appear'
+ ' to exist. Please verify the path is '
+ 'correct. If app is not on PYTHONPATH, '
+ 'ensure the extension is .py' % module)
+
+ mod = sys.modules[module]
+ if app_obj is None:
+ app = find_best_app(mod)
+ else:
+ app = getattr(mod, app_obj, None)
+ if app is None:
+ raise RuntimeError('Failed to find application in module "%s"'
+ % module)
+
+ return app
+
+
+def find_default_import_path():
+ app = os.environ.get('FLASK_APP')
+ if app is None:
+ return
+ if os.path.isfile(app):
+ return prepare_exec_for_file(app)
+ return app
+
+
+def get_version(ctx, param, value):
+ if not value or ctx.resilient_parsing:
+ return
+ message = 'Flask %(version)s\nPython %(python_version)s'
+ click.echo(message % {
+ 'version': __version__,
+ 'python_version': sys.version,
+ }, color=ctx.color)
+ ctx.exit()
+
+version_option = click.Option(['--version'],
+ help='Show the flask version',
+ expose_value=False,
+ callback=get_version,
+ is_flag=True, is_eager=True)
+
+class DispatchingApp(object):
+ """Special application that dispatches to a Flask application which
+ is imported by name in a background thread. If an error happens
+ it is recorded and shown as part of the WSGI handling which in case
+ of the Werkzeug debugger means that it shows up in the browser.
+ """
+
+ def __init__(self, loader, use_eager_loading=False):
+ self.loader = loader
+ self._app = None
+ self._lock = Lock()
+ self._bg_loading_exc_info = None
+ if use_eager_loading:
+ self._load_unlocked()
+ else:
+ self._load_in_background()
+
+ def _load_in_background(self):
+ def _load_app():
+ __traceback_hide__ = True
+ with self._lock:
+ try:
+ self._load_unlocked()
+ except Exception:
+ self._bg_loading_exc_info = sys.exc_info()
+ t = Thread(target=_load_app, args=())
+ t.start()
+
+ def _flush_bg_loading_exception(self):
+ __traceback_hide__ = True
+ exc_info = self._bg_loading_exc_info
+ if exc_info is not None:
+ self._bg_loading_exc_info = None
+ reraise(*exc_info)
+
+ def _load_unlocked(self):
+ __traceback_hide__ = True
+ self._app = rv = self.loader()
+ self._bg_loading_exc_info = None
+ return rv
+
+ def __call__(self, environ, start_response):
+ __traceback_hide__ = True
+ if self._app is not None:
+ return self._app(environ, start_response)
+ self._flush_bg_loading_exception()
+ with self._lock:
+ if self._app is not None:
+ rv = self._app
+ else:
+ rv = self._load_unlocked()
+ return rv(environ, start_response)
+
+
+class ScriptInfo(object):
+ """Help object to deal with Flask applications. This is usually not
+ necessary to interface with as it's used internally in the dispatching
+ to click. In future versions of Flask this object will most likely play
+ a bigger role. Typically it's created automatically by the
+ :class:`FlaskGroup` but you can also manually create it and pass it
+ onwards as click object.
+ """
+
+ def __init__(self, app_import_path=None, create_app=None):
+ if create_app is None:
+ if app_import_path is None:
+ app_import_path = find_default_import_path()
+ self.app_import_path = app_import_path
+ else:
+ app_import_path = None
+
+ #: Optionally the import path for the Flask application.
+ self.app_import_path = app_import_path
+ #: Optionally a function that is passed the script info to create
+ #: the instance of the application.
+ self.create_app = create_app
+ #: A dictionary with arbitrary data that can be associated with
+ #: this script info.
+ self.data = {}
+ self._loaded_app = None
+
+ def load_app(self):
+ """Loads the Flask app (if not yet loaded) and returns it. Calling
+ this multiple times will just result in the already loaded app to
+ be returned.
+ """
+ __traceback_hide__ = True
+ if self._loaded_app is not None:
+ return self._loaded_app
+ if self.create_app is not None:
+ rv = self.create_app(self)
+ else:
+ if not self.app_import_path:
+ raise NoAppException(
+ 'Could not locate Flask application. You did not provide '
+ 'the FLASK_APP environment variable.\n\nFor more '
+ 'information see '
+ 'http://flask.pocoo.org/docs/latest/quickstart/')
+ rv = locate_app(self.app_import_path)
+ debug = get_debug_flag()
+ if debug is not None:
+ rv.debug = debug
+ self._loaded_app = rv
+ return rv
+
+
+pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True)
+
+
+def with_appcontext(f):
+ """Wraps a callback so that it's guaranteed to be executed with the
+ script's application context. If callbacks are registered directly
+ to the ``app.cli`` object then they are wrapped with this function
+ by default unless it's disabled.
+ """
+ @click.pass_context
+ def decorator(__ctx, *args, **kwargs):
+ with __ctx.ensure_object(ScriptInfo).load_app().app_context():
+ return __ctx.invoke(f, *args, **kwargs)
+ return update_wrapper(decorator, f)
+
+
+class AppGroup(click.Group):
+ """This works similar to a regular click :class:`~click.Group` but it
+ changes the behavior of the :meth:`command` decorator so that it
+ automatically wraps the functions in :func:`with_appcontext`.
+
+ Not to be confused with :class:`FlaskGroup`.
+ """
+
+ def command(self, *args, **kwargs):
+ """This works exactly like the method of the same name on a regular
+ :class:`click.Group` but it wraps callbacks in :func:`with_appcontext`
+ unless it's disabled by passing ``with_appcontext=False``.
+ """
+ wrap_for_ctx = kwargs.pop('with_appcontext', True)
+ def decorator(f):
+ if wrap_for_ctx:
+ f = with_appcontext(f)
+ return click.Group.command(self, *args, **kwargs)(f)
+ return decorator
+
+ def group(self, *args, **kwargs):
+ """This works exactly like the method of the same name on a regular
+ :class:`click.Group` but it defaults the group class to
+ :class:`AppGroup`.
+ """
+ kwargs.setdefault('cls', AppGroup)
+ return click.Group.group(self, *args, **kwargs)
+
+
+class FlaskGroup(AppGroup):
+ """Special subclass of the :class:`AppGroup` group that supports
+ loading more commands from the configured Flask app. Normally a
+ developer does not have to interface with this class but there are
+ some very advanced use cases for which it makes sense to create an
+ instance of this.
+
+ For information as of why this is useful see :ref:`custom-scripts`.
+
+ :param add_default_commands: if this is True then the default run and
+ shell commands wil be added.
+ :param add_version_option: adds the ``--version`` option.
+ :param create_app: an optional callback that is passed the script info
+ and returns the loaded app.
+ """
+
+ def __init__(self, add_default_commands=True, create_app=None,
+ add_version_option=True, **extra):
+ params = list(extra.pop('params', None) or ())
+
+ if add_version_option:
+ params.append(version_option)
+
+ AppGroup.__init__(self, params=params, **extra)
+ self.create_app = create_app
+
+ if add_default_commands:
+ self.add_command(run_command)
+ self.add_command(shell_command)
+
+ self._loaded_plugin_commands = False
+
+ def _load_plugin_commands(self):
+ if self._loaded_plugin_commands:
+ return
+ try:
+ import pkg_resources
+ except ImportError:
+ self._loaded_plugin_commands = True
+ return
+
+ for ep in pkg_resources.iter_entry_points('flask.commands'):
+ self.add_command(ep.load(), ep.name)
+ self._loaded_plugin_commands = True
+
+ def get_command(self, ctx, name):
+ self._load_plugin_commands()
+
+ # We load built-in commands first as these should always be the
+ # same no matter what the app does. If the app does want to
+ # override this it needs to make a custom instance of this group
+ # and not attach the default commands.
+ #
+ # This also means that the script stays functional in case the
+ # application completely fails.
+ rv = AppGroup.get_command(self, ctx, name)
+ if rv is not None:
+ return rv
+
+ info = ctx.ensure_object(ScriptInfo)
+ try:
+ rv = info.load_app().cli.get_command(ctx, name)
+ if rv is not None:
+ return rv
+ except NoAppException:
+ pass
+
+ def list_commands(self, ctx):
+ self._load_plugin_commands()
+
+ # The commands available is the list of both the application (if
+ # available) plus the builtin commands.
+ rv = set(click.Group.list_commands(self, ctx))
+ info = ctx.ensure_object(ScriptInfo)
+ try:
+ rv.update(info.load_app().cli.list_commands(ctx))
+ except Exception:
+ # Here we intentionally swallow all exceptions as we don't
+ # want the help page to break if the app does not exist.
+ # If someone attempts to use the command we try to create
+ # the app again and this will give us the error.
+ pass
+ return sorted(rv)
+
+ def main(self, *args, **kwargs):
+ obj = kwargs.get('obj')
+ if obj is None:
+ obj = ScriptInfo(create_app=self.create_app)
+ kwargs['obj'] = obj
+ kwargs.setdefault('auto_envvar_prefix', 'FLASK')
+ return AppGroup.main(self, *args, **kwargs)
+
+
+@click.command('run', short_help='Runs a development server.')
+@click.option('--host', '-h', default='127.0.0.1',
+ help='The interface to bind to.')
+@click.option('--port', '-p', default=5000,
+ help='The port to bind to.')
+@click.option('--reload/--no-reload', default=None,
+ help='Enable or disable the reloader. By default the reloader '
+ 'is active if debug is enabled.')
+@click.option('--debugger/--no-debugger', default=None,
+ help='Enable or disable the debugger. By default the debugger '
+ 'is active if debug is enabled.')
+@click.option('--eager-loading/--lazy-loader', default=None,
+ help='Enable or disable eager loading. By default eager '
+ 'loading is enabled if the reloader is disabled.')
+@click.option('--with-threads/--without-threads', default=False,
+ help='Enable or disable multithreading.')
+@pass_script_info
+def run_command(info, host, port, reload, debugger, eager_loading,
+ with_threads):
+ """Runs a local development server for the Flask application.
+
+ This local server is recommended for development purposes only but it
+ can also be used for simple intranet deployments. By default it will
+ not support any sort of concurrency at all to simplify debugging. This
+ can be changed with the --with-threads option which will enable basic
+ multithreading.
+
+ The reloader and debugger are by default enabled if the debug flag of
+ Flask is enabled and disabled otherwise.
+ """
+ from werkzeug.serving import run_simple
+
+ debug = get_debug_flag()
+ if reload is None:
+ reload = bool(debug)
+ if debugger is None:
+ debugger = bool(debug)
+ if eager_loading is None:
+ eager_loading = not reload
+
+ app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
+
+ # Extra startup messages. This depends a bit on Werkzeug internals to
+ # not double execute when the reloader kicks in.
+ if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
+ # If we have an import path we can print it out now which can help
+ # people understand what's being served. If we do not have an
+ # import path because the app was loaded through a callback then
+ # we won't print anything.
+ if info.app_import_path is not None:
+ print(' * Serving Flask app "%s"' % info.app_import_path)
+ if debug is not None:
+ print(' * Forcing debug mode %s' % (debug and 'on' or 'off'))
+
+ run_simple(host, port, app, use_reloader=reload,
+ use_debugger=debugger, threaded=with_threads)
+
+
+@click.command('shell', short_help='Runs a shell in the app context.')
+@with_appcontext
+def shell_command():
+ """Runs an interactive Python shell in the context of a given
+ Flask application. The application will populate the default
+ namespace of this shell according to it's configuration.
+
+ This is useful for executing small snippets of management code
+ without having to manually configuring the application.
+ """
+ import code
+ from flask.globals import _app_ctx_stack
+ app = _app_ctx_stack.top.app
+ banner = 'Python %s on %s\nApp: %s%s\nInstance: %s' % (
+ sys.version,
+ sys.platform,
+ app.import_name,
+ app.debug and ' [debug]' or '',
+ app.instance_path,
+ )
+ ctx = {}
+
+ # Support the regular Python interpreter startup script if someone
+ # is using it.
+ startup = os.environ.get('PYTHONSTARTUP')
+ if startup and os.path.isfile(startup):
+ with open(startup, 'r') as f:
+ eval(compile(f.read(), startup, 'exec'), ctx)
+
+ ctx.update(app.make_shell_context())
+
+ code.interact(banner=banner, local=ctx)
+
+
+cli = FlaskGroup(help="""\
+This shell command acts as general utility script for Flask applications.
+
+It loads the application configured (through the FLASK_APP environment
+variable) and then provides commands either provided by the application or
+Flask itself.
+
+The most useful commands are the "run" and "shell" command.
+
+Example usage:
+
+\b
+ %(prefix)s%(cmd)s FLASK_APP=hello.py
+ %(prefix)s%(cmd)s FLASK_DEBUG=1
+ %(prefix)sflask run
+""" % {
+ 'cmd': os.name == 'posix' and 'export' or 'set',
+ 'prefix': os.name == 'posix' and '$ ' or '',
+})
+
+
+def main(as_module=False):
+ this_module = __package__ + '.cli'
+ args = sys.argv[1:]
+
+ if as_module:
+ if sys.version_info >= (2, 7):
+ name = 'python -m ' + this_module.rsplit('.', 1)[0]
+ else:
+ name = 'python -m ' + this_module
+
+ # This module is always executed as "python -m flask.run" and as such
+ # we need to ensure that we restore the actual command line so that
+ # the reloader can properly operate.
+ sys.argv = ['-m', this_module] + sys.argv[1:]
+ else:
+ name = None
+
+ cli.main(args=args, prog_name=name)
+
+
+if __name__ == '__main__':
+ main(as_module=True)
diff --git a/service2/flask/config.py b/service2/flask/config.py
new file mode 100644
index 0000000..697add7
--- /dev/null
+++ b/service2/flask/config.py
@@ -0,0 +1,263 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.config
+ ~~~~~~~~~~~~
+
+ Implements the configuration related objects.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import types
+import errno
+
+from werkzeug.utils import import_string
+from ._compat import string_types, iteritems
+from . import json
+
+
+class ConfigAttribute(object):
+ """Makes an attribute forward to the config"""
+
+ def __init__(self, name, get_converter=None):
+ self.__name__ = name
+ self.get_converter = get_converter
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ rv = obj.config[self.__name__]
+ if self.get_converter is not None:
+ rv = self.get_converter(rv)
+ return rv
+
+ def __set__(self, obj, value):
+ obj.config[self.__name__] = value
+
+
+class Config(dict):
+ """Works exactly like a dict but provides ways to fill it from files
+ or special dictionaries. There are two common patterns to populate the
+ config.
+
+ Either you can fill the config from a config file::
+
+ app.config.from_pyfile('yourconfig.cfg')
+
+ Or alternatively you can define the configuration options in the
+ module that calls :meth:`from_object` or provide an import path to
+ a module that should be loaded. It is also possible to tell it to
+ use the same module and with that provide the configuration values
+ just before the call::
+
+ DEBUG = True
+ SECRET_KEY = 'development key'
+ app.config.from_object(__name__)
+
+ In both cases (loading from any Python file or loading from modules),
+ only uppercase keys are added to the config. This makes it possible to use
+ lowercase values in the config file for temporary values that are not added
+ to the config or to define the config keys in the same file that implements
+ the application.
+
+ Probably the most interesting way to load configurations is from an
+ environment variable pointing to a file::
+
+ app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+ In this case before launching the application you have to set this
+ environment variable to the file you want to use. On Linux and OS X
+ use the export statement::
+
+ export YOURAPPLICATION_SETTINGS='/path/to/config/file'
+
+ On windows use `set` instead.
+
+ :param root_path: path to which files are read relative from. When the
+ config object is created by the application, this is
+ the application's :attr:`~flask.Flask.root_path`.
+ :param defaults: an optional dictionary of default values
+ """
+
+ def __init__(self, root_path, defaults=None):
+ dict.__init__(self, defaults or {})
+ self.root_path = root_path
+
+ def from_envvar(self, variable_name, silent=False):
+ """Loads a configuration from an environment variable pointing to
+ a configuration file. This is basically just a shortcut with nicer
+ error messages for this line of code::
+
+ app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
+
+ :param variable_name: name of the environment variable
+ :param silent: set to ``True`` if you want silent failure for missing
+ files.
+ :return: bool. ``True`` if able to load config, ``False`` otherwise.
+ """
+ rv = os.environ.get(variable_name)
+ if not rv:
+ if silent:
+ return False
+ raise RuntimeError('The environment variable %r is not set '
+ 'and as such configuration could not be '
+ 'loaded. Set this variable and make it '
+ 'point to a configuration file' %
+ variable_name)
+ return self.from_pyfile(rv, silent=silent)
+
+ def from_pyfile(self, filename, silent=False):
+ """Updates the values in the config from a Python file. This function
+ behaves as if the file was imported as module with the
+ :meth:`from_object` function.
+
+ :param filename: the filename of the config. This can either be an
+ absolute filename or a filename relative to the
+ root path.
+ :param silent: set to ``True`` if you want silent failure for missing
+ files.
+
+ .. versionadded:: 0.7
+ `silent` parameter.
+ """
+ filename = os.path.join(self.root_path, filename)
+ d = types.ModuleType('config')
+ d.__file__ = filename
+ try:
+ with open(filename, mode='rb') as config_file:
+ exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
+ except IOError as e:
+ if silent and e.errno in (errno.ENOENT, errno.EISDIR):
+ return False
+ e.strerror = 'Unable to load configuration file (%s)' % e.strerror
+ raise
+ self.from_object(d)
+ return True
+
+ def from_object(self, obj):
+ """Updates the values from the given object. An object can be of one
+ of the following two types:
+
+ - a string: in this case the object with that name will be imported
+ - an actual object reference: that object is used directly
+
+ Objects are usually either modules or classes. :meth:`from_object`
+ loads only the uppercase attributes of the module/class. A ``dict``
+ object will not work with :meth:`from_object` because the keys of a
+ ``dict`` are not attributes of the ``dict`` class.
+
+ Example of module-based configuration::
+
+ app.config.from_object('yourapplication.default_config')
+ from yourapplication import default_config
+ app.config.from_object(default_config)
+
+ You should not use this function to load the actual configuration but
+ rather configuration defaults. The actual config should be loaded
+ with :meth:`from_pyfile` and ideally from a location not within the
+ package because the package might be installed system wide.
+
+ See :ref:`config-dev-prod` for an example of class-based configuration
+ using :meth:`from_object`.
+
+ :param obj: an import name or object
+ """
+ if isinstance(obj, string_types):
+ obj = import_string(obj)
+ for key in dir(obj):
+ if key.isupper():
+ self[key] = getattr(obj, key)
+
+ def from_json(self, filename, silent=False):
+ """Updates the values in the config from a JSON file. This function
+ behaves as if the JSON object was a dictionary and passed to the
+ :meth:`from_mapping` function.
+
+ :param filename: the filename of the JSON file. This can either be an
+ absolute filename or a filename relative to the
+ root path.
+ :param silent: set to ``True`` if you want silent failure for missing
+ files.
+
+ .. versionadded:: 0.11
+ """
+ filename = os.path.join(self.root_path, filename)
+
+ try:
+ with open(filename) as json_file:
+ obj = json.loads(json_file.read())
+ except IOError as e:
+ if silent and e.errno in (errno.ENOENT, errno.EISDIR):
+ return False
+ e.strerror = 'Unable to load configuration file (%s)' % e.strerror
+ raise
+ return self.from_mapping(obj)
+
+ def from_mapping(self, *mapping, **kwargs):
+ """Updates the config like :meth:`update` ignoring items with non-upper
+ keys.
+
+ .. versionadded:: 0.11
+ """
+ mappings = []
+ if len(mapping) == 1:
+ if hasattr(mapping[0], 'items'):
+ mappings.append(mapping[0].items())
+ else:
+ mappings.append(mapping[0])
+ elif len(mapping) > 1:
+ raise TypeError(
+ 'expected at most 1 positional argument, got %d' % len(mapping)
+ )
+ mappings.append(kwargs.items())
+ for mapping in mappings:
+ for (key, value) in mapping:
+ if key.isupper():
+ self[key] = value
+ return True
+
+ def get_namespace(self, namespace, lowercase=True, trim_namespace=True):
+ """Returns a dictionary containing a subset of configuration options
+ that match the specified namespace/prefix. Example usage::
+
+ app.config['IMAGE_STORE_TYPE'] = 'fs'
+ app.config['IMAGE_STORE_PATH'] = '/var/app/images'
+ app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
+ image_store_config = app.config.get_namespace('IMAGE_STORE_')
+
+ The resulting dictionary `image_store_config` would look like::
+
+ {
+ 'type': 'fs',
+ 'path': '/var/app/images',
+ 'base_url': 'http://img.website.com'
+ }
+
+ This is often useful when configuration options map directly to
+ keyword arguments in functions or class constructors.
+
+ :param namespace: a configuration namespace
+ :param lowercase: a flag indicating if the keys of the resulting
+ dictionary should be lowercase
+ :param trim_namespace: a flag indicating if the keys of the resulting
+ dictionary should not include the namespace
+
+ .. versionadded:: 0.11
+ """
+ rv = {}
+ for k, v in iteritems(self):
+ if not k.startswith(namespace):
+ continue
+ if trim_namespace:
+ key = k[len(namespace):]
+ else:
+ key = k
+ if lowercase:
+ key = key.lower()
+ rv[key] = v
+ return rv
+
+ def __repr__(self):
+ return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
diff --git a/service2/flask/ctx.py b/service2/flask/ctx.py
new file mode 100644
index 0000000..480d9c5
--- /dev/null
+++ b/service2/flask/ctx.py
@@ -0,0 +1,410 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.ctx
+ ~~~~~~~~~
+
+ Implements the objects required to keep the context.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import sys
+from functools import update_wrapper
+
+from werkzeug.exceptions import HTTPException
+
+from .globals import _request_ctx_stack, _app_ctx_stack
+from .signals import appcontext_pushed, appcontext_popped
+from ._compat import BROKEN_PYPY_CTXMGR_EXIT, reraise
+
+
+# a singleton sentinel value for parameter defaults
+_sentinel = object()
+
+
+class _AppCtxGlobals(object):
+ """A plain object."""
+
+ def get(self, name, default=None):
+ return self.__dict__.get(name, default)
+
+ def pop(self, name, default=_sentinel):
+ if default is _sentinel:
+ return self.__dict__.pop(name)
+ else:
+ return self.__dict__.pop(name, default)
+
+ def setdefault(self, name, default=None):
+ return self.__dict__.setdefault(name, default)
+
+ def __contains__(self, item):
+ return item in self.__dict__
+
+ def __iter__(self):
+ return iter(self.__dict__)
+
+ def __repr__(self):
+ top = _app_ctx_stack.top
+ if top is not None:
+ return '' % top.app.name
+ return object.__repr__(self)
+
+
+def after_this_request(f):
+ """Executes a function after this request. This is useful to modify
+ response objects. The function is passed the response object and has
+ to return the same or a new one.
+
+ Example::
+
+ @app.route('/')
+ def index():
+ @after_this_request
+ def add_header(response):
+ response.headers['X-Foo'] = 'Parachute'
+ return response
+ return 'Hello World!'
+
+ This is more useful if a function other than the view function wants to
+ modify a response. For instance think of a decorator that wants to add
+ some headers without converting the return value into a response object.
+
+ .. versionadded:: 0.9
+ """
+ _request_ctx_stack.top._after_request_functions.append(f)
+ return f
+
+
+def copy_current_request_context(f):
+ """A helper function that decorates a function to retain the current
+ request context. This is useful when working with greenlets. The moment
+ the function is decorated a copy of the request context is created and
+ then pushed when the function is called.
+
+ Example::
+
+ import gevent
+ from flask import copy_current_request_context
+
+ @app.route('/')
+ def index():
+ @copy_current_request_context
+ def do_some_work():
+ # do some work here, it can access flask.request like you
+ # would otherwise in the view function.
+ ...
+ gevent.spawn(do_some_work)
+ return 'Regular response'
+
+ .. versionadded:: 0.10
+ """
+ top = _request_ctx_stack.top
+ if top is None:
+ raise RuntimeError('This decorator can only be used at local scopes '
+ 'when a request context is on the stack. For instance within '
+ 'view functions.')
+ reqctx = top.copy()
+ def wrapper(*args, **kwargs):
+ with reqctx:
+ return f(*args, **kwargs)
+ return update_wrapper(wrapper, f)
+
+
+def has_request_context():
+ """If you have code that wants to test if a request context is there or
+ not this function can be used. For instance, you may want to take advantage
+ of request information if the request object is available, but fail
+ silently if it is unavailable.
+
+ ::
+
+ class User(db.Model):
+
+ def __init__(self, username, remote_addr=None):
+ self.username = username
+ if remote_addr is None and has_request_context():
+ remote_addr = request.remote_addr
+ self.remote_addr = remote_addr
+
+ Alternatively you can also just test any of the context bound objects
+ (such as :class:`request` or :class:`g` for truthness)::
+
+ class User(db.Model):
+
+ def __init__(self, username, remote_addr=None):
+ self.username = username
+ if remote_addr is None and request:
+ remote_addr = request.remote_addr
+ self.remote_addr = remote_addr
+
+ .. versionadded:: 0.7
+ """
+ return _request_ctx_stack.top is not None
+
+
+def has_app_context():
+ """Works like :func:`has_request_context` but for the application
+ context. You can also just do a boolean check on the
+ :data:`current_app` object instead.
+
+ .. versionadded:: 0.9
+ """
+ return _app_ctx_stack.top is not None
+
+
+class AppContext(object):
+ """The application context binds an application object implicitly
+ to the current thread or greenlet, similar to how the
+ :class:`RequestContext` binds request information. The application
+ context is also implicitly created if a request context is created
+ but the application is not on top of the individual application
+ context.
+ """
+
+ def __init__(self, app):
+ self.app = app
+ self.url_adapter = app.create_url_adapter(None)
+ self.g = app.app_ctx_globals_class()
+
+ # Like request context, app contexts can be pushed multiple times
+ # but there a basic "refcount" is enough to track them.
+ self._refcnt = 0
+
+ def push(self):
+ """Binds the app context to the current context."""
+ self._refcnt += 1
+ if hasattr(sys, 'exc_clear'):
+ sys.exc_clear()
+ _app_ctx_stack.push(self)
+ appcontext_pushed.send(self.app)
+
+ def pop(self, exc=_sentinel):
+ """Pops the app context."""
+ try:
+ self._refcnt -= 1
+ if self._refcnt <= 0:
+ if exc is _sentinel:
+ exc = sys.exc_info()[1]
+ self.app.do_teardown_appcontext(exc)
+ finally:
+ rv = _app_ctx_stack.pop()
+ assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
+ % (rv, self)
+ appcontext_popped.send(self.app)
+
+ def __enter__(self):
+ self.push()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ self.pop(exc_value)
+
+ if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
+ reraise(exc_type, exc_value, tb)
+
+
+class RequestContext(object):
+ """The request context contains all request relevant information. It is
+ created at the beginning of the request and pushed to the
+ `_request_ctx_stack` and removed at the end of it. It will create the
+ URL adapter and request object for the WSGI environment provided.
+
+ Do not attempt to use this class directly, instead use
+ :meth:`~flask.Flask.test_request_context` and
+ :meth:`~flask.Flask.request_context` to create this object.
+
+ When the request context is popped, it will evaluate all the
+ functions registered on the application for teardown execution
+ (:meth:`~flask.Flask.teardown_request`).
+
+ The request context is automatically popped at the end of the request
+ for you. In debug mode the request context is kept around if
+ exceptions happen so that interactive debuggers have a chance to
+ introspect the data. With 0.4 this can also be forced for requests
+ that did not fail and outside of ``DEBUG`` mode. By setting
+ ``'flask._preserve_context'`` to ``True`` on the WSGI environment the
+ context will not pop itself at the end of the request. This is used by
+ the :meth:`~flask.Flask.test_client` for example to implement the
+ deferred cleanup functionality.
+
+ You might find this helpful for unittests where you need the
+ information from the context local around for a little longer. Make
+ sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in
+ that situation, otherwise your unittests will leak memory.
+ """
+
+ def __init__(self, app, environ, request=None):
+ self.app = app
+ if request is None:
+ request = app.request_class(environ)
+ self.request = request
+ self.url_adapter = app.create_url_adapter(self.request)
+ self.flashes = None
+ self.session = None
+
+ # Request contexts can be pushed multiple times and interleaved with
+ # other request contexts. Now only if the last level is popped we
+ # get rid of them. Additionally if an application context is missing
+ # one is created implicitly so for each level we add this information
+ self._implicit_app_ctx_stack = []
+
+ # indicator if the context was preserved. Next time another context
+ # is pushed the preserved context is popped.
+ self.preserved = False
+
+ # remembers the exception for pop if there is one in case the context
+ # preservation kicks in.
+ self._preserved_exc = None
+
+ # Functions that should be executed after the request on the response
+ # object. These will be called before the regular "after_request"
+ # functions.
+ self._after_request_functions = []
+
+ self.match_request()
+
+ def _get_g(self):
+ return _app_ctx_stack.top.g
+ def _set_g(self, value):
+ _app_ctx_stack.top.g = value
+ g = property(_get_g, _set_g)
+ del _get_g, _set_g
+
+ def copy(self):
+ """Creates a copy of this request context with the same request object.
+ This can be used to move a request context to a different greenlet.
+ Because the actual request object is the same this cannot be used to
+ move a request context to a different thread unless access to the
+ request object is locked.
+
+ .. versionadded:: 0.10
+ """
+ return self.__class__(self.app,
+ environ=self.request.environ,
+ request=self.request
+ )
+
+ def match_request(self):
+ """Can be overridden by a subclass to hook into the matching
+ of the request.
+ """
+ try:
+ url_rule, self.request.view_args = \
+ self.url_adapter.match(return_rule=True)
+ self.request.url_rule = url_rule
+ except HTTPException as e:
+ self.request.routing_exception = e
+
+ def push(self):
+ """Binds the request context to the current context."""
+ # If an exception occurs in debug mode or if context preservation is
+ # activated under exception situations exactly one context stays
+ # on the stack. The rationale is that you want to access that
+ # information under debug situations. However if someone forgets to
+ # pop that context again we want to make sure that on the next push
+ # it's invalidated, otherwise we run at risk that something leaks
+ # memory. This is usually only a problem in test suite since this
+ # functionality is not active in production environments.
+ top = _request_ctx_stack.top
+ if top is not None and top.preserved:
+ top.pop(top._preserved_exc)
+
+ # Before we push the request context we have to ensure that there
+ # is an application context.
+ app_ctx = _app_ctx_stack.top
+ if app_ctx is None or app_ctx.app != self.app:
+ app_ctx = self.app.app_context()
+ app_ctx.push()
+ self._implicit_app_ctx_stack.append(app_ctx)
+ else:
+ self._implicit_app_ctx_stack.append(None)
+
+ if hasattr(sys, 'exc_clear'):
+ sys.exc_clear()
+
+ _request_ctx_stack.push(self)
+
+ # Open the session at the moment that the request context is
+ # available. This allows a custom open_session method to use the
+ # request context (e.g. code that access database information
+ # stored on `g` instead of the appcontext).
+ self.session = self.app.open_session(self.request)
+ if self.session is None:
+ self.session = self.app.make_null_session()
+
+ def pop(self, exc=_sentinel):
+ """Pops the request context and unbinds it by doing that. This will
+ also trigger the execution of functions registered by the
+ :meth:`~flask.Flask.teardown_request` decorator.
+
+ .. versionchanged:: 0.9
+ Added the `exc` argument.
+ """
+ app_ctx = self._implicit_app_ctx_stack.pop()
+
+ try:
+ clear_request = False
+ if not self._implicit_app_ctx_stack:
+ self.preserved = False
+ self._preserved_exc = None
+ if exc is _sentinel:
+ exc = sys.exc_info()[1]
+ self.app.do_teardown_request(exc)
+
+ # If this interpreter supports clearing the exception information
+ # we do that now. This will only go into effect on Python 2.x,
+ # on 3.x it disappears automatically at the end of the exception
+ # stack.
+ if hasattr(sys, 'exc_clear'):
+ sys.exc_clear()
+
+ request_close = getattr(self.request, 'close', None)
+ if request_close is not None:
+ request_close()
+ clear_request = True
+ finally:
+ rv = _request_ctx_stack.pop()
+
+ # get rid of circular dependencies at the end of the request
+ # so that we don't require the GC to be active.
+ if clear_request:
+ rv.request.environ['werkzeug.request'] = None
+
+ # Get rid of the app as well if necessary.
+ if app_ctx is not None:
+ app_ctx.pop(exc)
+
+ assert rv is self, 'Popped wrong request context. ' \
+ '(%r instead of %r)' % (rv, self)
+
+ def auto_pop(self, exc):
+ if self.request.environ.get('flask._preserve_context') or \
+ (exc is not None and self.app.preserve_context_on_exception):
+ self.preserved = True
+ self._preserved_exc = exc
+ else:
+ self.pop(exc)
+
+ def __enter__(self):
+ self.push()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ # do not pop the request stack if we are in debug mode and an
+ # exception happened. This will allow the debugger to still
+ # access the request object in the interactive shell. Furthermore
+ # the context can be force kept alive for the test client.
+ # See flask.testing for how this works.
+ self.auto_pop(exc_value)
+
+ if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:
+ reraise(exc_type, exc_value, tb)
+
+ def __repr__(self):
+ return '<%s \'%s\' [%s] of %s>' % (
+ self.__class__.__name__,
+ self.request.url,
+ self.request.method,
+ self.app.name,
+ )
diff --git a/service2/flask/debughelpers.py b/service2/flask/debughelpers.py
new file mode 100644
index 0000000..90710dd
--- /dev/null
+++ b/service2/flask/debughelpers.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.debughelpers
+ ~~~~~~~~~~~~~~~~~~
+
+ Various helpers to make the development experience better.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from ._compat import implements_to_string, text_type
+from .app import Flask
+from .blueprints import Blueprint
+from .globals import _request_ctx_stack
+
+
+class UnexpectedUnicodeError(AssertionError, UnicodeError):
+ """Raised in places where we want some better error reporting for
+ unexpected unicode or binary data.
+ """
+
+
+@implements_to_string
+class DebugFilesKeyError(KeyError, AssertionError):
+ """Raised from request.files during debugging. The idea is that it can
+ provide a better error message than just a generic KeyError/BadRequest.
+ """
+
+ def __init__(self, request, key):
+ form_matches = request.form.getlist(key)
+ buf = ['You tried to access the file "%s" in the request.files '
+ 'dictionary but it does not exist. The mimetype for the request '
+ 'is "%s" instead of "multipart/form-data" which means that no '
+ 'file contents were transmitted. To fix this error you should '
+ 'provide enctype="multipart/form-data" in your form.' %
+ (key, request.mimetype)]
+ if form_matches:
+ buf.append('\n\nThe browser instead transmitted some file names. '
+ 'This was submitted: %s' % ', '.join('"%s"' % x
+ for x in form_matches))
+ self.msg = ''.join(buf)
+
+ def __str__(self):
+ return self.msg
+
+
+class FormDataRoutingRedirect(AssertionError):
+ """This exception is raised by Flask in debug mode if it detects a
+ redirect caused by the routing system when the request method is not
+ GET, HEAD or OPTIONS. Reasoning: form data will be dropped.
+ """
+
+ def __init__(self, request):
+ exc = request.routing_exception
+ buf = ['A request was sent to this URL (%s) but a redirect was '
+ 'issued automatically by the routing system to "%s".'
+ % (request.url, exc.new_url)]
+
+ # In case just a slash was appended we can be extra helpful
+ if request.base_url + '/' == exc.new_url.split('?')[0]:
+ buf.append(' The URL was defined with a trailing slash so '
+ 'Flask will automatically redirect to the URL '
+ 'with the trailing slash if it was accessed '
+ 'without one.')
+
+ buf.append(' Make sure to directly send your %s-request to this URL '
+ 'since we can\'t make browsers or HTTP clients redirect '
+ 'with form data reliably or without user interaction.' %
+ request.method)
+ buf.append('\n\nNote: this exception is only raised in debug mode')
+ AssertionError.__init__(self, ''.join(buf).encode('utf-8'))
+
+
+def attach_enctype_error_multidict(request):
+ """Since Flask 0.8 we're monkeypatching the files object in case a
+ request is detected that does not use multipart form data but the files
+ object is accessed.
+ """
+ oldcls = request.files.__class__
+ class newcls(oldcls):
+ def __getitem__(self, key):
+ try:
+ return oldcls.__getitem__(self, key)
+ except KeyError:
+ if key not in request.form:
+ raise
+ raise DebugFilesKeyError(request, key)
+ newcls.__name__ = oldcls.__name__
+ newcls.__module__ = oldcls.__module__
+ request.files.__class__ = newcls
+
+
+def _dump_loader_info(loader):
+ yield 'class: %s.%s' % (type(loader).__module__, type(loader).__name__)
+ for key, value in sorted(loader.__dict__.items()):
+ if key.startswith('_'):
+ continue
+ if isinstance(value, (tuple, list)):
+ if not all(isinstance(x, (str, text_type)) for x in value):
+ continue
+ yield '%s:' % key
+ for item in value:
+ yield ' - %s' % item
+ continue
+ elif not isinstance(value, (str, text_type, int, float, bool)):
+ continue
+ yield '%s: %r' % (key, value)
+
+
+def explain_template_loading_attempts(app, template, attempts):
+ """This should help developers understand what failed"""
+ info = ['Locating template "%s":' % template]
+ total_found = 0
+ blueprint = None
+ reqctx = _request_ctx_stack.top
+ if reqctx is not None and reqctx.request.blueprint is not None:
+ blueprint = reqctx.request.blueprint
+
+ for idx, (loader, srcobj, triple) in enumerate(attempts):
+ if isinstance(srcobj, Flask):
+ src_info = 'application "%s"' % srcobj.import_name
+ elif isinstance(srcobj, Blueprint):
+ src_info = 'blueprint "%s" (%s)' % (srcobj.name,
+ srcobj.import_name)
+ else:
+ src_info = repr(srcobj)
+
+ info.append('% 5d: trying loader of %s' % (
+ idx + 1, src_info))
+
+ for line in _dump_loader_info(loader):
+ info.append(' %s' % line)
+
+ if triple is None:
+ detail = 'no match'
+ else:
+ detail = 'found (%r)' % (triple[1] or '')
+ total_found += 1
+ info.append(' -> %s' % detail)
+
+ seems_fishy = False
+ if total_found == 0:
+ info.append('Error: the template could not be found.')
+ seems_fishy = True
+ elif total_found > 1:
+ info.append('Warning: multiple loaders returned a match for the template.')
+ seems_fishy = True
+
+ if blueprint is not None and seems_fishy:
+ info.append(' The template was looked up from an endpoint that '
+ 'belongs to the blueprint "%s".' % blueprint)
+ info.append(' Maybe you did not place a template in the right folder?')
+ info.append(' See http://flask.pocoo.org/docs/blueprints/#templates')
+
+ app.logger.info('\n'.join(info))
diff --git a/service2/flask/ext/__init__.py b/service2/flask/ext/__init__.py
new file mode 100644
index 0000000..051f44a
--- /dev/null
+++ b/service2/flask/ext/__init__.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.ext
+ ~~~~~~~~~
+
+ Redirect imports for extensions. This module basically makes it possible
+ for us to transition from flaskext.foo to flask_foo without having to
+ force all extensions to upgrade at the same time.
+
+ When a user does ``from flask.ext.foo import bar`` it will attempt to
+ import ``from flask_foo import bar`` first and when that fails it will
+ try to import ``from flaskext.foo import bar``.
+
+ We're switching from namespace packages because it was just too painful for
+ everybody involved.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+
+def setup():
+ from ..exthook import ExtensionImporter
+ importer = ExtensionImporter(['flask_%s', 'flaskext.%s'], __name__)
+ importer.install()
+
+
+setup()
+del setup
diff --git a/service2/flask/exthook.py b/service2/flask/exthook.py
new file mode 100644
index 0000000..d884280
--- /dev/null
+++ b/service2/flask/exthook.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.exthook
+ ~~~~~~~~~~~~~
+
+ Redirect imports for extensions. This module basically makes it possible
+ for us to transition from flaskext.foo to flask_foo without having to
+ force all extensions to upgrade at the same time.
+
+ When a user does ``from flask.ext.foo import bar`` it will attempt to
+ import ``from flask_foo import bar`` first and when that fails it will
+ try to import ``from flaskext.foo import bar``.
+
+ We're switching from namespace packages because it was just too painful for
+ everybody involved.
+
+ This is used by `flask.ext`.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import os
+import warnings
+from ._compat import reraise
+
+
+class ExtDeprecationWarning(DeprecationWarning):
+ pass
+
+warnings.simplefilter('always', ExtDeprecationWarning)
+
+
+class ExtensionImporter(object):
+ """This importer redirects imports from this submodule to other locations.
+ This makes it possible to transition from the old flaskext.name to the
+ newer flask_name without people having a hard time.
+ """
+
+ def __init__(self, module_choices, wrapper_module):
+ self.module_choices = module_choices
+ self.wrapper_module = wrapper_module
+ self.prefix = wrapper_module + '.'
+ self.prefix_cutoff = wrapper_module.count('.') + 1
+
+ def __eq__(self, other):
+ return self.__class__.__module__ == other.__class__.__module__ and \
+ self.__class__.__name__ == other.__class__.__name__ and \
+ self.wrapper_module == other.wrapper_module and \
+ self.module_choices == other.module_choices
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def install(self):
+ sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self]
+
+ def find_module(self, fullname, path=None):
+ if fullname.startswith(self.prefix) and \
+ fullname != 'flask.ext.ExtDeprecationWarning':
+ return self
+
+ def load_module(self, fullname):
+ if fullname in sys.modules:
+ return sys.modules[fullname]
+
+ modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff]
+
+ warnings.warn(
+ "Importing flask.ext.{x} is deprecated, use flask_{x} instead."
+ .format(x=modname), ExtDeprecationWarning, stacklevel=2
+ )
+
+ for path in self.module_choices:
+ realname = path % modname
+ try:
+ __import__(realname)
+ except ImportError:
+ exc_type, exc_value, tb = sys.exc_info()
+ # since we only establish the entry in sys.modules at the
+ # very this seems to be redundant, but if recursive imports
+ # happen we will call into the move import a second time.
+ # On the second invocation we still don't have an entry for
+ # fullname in sys.modules, but we will end up with the same
+ # fake module name and that import will succeed since this
+ # one already has a temporary entry in the modules dict.
+ # Since this one "succeeded" temporarily that second
+ # invocation now will have created a fullname entry in
+ # sys.modules which we have to kill.
+ sys.modules.pop(fullname, None)
+
+ # If it's an important traceback we reraise it, otherwise
+ # we swallow it and try the next choice. The skipped frame
+ # is the one from __import__ above which we don't care about
+ if self.is_important_traceback(realname, tb):
+ reraise(exc_type, exc_value, tb.tb_next)
+ continue
+ module = sys.modules[fullname] = sys.modules[realname]
+ if '.' not in modname:
+ setattr(sys.modules[self.wrapper_module], modname, module)
+
+ if realname.startswith('flaskext.'):
+ warnings.warn(
+ "Detected extension named flaskext.{x}, please rename it "
+ "to flask_{x}. The old form is deprecated."
+ .format(x=modname), ExtDeprecationWarning
+ )
+
+ return module
+ raise ImportError('No module named %s' % fullname)
+
+ def is_important_traceback(self, important_module, tb):
+ """Walks a traceback's frames and checks if any of the frames
+ originated in the given important module. If that is the case then we
+ were able to import the module itself but apparently something went
+ wrong when the module was imported. (Eg: import of an import failed).
+ """
+ while tb is not None:
+ if self.is_important_frame(important_module, tb):
+ return True
+ tb = tb.tb_next
+ return False
+
+ def is_important_frame(self, important_module, tb):
+ """Checks a single frame if it's important."""
+ g = tb.tb_frame.f_globals
+ if '__name__' not in g:
+ return False
+
+ module_name = g['__name__']
+
+ # Python 2.7 Behavior. Modules are cleaned up late so the
+ # name shows up properly here. Success!
+ if module_name == important_module:
+ return True
+
+ # Some python versions will clean up modules so early that the
+ # module name at that point is no longer set. Try guessing from
+ # the filename then.
+ filename = os.path.abspath(tb.tb_frame.f_code.co_filename)
+ test_string = os.path.sep + important_module.replace('.', os.path.sep)
+ return test_string + '.py' in filename or \
+ test_string + os.path.sep + '__init__.py' in filename
diff --git a/service2/flask/globals.py b/service2/flask/globals.py
new file mode 100644
index 0000000..0b70a3e
--- /dev/null
+++ b/service2/flask/globals.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.globals
+ ~~~~~~~~~~~~~
+
+ Defines all the global objects that are proxies to the current
+ active context.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from functools import partial
+from werkzeug.local import LocalStack, LocalProxy
+
+
+_request_ctx_err_msg = '''\
+Working outside of request context.
+
+This typically means that you attempted to use functionality that needed
+an active HTTP request. Consult the documentation on testing for
+information about how to avoid this problem.\
+'''
+_app_ctx_err_msg = '''\
+Working outside of application context.
+
+This typically means that you attempted to use functionality that needed
+to interface with the current application object in a way. To solve
+this set up an application context with app.app_context(). See the
+documentation for more information.\
+'''
+
+
+def _lookup_req_object(name):
+ top = _request_ctx_stack.top
+ if top is None:
+ raise RuntimeError(_request_ctx_err_msg)
+ return getattr(top, name)
+
+
+def _lookup_app_object(name):
+ top = _app_ctx_stack.top
+ if top is None:
+ raise RuntimeError(_app_ctx_err_msg)
+ return getattr(top, name)
+
+
+def _find_app():
+ top = _app_ctx_stack.top
+ if top is None:
+ raise RuntimeError(_app_ctx_err_msg)
+ return top.app
+
+
+# context locals
+_request_ctx_stack = LocalStack()
+_app_ctx_stack = LocalStack()
+current_app = LocalProxy(_find_app)
+request = LocalProxy(partial(_lookup_req_object, 'request'))
+session = LocalProxy(partial(_lookup_req_object, 'session'))
+g = LocalProxy(partial(_lookup_app_object, 'g'))
diff --git a/service2/flask/helpers.py b/service2/flask/helpers.py
new file mode 100644
index 0000000..4bb1d1c
--- /dev/null
+++ b/service2/flask/helpers.py
@@ -0,0 +1,966 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.helpers
+ ~~~~~~~~~~~~~
+
+ Implements various helpers.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+import sys
+import pkgutil
+import posixpath
+import mimetypes
+from time import time
+from zlib import adler32
+from threading import RLock
+from werkzeug.routing import BuildError
+from functools import update_wrapper
+
+try:
+ from werkzeug.urls import url_quote
+except ImportError:
+ from urlparse import quote as url_quote
+
+from werkzeug.datastructures import Headers, Range
+from werkzeug.exceptions import BadRequest, NotFound, \
+ RequestedRangeNotSatisfiable
+
+# this was moved in 0.7
+try:
+ from werkzeug.wsgi import wrap_file
+except ImportError:
+ from werkzeug.utils import wrap_file
+
+from jinja2 import FileSystemLoader
+
+from .signals import message_flashed
+from .globals import session, _request_ctx_stack, _app_ctx_stack, \
+ current_app, request
+from ._compat import string_types, text_type
+
+
+# sentinel
+_missing = object()
+
+
+# what separators does this operating system provide that are not a slash?
+# this is used by the send_from_directory function to ensure that nobody is
+# able to access files from outside the filesystem.
+_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
+ if sep not in (None, '/'))
+
+
+def get_debug_flag(default=None):
+ val = os.environ.get('FLASK_DEBUG')
+ if not val:
+ return default
+ return val not in ('0', 'false', 'no')
+
+
+def _endpoint_from_view_func(view_func):
+ """Internal helper that returns the default endpoint for a given
+ function. This always is the function name.
+ """
+ assert view_func is not None, 'expected view func if endpoint ' \
+ 'is not provided.'
+ return view_func.__name__
+
+
+def stream_with_context(generator_or_function):
+ """Request contexts disappear when the response is started on the server.
+ This is done for efficiency reasons and to make it less likely to encounter
+ memory leaks with badly written WSGI middlewares. The downside is that if
+ you are using streamed responses, the generator cannot access request bound
+ information any more.
+
+ This function however can help you keep the context around for longer::
+
+ from flask import stream_with_context, request, Response
+
+ @app.route('/stream')
+ def streamed_response():
+ @stream_with_context
+ def generate():
+ yield 'Hello '
+ yield request.args['name']
+ yield '!'
+ return Response(generate())
+
+ Alternatively it can also be used around a specific generator::
+
+ from flask import stream_with_context, request, Response
+
+ @app.route('/stream')
+ def streamed_response():
+ def generate():
+ yield 'Hello '
+ yield request.args['name']
+ yield '!'
+ return Response(stream_with_context(generate()))
+
+ .. versionadded:: 0.9
+ """
+ try:
+ gen = iter(generator_or_function)
+ except TypeError:
+ def decorator(*args, **kwargs):
+ gen = generator_or_function(*args, **kwargs)
+ return stream_with_context(gen)
+ return update_wrapper(decorator, generator_or_function)
+
+ def generator():
+ ctx = _request_ctx_stack.top
+ if ctx is None:
+ raise RuntimeError('Attempted to stream with context but '
+ 'there was no context in the first place to keep around.')
+ with ctx:
+ # Dummy sentinel. Has to be inside the context block or we're
+ # not actually keeping the context around.
+ yield None
+
+ # The try/finally is here so that if someone passes a WSGI level
+ # iterator in we're still running the cleanup logic. Generators
+ # don't need that because they are closed on their destruction
+ # automatically.
+ try:
+ for item in gen:
+ yield item
+ finally:
+ if hasattr(gen, 'close'):
+ gen.close()
+
+ # The trick is to start the generator. Then the code execution runs until
+ # the first dummy None is yielded at which point the context was already
+ # pushed. This item is discarded. Then when the iteration continues the
+ # real generator is executed.
+ wrapped_g = generator()
+ next(wrapped_g)
+ return wrapped_g
+
+
+def make_response(*args):
+ """Sometimes it is necessary to set additional headers in a view. Because
+ views do not have to return response objects but can return a value that
+ is converted into a response object by Flask itself, it becomes tricky to
+ add headers to it. This function can be called instead of using a return
+ and you will get a response object which you can use to attach headers.
+
+ If view looked like this and you want to add a new header::
+
+ def index():
+ return render_template('index.html', foo=42)
+
+ You can now do something like this::
+
+ def index():
+ response = make_response(render_template('index.html', foo=42))
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+ return response
+
+ This function accepts the very same arguments you can return from a
+ view function. This for example creates a response with a 404 error
+ code::
+
+ response = make_response(render_template('not_found.html'), 404)
+
+ The other use case of this function is to force the return value of a
+ view function into a response which is helpful with view
+ decorators::
+
+ response = make_response(view_function())
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+
+ Internally this function does the following things:
+
+ - if no arguments are passed, it creates a new response argument
+ - if one argument is passed, :meth:`flask.Flask.make_response`
+ is invoked with it.
+ - if more than one argument is passed, the arguments are passed
+ to the :meth:`flask.Flask.make_response` function as tuple.
+
+ .. versionadded:: 0.6
+ """
+ if not args:
+ return current_app.response_class()
+ if len(args) == 1:
+ args = args[0]
+ return current_app.make_response(args)
+
+
+def url_for(endpoint, **values):
+ """Generates a URL to the given endpoint with the method provided.
+
+ Variable arguments that are unknown to the target endpoint are appended
+ to the generated URL as query arguments. If the value of a query argument
+ is ``None``, the whole pair is skipped. In case blueprints are active
+ you can shortcut references to the same blueprint by prefixing the
+ local endpoint with a dot (``.``).
+
+ This will reference the index function local to the current blueprint::
+
+ url_for('.index')
+
+ For more information, head over to the :ref:`Quickstart `.
+
+ To integrate applications, :class:`Flask` has a hook to intercept URL build
+ errors through :attr:`Flask.url_build_error_handlers`. The `url_for`
+ function results in a :exc:`~werkzeug.routing.BuildError` when the current
+ app does not have a URL for the given endpoint and values. When it does, the
+ :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if
+ it is not ``None``, which can return a string to use as the result of
+ `url_for` (instead of `url_for`'s default to raise the
+ :exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
+ An example::
+
+ def external_url_handler(error, endpoint, values):
+ "Looks up an external URL when `url_for` cannot build a URL."
+ # This is an example of hooking the build_error_handler.
+ # Here, lookup_url is some utility function you've built
+ # which looks up the endpoint in some external URL registry.
+ url = lookup_url(endpoint, **values)
+ if url is None:
+ # External lookup did not have a URL.
+ # Re-raise the BuildError, in context of original traceback.
+ exc_type, exc_value, tb = sys.exc_info()
+ if exc_value is error:
+ raise exc_type, exc_value, tb
+ else:
+ raise error
+ # url_for will use this result, instead of raising BuildError.
+ return url
+
+ app.url_build_error_handlers.append(external_url_handler)
+
+ Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
+ `endpoint` and `values` are the arguments passed into `url_for`. Note
+ that this is for building URLs outside the current application, and not for
+ handling 404 NotFound errors.
+
+ .. versionadded:: 0.10
+ The `_scheme` parameter was added.
+
+ .. versionadded:: 0.9
+ The `_anchor` and `_method` parameters were added.
+
+ .. versionadded:: 0.9
+ Calls :meth:`Flask.handle_build_error` on
+ :exc:`~werkzeug.routing.BuildError`.
+
+ :param endpoint: the endpoint of the URL (name of the function)
+ :param values: the variable arguments of the URL rule
+ :param _external: if set to ``True``, an absolute URL is generated. Server
+ address can be changed via ``SERVER_NAME`` configuration variable which
+ defaults to `localhost`.
+ :param _scheme: a string specifying the desired URL scheme. The `_external`
+ parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default
+ behavior uses the same scheme as the current request, or
+ ``PREFERRED_URL_SCHEME`` from the :ref:`app configuration ` if no
+ request context is available. As of Werkzeug 0.10, this also can be set
+ to an empty string to build protocol-relative URLs.
+ :param _anchor: if provided this is added as anchor to the URL.
+ :param _method: if provided this explicitly specifies an HTTP method.
+ """
+ appctx = _app_ctx_stack.top
+ reqctx = _request_ctx_stack.top
+ if appctx is None:
+ raise RuntimeError('Attempted to generate a URL without the '
+ 'application context being pushed. This has to be '
+ 'executed when application context is available.')
+
+ # If request specific information is available we have some extra
+ # features that support "relative" URLs.
+ if reqctx is not None:
+ url_adapter = reqctx.url_adapter
+ blueprint_name = request.blueprint
+ if not reqctx.request._is_old_module:
+ if endpoint[:1] == '.':
+ if blueprint_name is not None:
+ endpoint = blueprint_name + endpoint
+ else:
+ endpoint = endpoint[1:]
+ else:
+ # TODO: get rid of this deprecated functionality in 1.0
+ if '.' not in endpoint:
+ if blueprint_name is not None:
+ endpoint = blueprint_name + '.' + endpoint
+ elif endpoint.startswith('.'):
+ endpoint = endpoint[1:]
+ external = values.pop('_external', False)
+
+ # Otherwise go with the url adapter from the appctx and make
+ # the URLs external by default.
+ else:
+ url_adapter = appctx.url_adapter
+ if url_adapter is None:
+ raise RuntimeError('Application was not able to create a URL '
+ 'adapter for request independent URL generation. '
+ 'You might be able to fix this by setting '
+ 'the SERVER_NAME config variable.')
+ external = values.pop('_external', True)
+
+ anchor = values.pop('_anchor', None)
+ method = values.pop('_method', None)
+ scheme = values.pop('_scheme', None)
+ appctx.app.inject_url_defaults(endpoint, values)
+
+ # This is not the best way to deal with this but currently the
+ # underlying Werkzeug router does not support overriding the scheme on
+ # a per build call basis.
+ old_scheme = None
+ if scheme is not None:
+ if not external:
+ raise ValueError('When specifying _scheme, _external must be True')
+ old_scheme = url_adapter.url_scheme
+ url_adapter.url_scheme = scheme
+
+ try:
+ try:
+ rv = url_adapter.build(endpoint, values, method=method,
+ force_external=external)
+ finally:
+ if old_scheme is not None:
+ url_adapter.url_scheme = old_scheme
+ except BuildError as error:
+ # We need to inject the values again so that the app callback can
+ # deal with that sort of stuff.
+ values['_external'] = external
+ values['_anchor'] = anchor
+ values['_method'] = method
+ return appctx.app.handle_url_build_error(error, endpoint, values)
+
+ if anchor is not None:
+ rv += '#' + url_quote(anchor)
+ return rv
+
+
+def get_template_attribute(template_name, attribute):
+ """Loads a macro (or variable) a template exports. This can be used to
+ invoke a macro from within Python code. If you for example have a
+ template named :file:`_cider.html` with the following contents:
+
+ .. sourcecode:: html+jinja
+
+ {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
+
+ You can access this from Python code like this::
+
+ hello = get_template_attribute('_cider.html', 'hello')
+ return hello('World')
+
+ .. versionadded:: 0.2
+
+ :param template_name: the name of the template
+ :param attribute: the name of the variable of macro to access
+ """
+ return getattr(current_app.jinja_env.get_template(template_name).module,
+ attribute)
+
+
+def flash(message, category='message'):
+ """Flashes a message to the next request. In order to remove the
+ flashed message from the session and to display it to the user,
+ the template has to call :func:`get_flashed_messages`.
+
+ .. versionchanged:: 0.3
+ `category` parameter added.
+
+ :param message: the message to be flashed.
+ :param category: the category for the message. The following values
+ are recommended: ``'message'`` for any kind of message,
+ ``'error'`` for errors, ``'info'`` for information
+ messages and ``'warning'`` for warnings. However any
+ kind of string can be used as category.
+ """
+ # Original implementation:
+ #
+ # session.setdefault('_flashes', []).append((category, message))
+ #
+ # This assumed that changes made to mutable structures in the session are
+ # are always in sync with the session object, which is not true for session
+ # implementations that use external storage for keeping their keys/values.
+ flashes = session.get('_flashes', [])
+ flashes.append((category, message))
+ session['_flashes'] = flashes
+ message_flashed.send(current_app._get_current_object(),
+ message=message, category=category)
+
+
+def get_flashed_messages(with_categories=False, category_filter=[]):
+ """Pulls all flashed messages from the session and returns them.
+ Further calls in the same request to the function will return
+ the same messages. By default just the messages are returned,
+ but when `with_categories` is set to ``True``, the return value will
+ be a list of tuples in the form ``(category, message)`` instead.
+
+ Filter the flashed messages to one or more categories by providing those
+ categories in `category_filter`. This allows rendering categories in
+ separate html blocks. The `with_categories` and `category_filter`
+ arguments are distinct:
+
+ * `with_categories` controls whether categories are returned with message
+ text (``True`` gives a tuple, where ``False`` gives just the message text).
+ * `category_filter` filters the messages down to only those matching the
+ provided categories.
+
+ See :ref:`message-flashing-pattern` for examples.
+
+ .. versionchanged:: 0.3
+ `with_categories` parameter added.
+
+ .. versionchanged:: 0.9
+ `category_filter` parameter added.
+
+ :param with_categories: set to ``True`` to also receive categories.
+ :param category_filter: whitelist of categories to limit return values
+ """
+ flashes = _request_ctx_stack.top.flashes
+ if flashes is None:
+ _request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \
+ if '_flashes' in session else []
+ if category_filter:
+ flashes = list(filter(lambda f: f[0] in category_filter, flashes))
+ if not with_categories:
+ return [x[1] for x in flashes]
+ return flashes
+
+
+def send_file(filename_or_fp, mimetype=None, as_attachment=False,
+ attachment_filename=None, add_etags=True,
+ cache_timeout=None, conditional=False, last_modified=None):
+ """Sends the contents of a file to the client. This will use the
+ most efficient method available and configured. By default it will
+ try to use the WSGI server's file_wrapper support. Alternatively
+ you can set the application's :attr:`~Flask.use_x_sendfile` attribute
+ to ``True`` to directly emit an ``X-Sendfile`` header. This however
+ requires support of the underlying webserver for ``X-Sendfile``.
+
+ By default it will try to guess the mimetype for you, but you can
+ also explicitly provide one. For extra security you probably want
+ to send certain files as attachment (HTML for instance). The mimetype
+ guessing requires a `filename` or an `attachment_filename` to be
+ provided.
+
+ ETags will also be attached automatically if a `filename` is provided. You
+ can turn this off by setting `add_etags=False`.
+
+ If `conditional=True` and `filename` is provided, this method will try to
+ upgrade the response stream to support range requests. This will allow
+ the request to be answered with partial content response.
+
+ Please never pass filenames to this function from user sources;
+ you should use :func:`send_from_directory` instead.
+
+ .. versionadded:: 0.2
+
+ .. versionadded:: 0.5
+ The `add_etags`, `cache_timeout` and `conditional` parameters were
+ added. The default behavior is now to attach etags.
+
+ .. versionchanged:: 0.7
+ mimetype guessing and etag support for file objects was
+ deprecated because it was unreliable. Pass a filename if you are
+ able to, otherwise attach an etag yourself. This functionality
+ will be removed in Flask 1.0
+
+ .. versionchanged:: 0.9
+ cache_timeout pulls its default from application config, when None.
+
+ .. versionchanged:: 0.12
+ The filename is no longer automatically inferred from file objects. If
+ you want to use automatic mimetype and etag support, pass a filepath via
+ `filename_or_fp` or `attachment_filename`.
+
+ .. versionchanged:: 0.12
+ The `attachment_filename` is preferred over `filename` for MIME-type
+ detection.
+
+ :param filename_or_fp: the filename of the file to send in `latin-1`.
+ This is relative to the :attr:`~Flask.root_path`
+ if a relative path is specified.
+ Alternatively a file object might be provided in
+ which case ``X-Sendfile`` might not work and fall
+ back to the traditional method. Make sure that the
+ file pointer is positioned at the start of data to
+ send before calling :func:`send_file`.
+ :param mimetype: the mimetype of the file if provided. If a file path is
+ given, auto detection happens as fallback, otherwise an
+ error will be raised.
+ :param as_attachment: set to ``True`` if you want to send this file with
+ a ``Content-Disposition: attachment`` header.
+ :param attachment_filename: the filename for the attachment if it
+ differs from the file's filename.
+ :param add_etags: set to ``False`` to disable attaching of etags.
+ :param conditional: set to ``True`` to enable conditional responses.
+
+ :param cache_timeout: the timeout in seconds for the headers. When ``None``
+ (default), this value is set by
+ :meth:`~Flask.get_send_file_max_age` of
+ :data:`~flask.current_app`.
+ :param last_modified: set the ``Last-Modified`` header to this value,
+ a :class:`~datetime.datetime` or timestamp.
+ If a file was passed, this overrides its mtime.
+ """
+ mtime = None
+ fsize = None
+ if isinstance(filename_or_fp, string_types):
+ filename = filename_or_fp
+ if not os.path.isabs(filename):
+ filename = os.path.join(current_app.root_path, filename)
+ file = None
+ if attachment_filename is None:
+ attachment_filename = os.path.basename(filename)
+ else:
+ file = filename_or_fp
+ filename = None
+
+ if mimetype is None:
+ if attachment_filename is not None:
+ mimetype = mimetypes.guess_type(attachment_filename)[0] \
+ or 'application/octet-stream'
+
+ if mimetype is None:
+ raise ValueError(
+ 'Unable to infer MIME-type because no filename is available. '
+ 'Please set either `attachment_filename`, pass a filepath to '
+ '`filename_or_fp` or set your own MIME-type via `mimetype`.'
+ )
+
+ headers = Headers()
+ if as_attachment:
+ if attachment_filename is None:
+ raise TypeError('filename unavailable, required for '
+ 'sending as attachment')
+ headers.add('Content-Disposition', 'attachment',
+ filename=attachment_filename)
+
+ if current_app.use_x_sendfile and filename:
+ if file is not None:
+ file.close()
+ headers['X-Sendfile'] = filename
+ fsize = os.path.getsize(filename)
+ headers['Content-Length'] = fsize
+ data = None
+ else:
+ if file is None:
+ file = open(filename, 'rb')
+ mtime = os.path.getmtime(filename)
+ fsize = os.path.getsize(filename)
+ headers['Content-Length'] = fsize
+ data = wrap_file(request.environ, file)
+
+ rv = current_app.response_class(data, mimetype=mimetype, headers=headers,
+ direct_passthrough=True)
+
+ if last_modified is not None:
+ rv.last_modified = last_modified
+ elif mtime is not None:
+ rv.last_modified = mtime
+
+ rv.cache_control.public = True
+ if cache_timeout is None:
+ cache_timeout = current_app.get_send_file_max_age(filename)
+ if cache_timeout is not None:
+ rv.cache_control.max_age = cache_timeout
+ rv.expires = int(time() + cache_timeout)
+
+ if add_etags and filename is not None:
+ from warnings import warn
+
+ try:
+ rv.set_etag('%s-%s-%s' % (
+ os.path.getmtime(filename),
+ os.path.getsize(filename),
+ adler32(
+ filename.encode('utf-8') if isinstance(filename, text_type)
+ else filename
+ ) & 0xffffffff
+ ))
+ except OSError:
+ warn('Access %s failed, maybe it does not exist, so ignore etags in '
+ 'headers' % filename, stacklevel=2)
+
+ if conditional:
+ if callable(getattr(Range, 'to_content_range_header', None)):
+ # Werkzeug supports Range Requests
+ # Remove this test when support for Werkzeug <0.12 is dropped
+ try:
+ rv = rv.make_conditional(request, accept_ranges=True,
+ complete_length=fsize)
+ except RequestedRangeNotSatisfiable:
+ file.close()
+ raise
+ else:
+ rv = rv.make_conditional(request)
+ # make sure we don't send x-sendfile for servers that
+ # ignore the 304 status code for x-sendfile.
+ if rv.status_code == 304:
+ rv.headers.pop('x-sendfile', None)
+ return rv
+
+
+def safe_join(directory, *pathnames):
+ """Safely join `directory` and zero or more untrusted `pathnames`
+ components.
+
+ Example usage::
+
+ @app.route('/wiki/')
+ def wiki_page(filename):
+ filename = safe_join(app.config['WIKI_FOLDER'], filename)
+ with open(filename, 'rb') as fd:
+ content = fd.read() # Read and process the file content...
+
+ :param directory: the trusted base directory.
+ :param pathnames: the untrusted pathnames relative to that directory.
+ :raises: :class:`~werkzeug.exceptions.NotFound` if one or more passed
+ paths fall out of its boundaries.
+ """
+
+ parts = [directory]
+
+ for filename in pathnames:
+ if filename != '':
+ filename = posixpath.normpath(filename)
+
+ if (
+ any(sep in filename for sep in _os_alt_seps)
+ or os.path.isabs(filename)
+ or filename == '..'
+ or filename.startswith('../')
+ ):
+ raise NotFound()
+
+ parts.append(filename)
+
+ return posixpath.join(*parts)
+
+
+def send_from_directory(directory, filename, **options):
+ """Send a file from a given directory with :func:`send_file`. This
+ is a secure way to quickly expose static files from an upload folder
+ or something similar.
+
+ Example usage::
+
+ @app.route('/uploads/')
+ def download_file(filename):
+ return send_from_directory(app.config['UPLOAD_FOLDER'],
+ filename, as_attachment=True)
+
+ .. admonition:: Sending files and Performance
+
+ It is strongly recommended to activate either ``X-Sendfile`` support in
+ your webserver or (if no authentication happens) to tell the webserver
+ to serve files for the given path on its own without calling into the
+ web application for improved performance.
+
+ .. versionadded:: 0.5
+
+ :param directory: the directory where all the files are stored.
+ :param filename: the filename relative to that directory to
+ download.
+ :param options: optional keyword arguments that are directly
+ forwarded to :func:`send_file`.
+ """
+ filename = safe_join(directory, filename)
+ if not os.path.isabs(filename):
+ filename = os.path.join(current_app.root_path, filename)
+ try:
+ if not os.path.isfile(filename):
+ raise NotFound()
+ except (TypeError, ValueError):
+ raise BadRequest()
+ options.setdefault('conditional', True)
+ return send_file(filename, **options)
+
+
+def get_root_path(import_name):
+ """Returns the path to a package or cwd if that cannot be found. This
+ returns the path of a package or the folder that contains a module.
+
+ Not to be confused with the package path returned by :func:`find_package`.
+ """
+ # Module already imported and has a file attribute. Use that first.
+ mod = sys.modules.get(import_name)
+ if mod is not None and hasattr(mod, '__file__'):
+ return os.path.dirname(os.path.abspath(mod.__file__))
+
+ # Next attempt: check the loader.
+ loader = pkgutil.get_loader(import_name)
+
+ # Loader does not exist or we're referring to an unloaded main module
+ # or a main module without path (interactive sessions), go with the
+ # current working directory.
+ if loader is None or import_name == '__main__':
+ return os.getcwd()
+
+ # For .egg, zipimporter does not have get_filename until Python 2.7.
+ # Some other loaders might exhibit the same behavior.
+ if hasattr(loader, 'get_filename'):
+ filepath = loader.get_filename(import_name)
+ else:
+ # Fall back to imports.
+ __import__(import_name)
+ mod = sys.modules[import_name]
+ filepath = getattr(mod, '__file__', None)
+
+ # If we don't have a filepath it might be because we are a
+ # namespace package. In this case we pick the root path from the
+ # first module that is contained in our package.
+ if filepath is None:
+ raise RuntimeError('No root path can be found for the provided '
+ 'module "%s". This can happen because the '
+ 'module came from an import hook that does '
+ 'not provide file name information or because '
+ 'it\'s a namespace package. In this case '
+ 'the root path needs to be explicitly '
+ 'provided.' % import_name)
+
+ # filepath is import_name.py for a module, or __init__.py for a package.
+ return os.path.dirname(os.path.abspath(filepath))
+
+
+def _matching_loader_thinks_module_is_package(loader, mod_name):
+ """Given the loader that loaded a module and the module this function
+ attempts to figure out if the given module is actually a package.
+ """
+ # If the loader can tell us if something is a package, we can
+ # directly ask the loader.
+ if hasattr(loader, 'is_package'):
+ return loader.is_package(mod_name)
+ # importlib's namespace loaders do not have this functionality but
+ # all the modules it loads are packages, so we can take advantage of
+ # this information.
+ elif (loader.__class__.__module__ == '_frozen_importlib' and
+ loader.__class__.__name__ == 'NamespaceLoader'):
+ return True
+ # Otherwise we need to fail with an error that explains what went
+ # wrong.
+ raise AttributeError(
+ ('%s.is_package() method is missing but is required by Flask of '
+ 'PEP 302 import hooks. If you do not use import hooks and '
+ 'you encounter this error please file a bug against Flask.') %
+ loader.__class__.__name__)
+
+
+def find_package(import_name):
+ """Finds a package and returns the prefix (or None if the package is
+ not installed) as well as the folder that contains the package or
+ module as a tuple. The package path returned is the module that would
+ have to be added to the pythonpath in order to make it possible to
+ import the module. The prefix is the path below which a UNIX like
+ folder structure exists (lib, share etc.).
+ """
+ root_mod_name = import_name.split('.')[0]
+ loader = pkgutil.get_loader(root_mod_name)
+ if loader is None or import_name == '__main__':
+ # import name is not found, or interactive/main module
+ package_path = os.getcwd()
+ else:
+ # For .egg, zipimporter does not have get_filename until Python 2.7.
+ if hasattr(loader, 'get_filename'):
+ filename = loader.get_filename(root_mod_name)
+ elif hasattr(loader, 'archive'):
+ # zipimporter's loader.archive points to the .egg or .zip
+ # archive filename is dropped in call to dirname below.
+ filename = loader.archive
+ else:
+ # At least one loader is missing both get_filename and archive:
+ # Google App Engine's HardenedModulesHook
+ #
+ # Fall back to imports.
+ __import__(import_name)
+ filename = sys.modules[import_name].__file__
+ package_path = os.path.abspath(os.path.dirname(filename))
+
+ # In case the root module is a package we need to chop of the
+ # rightmost part. This needs to go through a helper function
+ # because of python 3.3 namespace packages.
+ if _matching_loader_thinks_module_is_package(
+ loader, root_mod_name):
+ package_path = os.path.dirname(package_path)
+
+ site_parent, site_folder = os.path.split(package_path)
+ py_prefix = os.path.abspath(sys.prefix)
+ if package_path.startswith(py_prefix):
+ return py_prefix, package_path
+ elif site_folder.lower() == 'site-packages':
+ parent, folder = os.path.split(site_parent)
+ # Windows like installations
+ if folder.lower() == 'lib':
+ base_dir = parent
+ # UNIX like installations
+ elif os.path.basename(parent).lower() == 'lib':
+ base_dir = os.path.dirname(parent)
+ else:
+ base_dir = site_parent
+ return base_dir, package_path
+ return None, package_path
+
+
+class locked_cached_property(object):
+ """A decorator that converts a function into a lazy property. The
+ function wrapped is called the first time to retrieve the result
+ and then that calculated result is used the next time you access
+ the value. Works like the one in Werkzeug but has a lock for
+ thread safety.
+ """
+
+ def __init__(self, func, name=None, doc=None):
+ self.__name__ = name or func.__name__
+ self.__module__ = func.__module__
+ self.__doc__ = doc or func.__doc__
+ self.func = func
+ self.lock = RLock()
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ with self.lock:
+ value = obj.__dict__.get(self.__name__, _missing)
+ if value is _missing:
+ value = self.func(obj)
+ obj.__dict__[self.__name__] = value
+ return value
+
+
+class _PackageBoundObject(object):
+
+ def __init__(self, import_name, template_folder=None, root_path=None):
+ #: The name of the package or module. Do not change this once
+ #: it was set by the constructor.
+ self.import_name = import_name
+
+ #: location of the templates. ``None`` if templates should not be
+ #: exposed.
+ self.template_folder = template_folder
+
+ if root_path is None:
+ root_path = get_root_path(self.import_name)
+
+ #: Where is the app root located?
+ self.root_path = root_path
+
+ self._static_folder = None
+ self._static_url_path = None
+
+ def _get_static_folder(self):
+ if self._static_folder is not None:
+ return os.path.join(self.root_path, self._static_folder)
+ def _set_static_folder(self, value):
+ self._static_folder = value
+ static_folder = property(_get_static_folder, _set_static_folder, doc='''
+ The absolute path to the configured static folder.
+ ''')
+ del _get_static_folder, _set_static_folder
+
+ def _get_static_url_path(self):
+ if self._static_url_path is not None:
+ return self._static_url_path
+ if self.static_folder is not None:
+ return '/' + os.path.basename(self.static_folder)
+ def _set_static_url_path(self, value):
+ self._static_url_path = value
+ static_url_path = property(_get_static_url_path, _set_static_url_path)
+ del _get_static_url_path, _set_static_url_path
+
+ @property
+ def has_static_folder(self):
+ """This is ``True`` if the package bound object's container has a
+ folder for static files.
+
+ .. versionadded:: 0.5
+ """
+ return self.static_folder is not None
+
+ @locked_cached_property
+ def jinja_loader(self):
+ """The Jinja loader for this package bound object.
+
+ .. versionadded:: 0.5
+ """
+ if self.template_folder is not None:
+ return FileSystemLoader(os.path.join(self.root_path,
+ self.template_folder))
+
+ def get_send_file_max_age(self, filename):
+ """Provides default cache_timeout for the :func:`send_file` functions.
+
+ By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from
+ the configuration of :data:`~flask.current_app`.
+
+ Static file functions such as :func:`send_from_directory` use this
+ function, and :func:`send_file` calls this function on
+ :data:`~flask.current_app` when the given cache_timeout is ``None``. If a
+ cache_timeout is given in :func:`send_file`, that timeout is used;
+ otherwise, this method is called.
+
+ This allows subclasses to change the behavior when sending files based
+ on the filename. For example, to set the cache timeout for .js files
+ to 60 seconds::
+
+ class MyFlask(flask.Flask):
+ def get_send_file_max_age(self, name):
+ if name.lower().endswith('.js'):
+ return 60
+ return flask.Flask.get_send_file_max_age(self, name)
+
+ .. versionadded:: 0.9
+ """
+ return total_seconds(current_app.send_file_max_age_default)
+
+ def send_static_file(self, filename):
+ """Function used internally to send static files from the static
+ folder to the browser.
+
+ .. versionadded:: 0.5
+ """
+ if not self.has_static_folder:
+ raise RuntimeError('No static folder for this object')
+ # Ensure get_send_file_max_age is called in all cases.
+ # Here, we ensure get_send_file_max_age is called for Blueprints.
+ cache_timeout = self.get_send_file_max_age(filename)
+ return send_from_directory(self.static_folder, filename,
+ cache_timeout=cache_timeout)
+
+ def open_resource(self, resource, mode='rb'):
+ """Opens a resource from the application's resource folder. To see
+ how this works, consider the following folder structure::
+
+ /myapplication.py
+ /schema.sql
+ /static
+ /style.css
+ /templates
+ /layout.html
+ /index.html
+
+ If you want to open the :file:`schema.sql` file you would do the
+ following::
+
+ with app.open_resource('schema.sql') as f:
+ contents = f.read()
+ do_something_with(contents)
+
+ :param resource: the name of the resource. To access resources within
+ subfolders use forward slashes as separator.
+ :param mode: resource file opening mode, default is 'rb'.
+ """
+ if mode not in ('r', 'rb'):
+ raise ValueError('Resources can only be opened for reading')
+ return open(os.path.join(self.root_path, resource), mode)
+
+
+def total_seconds(td):
+ """Returns the total seconds from a timedelta object.
+
+ :param timedelta td: the timedelta to be converted in seconds
+
+ :returns: number of seconds
+ :rtype: int
+ """
+ return td.days * 60 * 60 * 24 + td.seconds
diff --git a/service2/flask/json.py b/service2/flask/json.py
new file mode 100644
index 0000000..16e0c29
--- /dev/null
+++ b/service2/flask/json.py
@@ -0,0 +1,269 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.jsonimpl
+ ~~~~~~~~~~~~~~
+
+ Implementation helpers for the JSON support in Flask.
+
+ :copyright: (c) 2015 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import io
+import uuid
+from datetime import date
+from .globals import current_app, request
+from ._compat import text_type, PY2
+
+from werkzeug.http import http_date
+from jinja2 import Markup
+
+# Use the same json implementation as itsdangerous on which we
+# depend anyways.
+from itsdangerous import json as _json
+
+
+# Figure out if simplejson escapes slashes. This behavior was changed
+# from one version to another without reason.
+_slash_escape = '\\/' not in _json.dumps('/')
+
+
+__all__ = ['dump', 'dumps', 'load', 'loads', 'htmlsafe_dump',
+ 'htmlsafe_dumps', 'JSONDecoder', 'JSONEncoder',
+ 'jsonify']
+
+
+def _wrap_reader_for_text(fp, encoding):
+ if isinstance(fp.read(0), bytes):
+ fp = io.TextIOWrapper(io.BufferedReader(fp), encoding)
+ return fp
+
+
+def _wrap_writer_for_text(fp, encoding):
+ try:
+ fp.write('')
+ except TypeError:
+ fp = io.TextIOWrapper(fp, encoding)
+ return fp
+
+
+class JSONEncoder(_json.JSONEncoder):
+ """The default Flask JSON encoder. This one extends the default simplejson
+ encoder by also supporting ``datetime`` objects, ``UUID`` as well as
+ ``Markup`` objects which are serialized as RFC 822 datetime strings (same
+ as the HTTP date format). In order to support more data types override the
+ :meth:`default` method.
+ """
+
+ def default(self, o):
+ """Implement this method in a subclass such that it returns a
+ serializable object for ``o``, or calls the base implementation (to
+ raise a :exc:`TypeError`).
+
+ For example, to support arbitrary iterators, you could implement
+ default like this::
+
+ def default(self, o):
+ try:
+ iterable = iter(o)
+ except TypeError:
+ pass
+ else:
+ return list(iterable)
+ return JSONEncoder.default(self, o)
+ """
+ if isinstance(o, date):
+ return http_date(o.timetuple())
+ if isinstance(o, uuid.UUID):
+ return str(o)
+ if hasattr(o, '__html__'):
+ return text_type(o.__html__())
+ return _json.JSONEncoder.default(self, o)
+
+
+class JSONDecoder(_json.JSONDecoder):
+ """The default JSON decoder. This one does not change the behavior from
+ the default simplejson decoder. Consult the :mod:`json` documentation
+ for more information. This decoder is not only used for the load
+ functions of this module but also :attr:`~flask.Request`.
+ """
+
+
+def _dump_arg_defaults(kwargs):
+ """Inject default arguments for dump functions."""
+ if current_app:
+ kwargs.setdefault('cls', current_app.json_encoder)
+ if not current_app.config['JSON_AS_ASCII']:
+ kwargs.setdefault('ensure_ascii', False)
+ kwargs.setdefault('sort_keys', current_app.config['JSON_SORT_KEYS'])
+ else:
+ kwargs.setdefault('sort_keys', True)
+ kwargs.setdefault('cls', JSONEncoder)
+
+
+def _load_arg_defaults(kwargs):
+ """Inject default arguments for load functions."""
+ if current_app:
+ kwargs.setdefault('cls', current_app.json_decoder)
+ else:
+ kwargs.setdefault('cls', JSONDecoder)
+
+
+def dumps(obj, **kwargs):
+ """Serialize ``obj`` to a JSON formatted ``str`` by using the application's
+ configured encoder (:attr:`~flask.Flask.json_encoder`) if there is an
+ application on the stack.
+
+ This function can return ``unicode`` strings or ascii-only bytestrings by
+ default which coerce into unicode strings automatically. That behavior by
+ default is controlled by the ``JSON_AS_ASCII`` configuration variable
+ and can be overridden by the simplejson ``ensure_ascii`` parameter.
+ """
+ _dump_arg_defaults(kwargs)
+ encoding = kwargs.pop('encoding', None)
+ rv = _json.dumps(obj, **kwargs)
+ if encoding is not None and isinstance(rv, text_type):
+ rv = rv.encode(encoding)
+ return rv
+
+
+def dump(obj, fp, **kwargs):
+ """Like :func:`dumps` but writes into a file object."""
+ _dump_arg_defaults(kwargs)
+ encoding = kwargs.pop('encoding', None)
+ if encoding is not None:
+ fp = _wrap_writer_for_text(fp, encoding)
+ _json.dump(obj, fp, **kwargs)
+
+
+def loads(s, **kwargs):
+ """Unserialize a JSON object from a string ``s`` by using the application's
+ configured decoder (:attr:`~flask.Flask.json_decoder`) if there is an
+ application on the stack.
+ """
+ _load_arg_defaults(kwargs)
+ if isinstance(s, bytes):
+ s = s.decode(kwargs.pop('encoding', None) or 'utf-8')
+ return _json.loads(s, **kwargs)
+
+
+def load(fp, **kwargs):
+ """Like :func:`loads` but reads from a file object.
+ """
+ _load_arg_defaults(kwargs)
+ if not PY2:
+ fp = _wrap_reader_for_text(fp, kwargs.pop('encoding', None) or 'utf-8')
+ return _json.load(fp, **kwargs)
+
+
+def htmlsafe_dumps(obj, **kwargs):
+ """Works exactly like :func:`dumps` but is safe for use in ``