commit
						28e3543ec4
					
				
				 168 changed files with 51052 additions and 0 deletions
			
			
		@ -0,0 +1,103 @@ | 
				
			|||||||
 | 
					Metadata-Version: 1.1 | 
				
			||||||
 | 
					Name: Flask | 
				
			||||||
 | 
					Version: 1.0.2 | 
				
			||||||
 | 
					Summary: A simple framework for building complex web applications. | 
				
			||||||
 | 
					Home-page: https://www.palletsprojects.com/p/flask/ | 
				
			||||||
 | 
					Author: Pallets team | 
				
			||||||
 | 
					Author-email: contact@palletsprojects.com | 
				
			||||||
 | 
					License: BSD | 
				
			||||||
 | 
					Description: Flask | 
				
			||||||
 | 
					        ===== | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Flask is a lightweight `WSGI`_ web application framework. It is designed | 
				
			||||||
 | 
					        to make getting started quick and easy, with the ability to scale up to | 
				
			||||||
 | 
					        complex applications. It began as a simple wrapper around `Werkzeug`_ | 
				
			||||||
 | 
					        and `Jinja`_ and has become one of the most popular Python web | 
				
			||||||
 | 
					        application frameworks. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Flask offers suggestions, but doesn't enforce any dependencies or | 
				
			||||||
 | 
					        project layout. It is up to the developer to choose the tools and | 
				
			||||||
 | 
					        libraries they want to use. There are many extensions provided by the | 
				
			||||||
 | 
					        community that make adding new functionality easy. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Installing | 
				
			||||||
 | 
					        ---------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Install and update using `pip`_: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: text | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            pip install -U Flask | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        A Simple Example | 
				
			||||||
 | 
					        ---------------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            from flask import Flask | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            app = Flask(__name__) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            @app.route('/') | 
				
			||||||
 | 
					            def hello(): | 
				
			||||||
 | 
					                return 'Hello, World!' | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: text | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            $ FLASK_APP=hello.py flask run | 
				
			||||||
 | 
					             * Serving Flask app "hello" | 
				
			||||||
 | 
					             * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Donate | 
				
			||||||
 | 
					        ------ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        The Pallets organization develops and supports Flask and the libraries | 
				
			||||||
 | 
					        it uses. In order to grow the community of contributors and users, and | 
				
			||||||
 | 
					        allow the maintainers to devote more time to the projects, `please | 
				
			||||||
 | 
					        donate today`_. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20 | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Links | 
				
			||||||
 | 
					        ----- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        * Website: https://www.palletsprojects.com/p/flask/ | 
				
			||||||
 | 
					        * Documentation: http://flask.pocoo.org/docs/ | 
				
			||||||
 | 
					        * License: `BSD <https://github.com/pallets/flask/blob/master/LICENSE>`_ | 
				
			||||||
 | 
					        * Releases: https://pypi.org/project/Flask/ | 
				
			||||||
 | 
					        * Code: https://github.com/pallets/flask | 
				
			||||||
 | 
					        * Issue tracker: https://github.com/pallets/flask/issues | 
				
			||||||
 | 
					        * Test status: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					          * Linux, Mac: https://travis-ci.org/pallets/flask | 
				
			||||||
 | 
					          * Windows: https://ci.appveyor.com/project/pallets/flask | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        * Test coverage: https://codecov.io/gh/pallets/flask | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. _WSGI: https://wsgi.readthedocs.io | 
				
			||||||
 | 
					        .. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/ | 
				
			||||||
 | 
					        .. _Jinja: https://www.palletsprojects.com/p/jinja/ | 
				
			||||||
 | 
					        .. _pip: https://pip.pypa.io/en/stable/quickstart/ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					Platform: any | 
				
			||||||
 | 
					Classifier: Development Status :: 5 - Production/Stable | 
				
			||||||
 | 
					Classifier: Environment :: Web Environment | 
				
			||||||
 | 
					Classifier: Framework :: Flask | 
				
			||||||
 | 
					Classifier: Intended Audience :: Developers | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: BSD License | 
				
			||||||
 | 
					Classifier: Operating System :: OS Independent | 
				
			||||||
 | 
					Classifier: Programming Language :: Python | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.5 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.6 | 
				
			||||||
 | 
					Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content | 
				
			||||||
 | 
					Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application | 
				
			||||||
 | 
					Classifier: Topic :: Software Development :: Libraries :: Application Frameworks | 
				
			||||||
 | 
					Classifier: Topic :: Software Development :: Libraries :: Python Modules | 
				
			||||||
@ -0,0 +1,223 @@ | 
				
			|||||||
 | 
					AUTHORS | 
				
			||||||
 | 
					CHANGES.rst | 
				
			||||||
 | 
					LICENSE | 
				
			||||||
 | 
					MANIFEST.in | 
				
			||||||
 | 
					Makefile | 
				
			||||||
 | 
					README.rst | 
				
			||||||
 | 
					setup.cfg | 
				
			||||||
 | 
					setup.py | 
				
			||||||
 | 
					tox.ini | 
				
			||||||
 | 
					Flask.egg-info/PKG-INFO | 
				
			||||||
 | 
					Flask.egg-info/SOURCES.txt | 
				
			||||||
 | 
					Flask.egg-info/dependency_links.txt | 
				
			||||||
 | 
					Flask.egg-info/entry_points.txt | 
				
			||||||
 | 
					Flask.egg-info/not-zip-safe | 
				
			||||||
 | 
					Flask.egg-info/requires.txt | 
				
			||||||
 | 
					Flask.egg-info/top_level.txt | 
				
			||||||
 | 
					artwork/LICENSE | 
				
			||||||
 | 
					artwork/logo-full.svg | 
				
			||||||
 | 
					artwork/logo-lineart.svg | 
				
			||||||
 | 
					docs/Makefile | 
				
			||||||
 | 
					docs/advanced_foreword.rst | 
				
			||||||
 | 
					docs/api.rst | 
				
			||||||
 | 
					docs/appcontext.rst | 
				
			||||||
 | 
					docs/becomingbig.rst | 
				
			||||||
 | 
					docs/blueprints.rst | 
				
			||||||
 | 
					docs/changelog.rst | 
				
			||||||
 | 
					docs/cli.rst | 
				
			||||||
 | 
					docs/conf.py | 
				
			||||||
 | 
					docs/config.rst | 
				
			||||||
 | 
					docs/contents.rst.inc | 
				
			||||||
 | 
					docs/contributing.rst | 
				
			||||||
 | 
					docs/design.rst | 
				
			||||||
 | 
					docs/errorhandling.rst | 
				
			||||||
 | 
					docs/extensiondev.rst | 
				
			||||||
 | 
					docs/extensions.rst | 
				
			||||||
 | 
					docs/flaskstyle.sty | 
				
			||||||
 | 
					docs/foreword.rst | 
				
			||||||
 | 
					docs/htmlfaq.rst | 
				
			||||||
 | 
					docs/index.rst | 
				
			||||||
 | 
					docs/installation.rst | 
				
			||||||
 | 
					docs/latexindex.rst | 
				
			||||||
 | 
					docs/license.rst | 
				
			||||||
 | 
					docs/logging.rst | 
				
			||||||
 | 
					docs/logo.pdf | 
				
			||||||
 | 
					docs/make.bat | 
				
			||||||
 | 
					docs/quickstart.rst | 
				
			||||||
 | 
					docs/reqcontext.rst | 
				
			||||||
 | 
					docs/security.rst | 
				
			||||||
 | 
					docs/server.rst | 
				
			||||||
 | 
					docs/shell.rst | 
				
			||||||
 | 
					docs/signals.rst | 
				
			||||||
 | 
					docs/styleguide.rst | 
				
			||||||
 | 
					docs/templating.rst | 
				
			||||||
 | 
					docs/testing.rst | 
				
			||||||
 | 
					docs/unicode.rst | 
				
			||||||
 | 
					docs/upgrading.rst | 
				
			||||||
 | 
					docs/views.rst | 
				
			||||||
 | 
					docs/_static/debugger.png | 
				
			||||||
 | 
					docs/_static/flask-favicon.ico | 
				
			||||||
 | 
					docs/_static/flask.png | 
				
			||||||
 | 
					docs/_static/logo-full.png | 
				
			||||||
 | 
					docs/_static/no.png | 
				
			||||||
 | 
					docs/_static/pycharm-runconfig.png | 
				
			||||||
 | 
					docs/_static/touch-icon.png | 
				
			||||||
 | 
					docs/_static/yes.png | 
				
			||||||
 | 
					docs/deploying/cgi.rst | 
				
			||||||
 | 
					docs/deploying/fastcgi.rst | 
				
			||||||
 | 
					docs/deploying/index.rst | 
				
			||||||
 | 
					docs/deploying/mod_wsgi.rst | 
				
			||||||
 | 
					docs/deploying/uwsgi.rst | 
				
			||||||
 | 
					docs/deploying/wsgi-standalone.rst | 
				
			||||||
 | 
					docs/patterns/apierrors.rst | 
				
			||||||
 | 
					docs/patterns/appdispatch.rst | 
				
			||||||
 | 
					docs/patterns/appfactories.rst | 
				
			||||||
 | 
					docs/patterns/caching.rst | 
				
			||||||
 | 
					docs/patterns/celery.rst | 
				
			||||||
 | 
					docs/patterns/deferredcallbacks.rst | 
				
			||||||
 | 
					docs/patterns/distribute.rst | 
				
			||||||
 | 
					docs/patterns/errorpages.rst | 
				
			||||||
 | 
					docs/patterns/fabric.rst | 
				
			||||||
 | 
					docs/patterns/favicon.rst | 
				
			||||||
 | 
					docs/patterns/fileuploads.rst | 
				
			||||||
 | 
					docs/patterns/flashing.rst | 
				
			||||||
 | 
					docs/patterns/index.rst | 
				
			||||||
 | 
					docs/patterns/jquery.rst | 
				
			||||||
 | 
					docs/patterns/lazyloading.rst | 
				
			||||||
 | 
					docs/patterns/methodoverrides.rst | 
				
			||||||
 | 
					docs/patterns/mongokit.rst | 
				
			||||||
 | 
					docs/patterns/packages.rst | 
				
			||||||
 | 
					docs/patterns/requestchecksum.rst | 
				
			||||||
 | 
					docs/patterns/sqlalchemy.rst | 
				
			||||||
 | 
					docs/patterns/sqlite3.rst | 
				
			||||||
 | 
					docs/patterns/streaming.rst | 
				
			||||||
 | 
					docs/patterns/subclassing.rst | 
				
			||||||
 | 
					docs/patterns/templateinheritance.rst | 
				
			||||||
 | 
					docs/patterns/urlprocessors.rst | 
				
			||||||
 | 
					docs/patterns/viewdecorators.rst | 
				
			||||||
 | 
					docs/patterns/wtforms.rst | 
				
			||||||
 | 
					docs/tutorial/blog.rst | 
				
			||||||
 | 
					docs/tutorial/database.rst | 
				
			||||||
 | 
					docs/tutorial/deploy.rst | 
				
			||||||
 | 
					docs/tutorial/factory.rst | 
				
			||||||
 | 
					docs/tutorial/flaskr_edit.png | 
				
			||||||
 | 
					docs/tutorial/flaskr_index.png | 
				
			||||||
 | 
					docs/tutorial/flaskr_login.png | 
				
			||||||
 | 
					docs/tutorial/index.rst | 
				
			||||||
 | 
					docs/tutorial/install.rst | 
				
			||||||
 | 
					docs/tutorial/layout.rst | 
				
			||||||
 | 
					docs/tutorial/next.rst | 
				
			||||||
 | 
					docs/tutorial/static.rst | 
				
			||||||
 | 
					docs/tutorial/templates.rst | 
				
			||||||
 | 
					docs/tutorial/tests.rst | 
				
			||||||
 | 
					docs/tutorial/views.rst | 
				
			||||||
 | 
					examples/javascript/.gitignore | 
				
			||||||
 | 
					examples/javascript/LICENSE | 
				
			||||||
 | 
					examples/javascript/MANIFEST.in | 
				
			||||||
 | 
					examples/javascript/README.rst | 
				
			||||||
 | 
					examples/javascript/setup.cfg | 
				
			||||||
 | 
					examples/javascript/setup.py | 
				
			||||||
 | 
					examples/javascript/js_example/__init__.py | 
				
			||||||
 | 
					examples/javascript/js_example/views.py | 
				
			||||||
 | 
					examples/javascript/js_example/templates/base.html | 
				
			||||||
 | 
					examples/javascript/js_example/templates/fetch.html | 
				
			||||||
 | 
					examples/javascript/js_example/templates/jquery.html | 
				
			||||||
 | 
					examples/javascript/js_example/templates/plain.html | 
				
			||||||
 | 
					examples/javascript/tests/conftest.py | 
				
			||||||
 | 
					examples/javascript/tests/test_js_example.py | 
				
			||||||
 | 
					examples/tutorial/.gitignore | 
				
			||||||
 | 
					examples/tutorial/LICENSE | 
				
			||||||
 | 
					examples/tutorial/MANIFEST.in | 
				
			||||||
 | 
					examples/tutorial/README.rst | 
				
			||||||
 | 
					examples/tutorial/setup.cfg | 
				
			||||||
 | 
					examples/tutorial/setup.py | 
				
			||||||
 | 
					examples/tutorial/flaskr/__init__.py | 
				
			||||||
 | 
					examples/tutorial/flaskr/auth.py | 
				
			||||||
 | 
					examples/tutorial/flaskr/blog.py | 
				
			||||||
 | 
					examples/tutorial/flaskr/db.py | 
				
			||||||
 | 
					examples/tutorial/flaskr/schema.sql | 
				
			||||||
 | 
					examples/tutorial/flaskr/static/style.css | 
				
			||||||
 | 
					examples/tutorial/flaskr/templates/base.html | 
				
			||||||
 | 
					examples/tutorial/flaskr/templates/auth/login.html | 
				
			||||||
 | 
					examples/tutorial/flaskr/templates/auth/register.html | 
				
			||||||
 | 
					examples/tutorial/flaskr/templates/blog/create.html | 
				
			||||||
 | 
					examples/tutorial/flaskr/templates/blog/index.html | 
				
			||||||
 | 
					examples/tutorial/flaskr/templates/blog/update.html | 
				
			||||||
 | 
					examples/tutorial/tests/conftest.py | 
				
			||||||
 | 
					examples/tutorial/tests/data.sql | 
				
			||||||
 | 
					examples/tutorial/tests/test_auth.py | 
				
			||||||
 | 
					examples/tutorial/tests/test_blog.py | 
				
			||||||
 | 
					examples/tutorial/tests/test_db.py | 
				
			||||||
 | 
					examples/tutorial/tests/test_factory.py | 
				
			||||||
 | 
					flask/__init__.py | 
				
			||||||
 | 
					flask/__main__.py | 
				
			||||||
 | 
					flask/_compat.py | 
				
			||||||
 | 
					flask/app.py | 
				
			||||||
 | 
					flask/blueprints.py | 
				
			||||||
 | 
					flask/cli.py | 
				
			||||||
 | 
					flask/config.py | 
				
			||||||
 | 
					flask/ctx.py | 
				
			||||||
 | 
					flask/debughelpers.py | 
				
			||||||
 | 
					flask/globals.py | 
				
			||||||
 | 
					flask/helpers.py | 
				
			||||||
 | 
					flask/logging.py | 
				
			||||||
 | 
					flask/sessions.py | 
				
			||||||
 | 
					flask/signals.py | 
				
			||||||
 | 
					flask/templating.py | 
				
			||||||
 | 
					flask/testing.py | 
				
			||||||
 | 
					flask/views.py | 
				
			||||||
 | 
					flask/wrappers.py | 
				
			||||||
 | 
					flask/json/__init__.py | 
				
			||||||
 | 
					flask/json/tag.py | 
				
			||||||
 | 
					tests/conftest.py | 
				
			||||||
 | 
					tests/test_appctx.py | 
				
			||||||
 | 
					tests/test_basic.py | 
				
			||||||
 | 
					tests/test_blueprints.py | 
				
			||||||
 | 
					tests/test_cli.py | 
				
			||||||
 | 
					tests/test_config.py | 
				
			||||||
 | 
					tests/test_helpers.py | 
				
			||||||
 | 
					tests/test_instance_config.py | 
				
			||||||
 | 
					tests/test_json_tag.py | 
				
			||||||
 | 
					tests/test_logging.py | 
				
			||||||
 | 
					tests/test_regression.py | 
				
			||||||
 | 
					tests/test_reqctx.py | 
				
			||||||
 | 
					tests/test_signals.py | 
				
			||||||
 | 
					tests/test_subclassing.py | 
				
			||||||
 | 
					tests/test_templating.py | 
				
			||||||
 | 
					tests/test_testing.py | 
				
			||||||
 | 
					tests/test_user_error_handler.py | 
				
			||||||
 | 
					tests/test_views.py | 
				
			||||||
 | 
					tests/static/config.json | 
				
			||||||
 | 
					tests/static/index.html | 
				
			||||||
 | 
					tests/templates/_macro.html | 
				
			||||||
 | 
					tests/templates/context_template.html | 
				
			||||||
 | 
					tests/templates/escaping_template.html | 
				
			||||||
 | 
					tests/templates/mail.txt | 
				
			||||||
 | 
					tests/templates/non_escaping_template.txt | 
				
			||||||
 | 
					tests/templates/simple_template.html | 
				
			||||||
 | 
					tests/templates/template_filter.html | 
				
			||||||
 | 
					tests/templates/template_test.html | 
				
			||||||
 | 
					tests/templates/nested/nested.txt | 
				
			||||||
 | 
					tests/test_apps/.env | 
				
			||||||
 | 
					tests/test_apps/.flaskenv | 
				
			||||||
 | 
					tests/test_apps/blueprintapp/__init__.py | 
				
			||||||
 | 
					tests/test_apps/blueprintapp/apps/__init__.py | 
				
			||||||
 | 
					tests/test_apps/blueprintapp/apps/admin/__init__.py | 
				
			||||||
 | 
					tests/test_apps/blueprintapp/apps/admin/static/test.txt | 
				
			||||||
 | 
					tests/test_apps/blueprintapp/apps/admin/static/css/test.css | 
				
			||||||
 | 
					tests/test_apps/blueprintapp/apps/admin/templates/admin/index.html | 
				
			||||||
 | 
					tests/test_apps/blueprintapp/apps/frontend/__init__.py | 
				
			||||||
 | 
					tests/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html | 
				
			||||||
 | 
					tests/test_apps/cliapp/__init__.py | 
				
			||||||
 | 
					tests/test_apps/cliapp/app.py | 
				
			||||||
 | 
					tests/test_apps/cliapp/factory.py | 
				
			||||||
 | 
					tests/test_apps/cliapp/importerrorapp.py | 
				
			||||||
 | 
					tests/test_apps/cliapp/message.txt | 
				
			||||||
 | 
					tests/test_apps/cliapp/multiapp.py | 
				
			||||||
 | 
					tests/test_apps/cliapp/inner1/__init__.py | 
				
			||||||
 | 
					tests/test_apps/cliapp/inner1/inner2/__init__.py | 
				
			||||||
 | 
					tests/test_apps/cliapp/inner1/inner2/flask.py | 
				
			||||||
 | 
					tests/test_apps/helloworld/hello.py | 
				
			||||||
 | 
					tests/test_apps/helloworld/wsgi.py | 
				
			||||||
 | 
					tests/test_apps/subdomaintestmodule/__init__.py | 
				
			||||||
 | 
					tests/test_apps/subdomaintestmodule/static/hello.txt | 
				
			||||||
@ -0,0 +1,3 @@ | 
				
			|||||||
 | 
					[console_scripts] | 
				
			||||||
 | 
					flask = flask.cli:main | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,48 @@ | 
				
			|||||||
 | 
					../flask/testing.py | 
				
			||||||
 | 
					../flask/templating.py | 
				
			||||||
 | 
					../flask/__main__.py | 
				
			||||||
 | 
					../flask/sessions.py | 
				
			||||||
 | 
					../flask/signals.py | 
				
			||||||
 | 
					../flask/helpers.py | 
				
			||||||
 | 
					../flask/debughelpers.py | 
				
			||||||
 | 
					../flask/wrappers.py | 
				
			||||||
 | 
					../flask/app.py | 
				
			||||||
 | 
					../flask/ctx.py | 
				
			||||||
 | 
					../flask/config.py | 
				
			||||||
 | 
					../flask/logging.py | 
				
			||||||
 | 
					../flask/blueprints.py | 
				
			||||||
 | 
					../flask/views.py | 
				
			||||||
 | 
					../flask/cli.py | 
				
			||||||
 | 
					../flask/_compat.py | 
				
			||||||
 | 
					../flask/globals.py | 
				
			||||||
 | 
					../flask/__init__.py | 
				
			||||||
 | 
					../flask/json/tag.py | 
				
			||||||
 | 
					../flask/json/__init__.py | 
				
			||||||
 | 
					../flask/testing.pyc | 
				
			||||||
 | 
					../flask/templating.pyc | 
				
			||||||
 | 
					../flask/__main__.pyc | 
				
			||||||
 | 
					../flask/sessions.pyc | 
				
			||||||
 | 
					../flask/signals.pyc | 
				
			||||||
 | 
					../flask/helpers.pyc | 
				
			||||||
 | 
					../flask/debughelpers.pyc | 
				
			||||||
 | 
					../flask/wrappers.pyc | 
				
			||||||
 | 
					../flask/app.pyc | 
				
			||||||
 | 
					../flask/ctx.pyc | 
				
			||||||
 | 
					../flask/config.pyc | 
				
			||||||
 | 
					../flask/logging.pyc | 
				
			||||||
 | 
					../flask/blueprints.pyc | 
				
			||||||
 | 
					../flask/views.pyc | 
				
			||||||
 | 
					../flask/cli.pyc | 
				
			||||||
 | 
					../flask/_compat.pyc | 
				
			||||||
 | 
					../flask/globals.pyc | 
				
			||||||
 | 
					../flask/__init__.pyc | 
				
			||||||
 | 
					../flask/json/tag.pyc | 
				
			||||||
 | 
					../flask/json/__init__.pyc | 
				
			||||||
 | 
					not-zip-safe | 
				
			||||||
 | 
					entry_points.txt | 
				
			||||||
 | 
					dependency_links.txt | 
				
			||||||
 | 
					PKG-INFO | 
				
			||||||
 | 
					top_level.txt | 
				
			||||||
 | 
					requires.txt | 
				
			||||||
 | 
					SOURCES.txt | 
				
			||||||
 | 
					../../../../bin/flask | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,20 @@ | 
				
			|||||||
 | 
					Werkzeug>=0.14 | 
				
			||||||
 | 
					Jinja2>=2.10 | 
				
			||||||
 | 
					itsdangerous>=0.24 | 
				
			||||||
 | 
					click>=5.1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dev] | 
				
			||||||
 | 
					pytest>=3 | 
				
			||||||
 | 
					coverage | 
				
			||||||
 | 
					tox | 
				
			||||||
 | 
					sphinx | 
				
			||||||
 | 
					pallets-sphinx-themes | 
				
			||||||
 | 
					sphinxcontrib-log-cabinet | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[docs] | 
				
			||||||
 | 
					sphinx | 
				
			||||||
 | 
					pallets-sphinx-themes | 
				
			||||||
 | 
					sphinxcontrib-log-cabinet | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dotenv] | 
				
			||||||
 | 
					python-dotenv | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					flask | 
				
			||||||
@ -0,0 +1,62 @@ | 
				
			|||||||
 | 
					Metadata-Version: 1.1 | 
				
			||||||
 | 
					Name: Jinja2 | 
				
			||||||
 | 
					Version: 2.10 | 
				
			||||||
 | 
					Summary: A small but fast and easy to use stand-alone template engine written in pure python. | 
				
			||||||
 | 
					Home-page: http://jinja.pocoo.org/ | 
				
			||||||
 | 
					Author: Armin Ronacher | 
				
			||||||
 | 
					Author-email: armin.ronacher@active-4.com | 
				
			||||||
 | 
					License: BSD | 
				
			||||||
 | 
					Description:  | 
				
			||||||
 | 
					        Jinja2 | 
				
			||||||
 | 
					        ~~~~~~ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Jinja2 is a template engine written in pure Python.  It provides a | 
				
			||||||
 | 
					        `Django`_ inspired non-XML syntax but supports inline expressions and | 
				
			||||||
 | 
					        an optional `sandboxed`_ environment. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Nutshell | 
				
			||||||
 | 
					        -------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Here a small example of a Jinja template:: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            {% extends 'base.html' %} | 
				
			||||||
 | 
					            {% block title %}Memberlist{% endblock %} | 
				
			||||||
 | 
					            {% block content %} | 
				
			||||||
 | 
					              <ul> | 
				
			||||||
 | 
					              {% for user in users %} | 
				
			||||||
 | 
					                <li><a href="{{ user.url }}">{{ user.username }}</a></li> | 
				
			||||||
 | 
					              {% endfor %} | 
				
			||||||
 | 
					              </ul> | 
				
			||||||
 | 
					            {% endblock %} | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Philosophy | 
				
			||||||
 | 
					        ---------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Application logic is for the controller but don't try to make the life | 
				
			||||||
 | 
					        for the template designer too hard by giving him too few functionality. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        For more informations visit the new `Jinja2 webpage`_ and `documentation`_. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. _sandboxed: https://en.wikipedia.org/wiki/Sandbox_(computer_security) | 
				
			||||||
 | 
					        .. _Django: https://www.djangoproject.com/ | 
				
			||||||
 | 
					        .. _Jinja2 webpage: http://jinja.pocoo.org/ | 
				
			||||||
 | 
					        .. _documentation: http://jinja.pocoo.org/2/documentation/ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					Platform: UNKNOWN | 
				
			||||||
 | 
					Classifier: Development Status :: 5 - Production/Stable | 
				
			||||||
 | 
					Classifier: Environment :: Web Environment | 
				
			||||||
 | 
					Classifier: Intended Audience :: Developers | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: BSD License | 
				
			||||||
 | 
					Classifier: Operating System :: OS Independent | 
				
			||||||
 | 
					Classifier: Programming Language :: Python | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.5 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.6 | 
				
			||||||
 | 
					Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content | 
				
			||||||
 | 
					Classifier: Topic :: Software Development :: Libraries :: Python Modules | 
				
			||||||
 | 
					Classifier: Topic :: Text Processing :: Markup :: HTML | 
				
			||||||
@ -0,0 +1,133 @@ | 
				
			|||||||
 | 
					AUTHORS | 
				
			||||||
 | 
					CHANGES.rst | 
				
			||||||
 | 
					LICENSE | 
				
			||||||
 | 
					MANIFEST.in | 
				
			||||||
 | 
					README.rst | 
				
			||||||
 | 
					setup.cfg | 
				
			||||||
 | 
					setup.py | 
				
			||||||
 | 
					Jinja2.egg-info/PKG-INFO | 
				
			||||||
 | 
					Jinja2.egg-info/SOURCES.txt | 
				
			||||||
 | 
					Jinja2.egg-info/dependency_links.txt | 
				
			||||||
 | 
					Jinja2.egg-info/entry_points.txt | 
				
			||||||
 | 
					Jinja2.egg-info/not-zip-safe | 
				
			||||||
 | 
					Jinja2.egg-info/requires.txt | 
				
			||||||
 | 
					Jinja2.egg-info/top_level.txt | 
				
			||||||
 | 
					artwork/jinjalogo.svg | 
				
			||||||
 | 
					docs/Makefile | 
				
			||||||
 | 
					docs/api.rst | 
				
			||||||
 | 
					docs/cache_extension.py | 
				
			||||||
 | 
					docs/changelog.rst | 
				
			||||||
 | 
					docs/conf.py | 
				
			||||||
 | 
					docs/contents.rst.inc | 
				
			||||||
 | 
					docs/extensions.rst | 
				
			||||||
 | 
					docs/faq.rst | 
				
			||||||
 | 
					docs/index.rst | 
				
			||||||
 | 
					docs/integration.rst | 
				
			||||||
 | 
					docs/intro.rst | 
				
			||||||
 | 
					docs/jinjaext.py | 
				
			||||||
 | 
					docs/jinjastyle.sty | 
				
			||||||
 | 
					docs/latexindex.rst | 
				
			||||||
 | 
					docs/logo.pdf | 
				
			||||||
 | 
					docs/nativetypes.rst | 
				
			||||||
 | 
					docs/sandbox.rst | 
				
			||||||
 | 
					docs/switching.rst | 
				
			||||||
 | 
					docs/templates.rst | 
				
			||||||
 | 
					docs/tricks.rst | 
				
			||||||
 | 
					docs/_static/.ignore | 
				
			||||||
 | 
					docs/_static/jinja-small.png | 
				
			||||||
 | 
					docs/_templates/sidebarintro.html | 
				
			||||||
 | 
					docs/_templates/sidebarlogo.html | 
				
			||||||
 | 
					docs/_themes/LICENSE | 
				
			||||||
 | 
					docs/_themes/README | 
				
			||||||
 | 
					docs/_themes/jinja/layout.html | 
				
			||||||
 | 
					docs/_themes/jinja/relations.html | 
				
			||||||
 | 
					docs/_themes/jinja/theme.conf | 
				
			||||||
 | 
					docs/_themes/jinja/static/jinja.css_t | 
				
			||||||
 | 
					examples/bench.py | 
				
			||||||
 | 
					examples/profile.py | 
				
			||||||
 | 
					examples/basic/cycle.py | 
				
			||||||
 | 
					examples/basic/debugger.py | 
				
			||||||
 | 
					examples/basic/inheritance.py | 
				
			||||||
 | 
					examples/basic/test.py | 
				
			||||||
 | 
					examples/basic/test_filter_and_linestatements.py | 
				
			||||||
 | 
					examples/basic/test_loop_filter.py | 
				
			||||||
 | 
					examples/basic/translate.py | 
				
			||||||
 | 
					examples/basic/templates/broken.html | 
				
			||||||
 | 
					examples/basic/templates/subbroken.html | 
				
			||||||
 | 
					examples/rwbench/djangoext.py | 
				
			||||||
 | 
					examples/rwbench/rwbench.py | 
				
			||||||
 | 
					examples/rwbench/django/_form.html | 
				
			||||||
 | 
					examples/rwbench/django/_input_field.html | 
				
			||||||
 | 
					examples/rwbench/django/_textarea.html | 
				
			||||||
 | 
					examples/rwbench/django/index.html | 
				
			||||||
 | 
					examples/rwbench/django/layout.html | 
				
			||||||
 | 
					examples/rwbench/genshi/helpers.html | 
				
			||||||
 | 
					examples/rwbench/genshi/index.html | 
				
			||||||
 | 
					examples/rwbench/genshi/layout.html | 
				
			||||||
 | 
					examples/rwbench/jinja/helpers.html | 
				
			||||||
 | 
					examples/rwbench/jinja/index.html | 
				
			||||||
 | 
					examples/rwbench/jinja/layout.html | 
				
			||||||
 | 
					examples/rwbench/mako/helpers.html | 
				
			||||||
 | 
					examples/rwbench/mako/index.html | 
				
			||||||
 | 
					examples/rwbench/mako/layout.html | 
				
			||||||
 | 
					ext/djangojinja2.py | 
				
			||||||
 | 
					ext/inlinegettext.py | 
				
			||||||
 | 
					ext/jinja.el | 
				
			||||||
 | 
					ext/Vim/jinja.vim | 
				
			||||||
 | 
					ext/django2jinja/django2jinja.py | 
				
			||||||
 | 
					ext/django2jinja/example.py | 
				
			||||||
 | 
					ext/django2jinja/templates/index.html | 
				
			||||||
 | 
					ext/django2jinja/templates/layout.html | 
				
			||||||
 | 
					ext/django2jinja/templates/subtemplate.html | 
				
			||||||
 | 
					jinja2/__init__.py | 
				
			||||||
 | 
					jinja2/_compat.py | 
				
			||||||
 | 
					jinja2/_identifier.py | 
				
			||||||
 | 
					jinja2/asyncfilters.py | 
				
			||||||
 | 
					jinja2/asyncsupport.py | 
				
			||||||
 | 
					jinja2/bccache.py | 
				
			||||||
 | 
					jinja2/compiler.py | 
				
			||||||
 | 
					jinja2/constants.py | 
				
			||||||
 | 
					jinja2/debug.py | 
				
			||||||
 | 
					jinja2/defaults.py | 
				
			||||||
 | 
					jinja2/environment.py | 
				
			||||||
 | 
					jinja2/exceptions.py | 
				
			||||||
 | 
					jinja2/ext.py | 
				
			||||||
 | 
					jinja2/filters.py | 
				
			||||||
 | 
					jinja2/idtracking.py | 
				
			||||||
 | 
					jinja2/lexer.py | 
				
			||||||
 | 
					jinja2/loaders.py | 
				
			||||||
 | 
					jinja2/meta.py | 
				
			||||||
 | 
					jinja2/nativetypes.py | 
				
			||||||
 | 
					jinja2/nodes.py | 
				
			||||||
 | 
					jinja2/optimizer.py | 
				
			||||||
 | 
					jinja2/parser.py | 
				
			||||||
 | 
					jinja2/runtime.py | 
				
			||||||
 | 
					jinja2/sandbox.py | 
				
			||||||
 | 
					jinja2/tests.py | 
				
			||||||
 | 
					jinja2/utils.py | 
				
			||||||
 | 
					jinja2/visitor.py | 
				
			||||||
 | 
					tests/conftest.py | 
				
			||||||
 | 
					tests/test_api.py | 
				
			||||||
 | 
					tests/test_async.py | 
				
			||||||
 | 
					tests/test_asyncfilters.py | 
				
			||||||
 | 
					tests/test_bytecode_cache.py | 
				
			||||||
 | 
					tests/test_core_tags.py | 
				
			||||||
 | 
					tests/test_debug.py | 
				
			||||||
 | 
					tests/test_ext.py | 
				
			||||||
 | 
					tests/test_features.py | 
				
			||||||
 | 
					tests/test_filters.py | 
				
			||||||
 | 
					tests/test_idtracking.py | 
				
			||||||
 | 
					tests/test_imports.py | 
				
			||||||
 | 
					tests/test_inheritance.py | 
				
			||||||
 | 
					tests/test_lexnparse.py | 
				
			||||||
 | 
					tests/test_loader.py | 
				
			||||||
 | 
					tests/test_nativetypes.py | 
				
			||||||
 | 
					tests/test_regression.py | 
				
			||||||
 | 
					tests/test_security.py | 
				
			||||||
 | 
					tests/test_tests.py | 
				
			||||||
 | 
					tests/test_utils.py | 
				
			||||||
 | 
					tests/res/__init__.py | 
				
			||||||
 | 
					tests/res/templates/broken.html | 
				
			||||||
 | 
					tests/res/templates/syntaxerror.html | 
				
			||||||
 | 
					tests/res/templates/test.html | 
				
			||||||
 | 
					tests/res/templates/foo/test.html | 
				
			||||||
@ -0,0 +1,4 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					    [babel.extractors] | 
				
			||||||
 | 
					    jinja2 = jinja2.ext:babel_extract[i18n] | 
				
			||||||
 | 
					     | 
				
			||||||
@ -0,0 +1,61 @@ | 
				
			|||||||
 | 
					../jinja2/lexer.py | 
				
			||||||
 | 
					../jinja2/idtracking.py | 
				
			||||||
 | 
					../jinja2/_identifier.py | 
				
			||||||
 | 
					../jinja2/nodes.py | 
				
			||||||
 | 
					../jinja2/asyncfilters.py | 
				
			||||||
 | 
					../jinja2/loaders.py | 
				
			||||||
 | 
					../jinja2/defaults.py | 
				
			||||||
 | 
					../jinja2/meta.py | 
				
			||||||
 | 
					../jinja2/compiler.py | 
				
			||||||
 | 
					../jinja2/environment.py | 
				
			||||||
 | 
					../jinja2/tests.py | 
				
			||||||
 | 
					../jinja2/sandbox.py | 
				
			||||||
 | 
					../jinja2/filters.py | 
				
			||||||
 | 
					../jinja2/exceptions.py | 
				
			||||||
 | 
					../jinja2/asyncsupport.py | 
				
			||||||
 | 
					../jinja2/visitor.py | 
				
			||||||
 | 
					../jinja2/constants.py | 
				
			||||||
 | 
					../jinja2/utils.py | 
				
			||||||
 | 
					../jinja2/ext.py | 
				
			||||||
 | 
					../jinja2/optimizer.py | 
				
			||||||
 | 
					../jinja2/nativetypes.py | 
				
			||||||
 | 
					../jinja2/parser.py | 
				
			||||||
 | 
					../jinja2/runtime.py | 
				
			||||||
 | 
					../jinja2/debug.py | 
				
			||||||
 | 
					../jinja2/_compat.py | 
				
			||||||
 | 
					../jinja2/bccache.py | 
				
			||||||
 | 
					../jinja2/__init__.py | 
				
			||||||
 | 
					../jinja2/lexer.pyc | 
				
			||||||
 | 
					../jinja2/idtracking.pyc | 
				
			||||||
 | 
					../jinja2/_identifier.pyc | 
				
			||||||
 | 
					../jinja2/nodes.pyc | 
				
			||||||
 | 
					../jinja2/asyncfilters.pyc | 
				
			||||||
 | 
					../jinja2/loaders.pyc | 
				
			||||||
 | 
					../jinja2/defaults.pyc | 
				
			||||||
 | 
					../jinja2/meta.pyc | 
				
			||||||
 | 
					../jinja2/compiler.pyc | 
				
			||||||
 | 
					../jinja2/environment.pyc | 
				
			||||||
 | 
					../jinja2/tests.pyc | 
				
			||||||
 | 
					../jinja2/sandbox.pyc | 
				
			||||||
 | 
					../jinja2/filters.pyc | 
				
			||||||
 | 
					../jinja2/exceptions.pyc | 
				
			||||||
 | 
					../jinja2/asyncsupport.pyc | 
				
			||||||
 | 
					../jinja2/visitor.pyc | 
				
			||||||
 | 
					../jinja2/constants.pyc | 
				
			||||||
 | 
					../jinja2/utils.pyc | 
				
			||||||
 | 
					../jinja2/ext.pyc | 
				
			||||||
 | 
					../jinja2/optimizer.pyc | 
				
			||||||
 | 
					../jinja2/nativetypes.pyc | 
				
			||||||
 | 
					../jinja2/parser.pyc | 
				
			||||||
 | 
					../jinja2/runtime.pyc | 
				
			||||||
 | 
					../jinja2/debug.pyc | 
				
			||||||
 | 
					../jinja2/_compat.pyc | 
				
			||||||
 | 
					../jinja2/bccache.pyc | 
				
			||||||
 | 
					../jinja2/__init__.pyc | 
				
			||||||
 | 
					not-zip-safe | 
				
			||||||
 | 
					entry_points.txt | 
				
			||||||
 | 
					dependency_links.txt | 
				
			||||||
 | 
					PKG-INFO | 
				
			||||||
 | 
					top_level.txt | 
				
			||||||
 | 
					requires.txt | 
				
			||||||
 | 
					SOURCES.txt | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,4 @@ | 
				
			|||||||
 | 
					MarkupSafe>=0.23 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[i18n] | 
				
			||||||
 | 
					Babel>=0.8 | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					jinja2 | 
				
			||||||
@ -0,0 +1,104 @@ | 
				
			|||||||
 | 
					Metadata-Version: 1.1 | 
				
			||||||
 | 
					Name: Werkzeug | 
				
			||||||
 | 
					Version: 0.14.1 | 
				
			||||||
 | 
					Summary: The comprehensive WSGI web application library. | 
				
			||||||
 | 
					Home-page: https://www.palletsprojects.org/p/werkzeug/ | 
				
			||||||
 | 
					Author: Armin Ronacher | 
				
			||||||
 | 
					Author-email: armin.ronacher@active-4.com | 
				
			||||||
 | 
					License: BSD | 
				
			||||||
 | 
					Description: Werkzeug | 
				
			||||||
 | 
					        ======== | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Werkzeug is a comprehensive `WSGI`_ web application library. It began as | 
				
			||||||
 | 
					        a simple collection of various utilities for WSGI applications and has | 
				
			||||||
 | 
					        become one of the most advanced WSGI utility libraries. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        It includes: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        * An interactive debugger that allows inspecting stack traces and source | 
				
			||||||
 | 
					          code in the browser with an interactive interpreter for any frame in | 
				
			||||||
 | 
					          the stack. | 
				
			||||||
 | 
					        * A full-featured request object with objects to interact with headers, | 
				
			||||||
 | 
					          query args, form data, files, and cookies. | 
				
			||||||
 | 
					        * A response object that can wrap other WSGI applications and handle | 
				
			||||||
 | 
					          streaming data. | 
				
			||||||
 | 
					        * A routing system for matching URLs to endpoints and generating URLs | 
				
			||||||
 | 
					          for endpoints, with an extensible system for capturing variables from | 
				
			||||||
 | 
					          URLs. | 
				
			||||||
 | 
					        * HTTP utilities to handle entity tags, cache control, dates, user | 
				
			||||||
 | 
					          agents, cookies, files, and more. | 
				
			||||||
 | 
					        * A threaded WSGI server for use while developing applications locally. | 
				
			||||||
 | 
					        * A test client for simulating HTTP requests during testing without | 
				
			||||||
 | 
					          requiring running a server. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up | 
				
			||||||
 | 
					        to the developer to choose a template engine, database adapter, and even | 
				
			||||||
 | 
					        how to handle requests. It can be used to build all sorts of end user | 
				
			||||||
 | 
					        applications such as blogs, wikis, or bulletin boards. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        `Flask`_ wraps Werkzeug, using it to handle the details of WSGI while | 
				
			||||||
 | 
					        providing more structure and patterns for defining powerful | 
				
			||||||
 | 
					        applications. | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Installing | 
				
			||||||
 | 
					        ---------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Install and update using `pip`_: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: text | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            pip install -U Werkzeug | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        A Simple Example | 
				
			||||||
 | 
					        ---------------- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. code-block:: python | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            from werkzeug.wrappers import Request, Response | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            @Request.application | 
				
			||||||
 | 
					            def application(request): | 
				
			||||||
 | 
					                return Response('Hello, World!') | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					            if __name__ == '__main__': | 
				
			||||||
 | 
					                from werkzeug.serving import run_simple | 
				
			||||||
 | 
					                run_simple('localhost', 4000, application) | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        Links | 
				
			||||||
 | 
					        ----- | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        * Website: https://www.palletsprojects.com/p/werkzeug/ | 
				
			||||||
 | 
					        * Releases: https://pypi.org/project/Werkzeug/ | 
				
			||||||
 | 
					        * Code: https://github.com/pallets/werkzeug | 
				
			||||||
 | 
					        * Issue tracker: https://github.com/pallets/werkzeug/issues | 
				
			||||||
 | 
					        * Test status: | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					          * Linux, Mac: https://travis-ci.org/pallets/werkzeug | 
				
			||||||
 | 
					          * Windows: https://ci.appveyor.com/project/davidism/werkzeug | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        * Test coverage: https://codecov.io/gh/pallets/werkzeug | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					        .. _WSGI: https://wsgi.readthedocs.io/en/latest/ | 
				
			||||||
 | 
					        .. _Flask: https://www.palletsprojects.com/p/flask/ | 
				
			||||||
 | 
					        .. _pip: https://pip.pypa.io/en/stable/quickstart/ | 
				
			||||||
 | 
					         | 
				
			||||||
 | 
					Platform: any | 
				
			||||||
 | 
					Classifier: Development Status :: 5 - Production/Stable | 
				
			||||||
 | 
					Classifier: Environment :: Web Environment | 
				
			||||||
 | 
					Classifier: Intended Audience :: Developers | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: BSD License | 
				
			||||||
 | 
					Classifier: Operating System :: OS Independent | 
				
			||||||
 | 
					Classifier: Programming Language :: Python | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.5 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.6 | 
				
			||||||
 | 
					Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content | 
				
			||||||
 | 
					Classifier: Topic :: Software Development :: Libraries :: Python Modules | 
				
			||||||
@ -0,0 +1,299 @@ | 
				
			|||||||
 | 
					AUTHORS | 
				
			||||||
 | 
					CHANGES.rst | 
				
			||||||
 | 
					LICENSE | 
				
			||||||
 | 
					MANIFEST.in | 
				
			||||||
 | 
					Makefile | 
				
			||||||
 | 
					README.rst | 
				
			||||||
 | 
					setup.cfg | 
				
			||||||
 | 
					setup.py | 
				
			||||||
 | 
					tox.ini | 
				
			||||||
 | 
					Werkzeug.egg-info/PKG-INFO | 
				
			||||||
 | 
					Werkzeug.egg-info/SOURCES.txt | 
				
			||||||
 | 
					Werkzeug.egg-info/dependency_links.txt | 
				
			||||||
 | 
					Werkzeug.egg-info/not-zip-safe | 
				
			||||||
 | 
					Werkzeug.egg-info/requires.txt | 
				
			||||||
 | 
					Werkzeug.egg-info/top_level.txt | 
				
			||||||
 | 
					artwork/.DS_Store | 
				
			||||||
 | 
					artwork/logo.png | 
				
			||||||
 | 
					artwork/logo.svg | 
				
			||||||
 | 
					docs/.DS_Store | 
				
			||||||
 | 
					docs/Makefile | 
				
			||||||
 | 
					docs/changes.rst | 
				
			||||||
 | 
					docs/conf.py | 
				
			||||||
 | 
					docs/contents.rst.inc | 
				
			||||||
 | 
					docs/datastructures.rst | 
				
			||||||
 | 
					docs/debug.rst | 
				
			||||||
 | 
					docs/exceptions.rst | 
				
			||||||
 | 
					docs/filesystem.rst | 
				
			||||||
 | 
					docs/http.rst | 
				
			||||||
 | 
					docs/index.rst | 
				
			||||||
 | 
					docs/installation.rst | 
				
			||||||
 | 
					docs/latexindex.rst | 
				
			||||||
 | 
					docs/levels.rst | 
				
			||||||
 | 
					docs/local.rst | 
				
			||||||
 | 
					docs/logo.pdf | 
				
			||||||
 | 
					docs/make.bat | 
				
			||||||
 | 
					docs/makearchive.py | 
				
			||||||
 | 
					docs/middlewares.rst | 
				
			||||||
 | 
					docs/python3.rst | 
				
			||||||
 | 
					docs/quickstart.rst | 
				
			||||||
 | 
					docs/request_data.rst | 
				
			||||||
 | 
					docs/routing.rst | 
				
			||||||
 | 
					docs/serving.rst | 
				
			||||||
 | 
					docs/terms.rst | 
				
			||||||
 | 
					docs/test.rst | 
				
			||||||
 | 
					docs/transition.rst | 
				
			||||||
 | 
					docs/tutorial.rst | 
				
			||||||
 | 
					docs/unicode.rst | 
				
			||||||
 | 
					docs/urls.rst | 
				
			||||||
 | 
					docs/utils.rst | 
				
			||||||
 | 
					docs/werkzeugext.py | 
				
			||||||
 | 
					docs/werkzeugstyle.sty | 
				
			||||||
 | 
					docs/wrappers.rst | 
				
			||||||
 | 
					docs/wsgi.rst | 
				
			||||||
 | 
					docs/_static/background.png | 
				
			||||||
 | 
					docs/_static/codebackground.png | 
				
			||||||
 | 
					docs/_static/contents.png | 
				
			||||||
 | 
					docs/_static/debug-screenshot.png | 
				
			||||||
 | 
					docs/_static/favicon.ico | 
				
			||||||
 | 
					docs/_static/header.png | 
				
			||||||
 | 
					docs/_static/navigation.png | 
				
			||||||
 | 
					docs/_static/navigation_active.png | 
				
			||||||
 | 
					docs/_static/shortly.png | 
				
			||||||
 | 
					docs/_static/shorty-screenshot.png | 
				
			||||||
 | 
					docs/_static/style.css | 
				
			||||||
 | 
					docs/_static/werkzeug.js | 
				
			||||||
 | 
					docs/_static/werkzeug.png | 
				
			||||||
 | 
					docs/_templates/sidebarintro.html | 
				
			||||||
 | 
					docs/_templates/sidebarlogo.html | 
				
			||||||
 | 
					docs/contrib/atom.rst | 
				
			||||||
 | 
					docs/contrib/cache.rst | 
				
			||||||
 | 
					docs/contrib/fixers.rst | 
				
			||||||
 | 
					docs/contrib/index.rst | 
				
			||||||
 | 
					docs/contrib/iterio.rst | 
				
			||||||
 | 
					docs/contrib/lint.rst | 
				
			||||||
 | 
					docs/contrib/profiler.rst | 
				
			||||||
 | 
					docs/contrib/securecookie.rst | 
				
			||||||
 | 
					docs/contrib/sessions.rst | 
				
			||||||
 | 
					docs/contrib/wrappers.rst | 
				
			||||||
 | 
					docs/deployment/cgi.rst | 
				
			||||||
 | 
					docs/deployment/fastcgi.rst | 
				
			||||||
 | 
					docs/deployment/index.rst | 
				
			||||||
 | 
					docs/deployment/mod_wsgi.rst | 
				
			||||||
 | 
					docs/deployment/proxying.rst | 
				
			||||||
 | 
					examples/README | 
				
			||||||
 | 
					examples/cookieauth.py | 
				
			||||||
 | 
					examples/httpbasicauth.py | 
				
			||||||
 | 
					examples/manage-coolmagic.py | 
				
			||||||
 | 
					examples/manage-couchy.py | 
				
			||||||
 | 
					examples/manage-cupoftee.py | 
				
			||||||
 | 
					examples/manage-i18nurls.py | 
				
			||||||
 | 
					examples/manage-plnt.py | 
				
			||||||
 | 
					examples/manage-shorty.py | 
				
			||||||
 | 
					examples/manage-simplewiki.py | 
				
			||||||
 | 
					examples/manage-webpylike.py | 
				
			||||||
 | 
					examples/upload.py | 
				
			||||||
 | 
					examples/contrib/README | 
				
			||||||
 | 
					examples/contrib/securecookie.py | 
				
			||||||
 | 
					examples/contrib/sessions.py | 
				
			||||||
 | 
					examples/coolmagic/__init__.py | 
				
			||||||
 | 
					examples/coolmagic/application.py | 
				
			||||||
 | 
					examples/coolmagic/helpers.py | 
				
			||||||
 | 
					examples/coolmagic/utils.py | 
				
			||||||
 | 
					examples/coolmagic/public/style.css | 
				
			||||||
 | 
					examples/coolmagic/templates/layout.html | 
				
			||||||
 | 
					examples/coolmagic/templates/static/about.html | 
				
			||||||
 | 
					examples/coolmagic/templates/static/index.html | 
				
			||||||
 | 
					examples/coolmagic/templates/static/not_found.html | 
				
			||||||
 | 
					examples/coolmagic/views/__init__.py | 
				
			||||||
 | 
					examples/coolmagic/views/static.py | 
				
			||||||
 | 
					examples/couchy/README | 
				
			||||||
 | 
					examples/couchy/__init__.py | 
				
			||||||
 | 
					examples/couchy/application.py | 
				
			||||||
 | 
					examples/couchy/models.py | 
				
			||||||
 | 
					examples/couchy/utils.py | 
				
			||||||
 | 
					examples/couchy/views.py | 
				
			||||||
 | 
					examples/couchy/static/style.css | 
				
			||||||
 | 
					examples/couchy/templates/display.html | 
				
			||||||
 | 
					examples/couchy/templates/layout.html | 
				
			||||||
 | 
					examples/couchy/templates/list.html | 
				
			||||||
 | 
					examples/couchy/templates/new.html | 
				
			||||||
 | 
					examples/couchy/templates/not_found.html | 
				
			||||||
 | 
					examples/cupoftee/__init__.py | 
				
			||||||
 | 
					examples/cupoftee/application.py | 
				
			||||||
 | 
					examples/cupoftee/db.py | 
				
			||||||
 | 
					examples/cupoftee/network.py | 
				
			||||||
 | 
					examples/cupoftee/pages.py | 
				
			||||||
 | 
					examples/cupoftee/utils.py | 
				
			||||||
 | 
					examples/cupoftee/shared/content.png | 
				
			||||||
 | 
					examples/cupoftee/shared/down.png | 
				
			||||||
 | 
					examples/cupoftee/shared/favicon.ico | 
				
			||||||
 | 
					examples/cupoftee/shared/header.png | 
				
			||||||
 | 
					examples/cupoftee/shared/logo.png | 
				
			||||||
 | 
					examples/cupoftee/shared/style.css | 
				
			||||||
 | 
					examples/cupoftee/shared/up.png | 
				
			||||||
 | 
					examples/cupoftee/templates/layout.html | 
				
			||||||
 | 
					examples/cupoftee/templates/missingpage.html | 
				
			||||||
 | 
					examples/cupoftee/templates/search.html | 
				
			||||||
 | 
					examples/cupoftee/templates/server.html | 
				
			||||||
 | 
					examples/cupoftee/templates/serverlist.html | 
				
			||||||
 | 
					examples/i18nurls/__init__.py | 
				
			||||||
 | 
					examples/i18nurls/application.py | 
				
			||||||
 | 
					examples/i18nurls/urls.py | 
				
			||||||
 | 
					examples/i18nurls/views.py | 
				
			||||||
 | 
					examples/i18nurls/templates/about.html | 
				
			||||||
 | 
					examples/i18nurls/templates/blog.html | 
				
			||||||
 | 
					examples/i18nurls/templates/index.html | 
				
			||||||
 | 
					examples/i18nurls/templates/layout.html | 
				
			||||||
 | 
					examples/partial/README | 
				
			||||||
 | 
					examples/partial/complex_routing.py | 
				
			||||||
 | 
					examples/plnt/__init__.py | 
				
			||||||
 | 
					examples/plnt/database.py | 
				
			||||||
 | 
					examples/plnt/sync.py | 
				
			||||||
 | 
					examples/plnt/utils.py | 
				
			||||||
 | 
					examples/plnt/views.py | 
				
			||||||
 | 
					examples/plnt/webapp.py | 
				
			||||||
 | 
					examples/plnt/shared/style.css | 
				
			||||||
 | 
					examples/plnt/templates/about.html | 
				
			||||||
 | 
					examples/plnt/templates/index.html | 
				
			||||||
 | 
					examples/plnt/templates/layout.html | 
				
			||||||
 | 
					examples/shortly/shortly.py | 
				
			||||||
 | 
					examples/shortly/static/style.css | 
				
			||||||
 | 
					examples/shortly/templates/404.html | 
				
			||||||
 | 
					examples/shortly/templates/layout.html | 
				
			||||||
 | 
					examples/shortly/templates/new_url.html | 
				
			||||||
 | 
					examples/shortly/templates/short_link_details.html | 
				
			||||||
 | 
					examples/shorty/__init__.py | 
				
			||||||
 | 
					examples/shorty/application.py | 
				
			||||||
 | 
					examples/shorty/models.py | 
				
			||||||
 | 
					examples/shorty/utils.py | 
				
			||||||
 | 
					examples/shorty/views.py | 
				
			||||||
 | 
					examples/shorty/static/style.css | 
				
			||||||
 | 
					examples/shorty/templates/display.html | 
				
			||||||
 | 
					examples/shorty/templates/layout.html | 
				
			||||||
 | 
					examples/shorty/templates/list.html | 
				
			||||||
 | 
					examples/shorty/templates/new.html | 
				
			||||||
 | 
					examples/shorty/templates/not_found.html | 
				
			||||||
 | 
					examples/simplewiki/__init__.py | 
				
			||||||
 | 
					examples/simplewiki/actions.py | 
				
			||||||
 | 
					examples/simplewiki/application.py | 
				
			||||||
 | 
					examples/simplewiki/database.py | 
				
			||||||
 | 
					examples/simplewiki/specialpages.py | 
				
			||||||
 | 
					examples/simplewiki/utils.py | 
				
			||||||
 | 
					examples/simplewiki/shared/style.css | 
				
			||||||
 | 
					examples/simplewiki/templates/action_diff.html | 
				
			||||||
 | 
					examples/simplewiki/templates/action_edit.html | 
				
			||||||
 | 
					examples/simplewiki/templates/action_log.html | 
				
			||||||
 | 
					examples/simplewiki/templates/action_revert.html | 
				
			||||||
 | 
					examples/simplewiki/templates/action_show.html | 
				
			||||||
 | 
					examples/simplewiki/templates/layout.html | 
				
			||||||
 | 
					examples/simplewiki/templates/macros.xml | 
				
			||||||
 | 
					examples/simplewiki/templates/missing_action.html | 
				
			||||||
 | 
					examples/simplewiki/templates/page_index.html | 
				
			||||||
 | 
					examples/simplewiki/templates/page_missing.html | 
				
			||||||
 | 
					examples/simplewiki/templates/recent_changes.html | 
				
			||||||
 | 
					examples/webpylike/example.py | 
				
			||||||
 | 
					examples/webpylike/webpylike.py | 
				
			||||||
 | 
					tests/__init__.py | 
				
			||||||
 | 
					tests/conftest.py | 
				
			||||||
 | 
					tests/test_compat.py | 
				
			||||||
 | 
					tests/test_datastructures.py | 
				
			||||||
 | 
					tests/test_debug.py | 
				
			||||||
 | 
					tests/test_exceptions.py | 
				
			||||||
 | 
					tests/test_formparser.py | 
				
			||||||
 | 
					tests/test_http.py | 
				
			||||||
 | 
					tests/test_internal.py | 
				
			||||||
 | 
					tests/test_local.py | 
				
			||||||
 | 
					tests/test_routing.py | 
				
			||||||
 | 
					tests/test_security.py | 
				
			||||||
 | 
					tests/test_serving.py | 
				
			||||||
 | 
					tests/test_test.py | 
				
			||||||
 | 
					tests/test_urls.py | 
				
			||||||
 | 
					tests/test_utils.py | 
				
			||||||
 | 
					tests/test_wrappers.py | 
				
			||||||
 | 
					tests/test_wsgi.py | 
				
			||||||
 | 
					tests/contrib/__init__.py | 
				
			||||||
 | 
					tests/contrib/test_atom.py | 
				
			||||||
 | 
					tests/contrib/test_cache.py | 
				
			||||||
 | 
					tests/contrib/test_fixers.py | 
				
			||||||
 | 
					tests/contrib/test_iterio.py | 
				
			||||||
 | 
					tests/contrib/test_securecookie.py | 
				
			||||||
 | 
					tests/contrib/test_sessions.py | 
				
			||||||
 | 
					tests/contrib/test_wrappers.py | 
				
			||||||
 | 
					tests/contrib/cache/conftest.py | 
				
			||||||
 | 
					tests/contrib/cache/test_cache.py | 
				
			||||||
 | 
					tests/hypothesis/__init__.py | 
				
			||||||
 | 
					tests/hypothesis/test_urls.py | 
				
			||||||
 | 
					tests/multipart/__init__.py | 
				
			||||||
 | 
					tests/multipart/ie7_full_path_request.txt | 
				
			||||||
 | 
					tests/multipart/test_collect.py | 
				
			||||||
 | 
					tests/multipart/firefox3-2png1txt/file1.png | 
				
			||||||
 | 
					tests/multipart/firefox3-2png1txt/file2.png | 
				
			||||||
 | 
					tests/multipart/firefox3-2png1txt/request.txt | 
				
			||||||
 | 
					tests/multipart/firefox3-2png1txt/text.txt | 
				
			||||||
 | 
					tests/multipart/firefox3-2pnglongtext/file1.png | 
				
			||||||
 | 
					tests/multipart/firefox3-2pnglongtext/file2.png | 
				
			||||||
 | 
					tests/multipart/firefox3-2pnglongtext/request.txt | 
				
			||||||
 | 
					tests/multipart/firefox3-2pnglongtext/text.txt | 
				
			||||||
 | 
					tests/multipart/ie6-2png1txt/file1.png | 
				
			||||||
 | 
					tests/multipart/ie6-2png1txt/file2.png | 
				
			||||||
 | 
					tests/multipart/ie6-2png1txt/request.txt | 
				
			||||||
 | 
					tests/multipart/ie6-2png1txt/text.txt | 
				
			||||||
 | 
					tests/multipart/opera8-2png1txt/file1.png | 
				
			||||||
 | 
					tests/multipart/opera8-2png1txt/file2.png | 
				
			||||||
 | 
					tests/multipart/opera8-2png1txt/request.txt | 
				
			||||||
 | 
					tests/multipart/opera8-2png1txt/text.txt | 
				
			||||||
 | 
					tests/multipart/webkit3-2png1txt/file1.png | 
				
			||||||
 | 
					tests/multipart/webkit3-2png1txt/file2.png | 
				
			||||||
 | 
					tests/multipart/webkit3-2png1txt/request.txt | 
				
			||||||
 | 
					tests/multipart/webkit3-2png1txt/text.txt | 
				
			||||||
 | 
					tests/res/chunked.txt | 
				
			||||||
 | 
					tests/res/test.txt | 
				
			||||||
 | 
					werkzeug/__init__.py | 
				
			||||||
 | 
					werkzeug/_compat.py | 
				
			||||||
 | 
					werkzeug/_internal.py | 
				
			||||||
 | 
					werkzeug/_reloader.py | 
				
			||||||
 | 
					werkzeug/datastructures.py | 
				
			||||||
 | 
					werkzeug/exceptions.py | 
				
			||||||
 | 
					werkzeug/filesystem.py | 
				
			||||||
 | 
					werkzeug/formparser.py | 
				
			||||||
 | 
					werkzeug/http.py | 
				
			||||||
 | 
					werkzeug/local.py | 
				
			||||||
 | 
					werkzeug/posixemulation.py | 
				
			||||||
 | 
					werkzeug/routing.py | 
				
			||||||
 | 
					werkzeug/security.py | 
				
			||||||
 | 
					werkzeug/serving.py | 
				
			||||||
 | 
					werkzeug/test.py | 
				
			||||||
 | 
					werkzeug/testapp.py | 
				
			||||||
 | 
					werkzeug/urls.py | 
				
			||||||
 | 
					werkzeug/useragents.py | 
				
			||||||
 | 
					werkzeug/utils.py | 
				
			||||||
 | 
					werkzeug/websocket.py | 
				
			||||||
 | 
					werkzeug/wrappers.py | 
				
			||||||
 | 
					werkzeug/wsgi.py | 
				
			||||||
 | 
					werkzeug/contrib/__init__.py | 
				
			||||||
 | 
					werkzeug/contrib/atom.py | 
				
			||||||
 | 
					werkzeug/contrib/cache.py | 
				
			||||||
 | 
					werkzeug/contrib/fixers.py | 
				
			||||||
 | 
					werkzeug/contrib/iterio.py | 
				
			||||||
 | 
					werkzeug/contrib/jsrouting.py | 
				
			||||||
 | 
					werkzeug/contrib/limiter.py | 
				
			||||||
 | 
					werkzeug/contrib/lint.py | 
				
			||||||
 | 
					werkzeug/contrib/profiler.py | 
				
			||||||
 | 
					werkzeug/contrib/securecookie.py | 
				
			||||||
 | 
					werkzeug/contrib/sessions.py | 
				
			||||||
 | 
					werkzeug/contrib/testtools.py | 
				
			||||||
 | 
					werkzeug/contrib/wrappers.py | 
				
			||||||
 | 
					werkzeug/debug/__init__.py | 
				
			||||||
 | 
					werkzeug/debug/console.py | 
				
			||||||
 | 
					werkzeug/debug/repr.py | 
				
			||||||
 | 
					werkzeug/debug/tbtools.py | 
				
			||||||
 | 
					werkzeug/debug/shared/FONT_LICENSE | 
				
			||||||
 | 
					werkzeug/debug/shared/console.png | 
				
			||||||
 | 
					werkzeug/debug/shared/debugger.js | 
				
			||||||
 | 
					werkzeug/debug/shared/jquery.js | 
				
			||||||
 | 
					werkzeug/debug/shared/less.png | 
				
			||||||
 | 
					werkzeug/debug/shared/more.png | 
				
			||||||
 | 
					werkzeug/debug/shared/source.png | 
				
			||||||
 | 
					werkzeug/debug/shared/style.css | 
				
			||||||
 | 
					werkzeug/debug/shared/ubuntu.ttf | 
				
			||||||
@ -0,0 +1,93 @@ | 
				
			|||||||
 | 
					../werkzeug/_reloader.py | 
				
			||||||
 | 
					../werkzeug/_internal.py | 
				
			||||||
 | 
					../werkzeug/serving.py | 
				
			||||||
 | 
					../werkzeug/local.py | 
				
			||||||
 | 
					../werkzeug/filesystem.py | 
				
			||||||
 | 
					../werkzeug/security.py | 
				
			||||||
 | 
					../werkzeug/__init__.py | 
				
			||||||
 | 
					../werkzeug/test.py | 
				
			||||||
 | 
					../werkzeug/formparser.py | 
				
			||||||
 | 
					../werkzeug/posixemulation.py | 
				
			||||||
 | 
					../werkzeug/utils.py | 
				
			||||||
 | 
					../werkzeug/wrappers.py | 
				
			||||||
 | 
					../werkzeug/routing.py | 
				
			||||||
 | 
					../werkzeug/http.py | 
				
			||||||
 | 
					../werkzeug/useragents.py | 
				
			||||||
 | 
					../werkzeug/exceptions.py | 
				
			||||||
 | 
					../werkzeug/_compat.py | 
				
			||||||
 | 
					../werkzeug/datastructures.py | 
				
			||||||
 | 
					../werkzeug/urls.py | 
				
			||||||
 | 
					../werkzeug/websocket.py | 
				
			||||||
 | 
					../werkzeug/wsgi.py | 
				
			||||||
 | 
					../werkzeug/testapp.py | 
				
			||||||
 | 
					../werkzeug/contrib/sessions.py | 
				
			||||||
 | 
					../werkzeug/contrib/cache.py | 
				
			||||||
 | 
					../werkzeug/contrib/__init__.py | 
				
			||||||
 | 
					../werkzeug/contrib/testtools.py | 
				
			||||||
 | 
					../werkzeug/contrib/wrappers.py | 
				
			||||||
 | 
					../werkzeug/contrib/jsrouting.py | 
				
			||||||
 | 
					../werkzeug/contrib/fixers.py | 
				
			||||||
 | 
					../werkzeug/contrib/profiler.py | 
				
			||||||
 | 
					../werkzeug/contrib/iterio.py | 
				
			||||||
 | 
					../werkzeug/contrib/atom.py | 
				
			||||||
 | 
					../werkzeug/contrib/securecookie.py | 
				
			||||||
 | 
					../werkzeug/contrib/limiter.py | 
				
			||||||
 | 
					../werkzeug/contrib/lint.py | 
				
			||||||
 | 
					../werkzeug/debug/console.py | 
				
			||||||
 | 
					../werkzeug/debug/tbtools.py | 
				
			||||||
 | 
					../werkzeug/debug/__init__.py | 
				
			||||||
 | 
					../werkzeug/debug/repr.py | 
				
			||||||
 | 
					../werkzeug/debug/shared/FONT_LICENSE | 
				
			||||||
 | 
					../werkzeug/debug/shared/console.png | 
				
			||||||
 | 
					../werkzeug/debug/shared/debugger.js | 
				
			||||||
 | 
					../werkzeug/debug/shared/jquery.js | 
				
			||||||
 | 
					../werkzeug/debug/shared/less.png | 
				
			||||||
 | 
					../werkzeug/debug/shared/more.png | 
				
			||||||
 | 
					../werkzeug/debug/shared/source.png | 
				
			||||||
 | 
					../werkzeug/debug/shared/style.css | 
				
			||||||
 | 
					../werkzeug/debug/shared/ubuntu.ttf | 
				
			||||||
 | 
					../werkzeug/_reloader.pyc | 
				
			||||||
 | 
					../werkzeug/_internal.pyc | 
				
			||||||
 | 
					../werkzeug/serving.pyc | 
				
			||||||
 | 
					../werkzeug/local.pyc | 
				
			||||||
 | 
					../werkzeug/filesystem.pyc | 
				
			||||||
 | 
					../werkzeug/security.pyc | 
				
			||||||
 | 
					../werkzeug/__init__.pyc | 
				
			||||||
 | 
					../werkzeug/test.pyc | 
				
			||||||
 | 
					../werkzeug/formparser.pyc | 
				
			||||||
 | 
					../werkzeug/posixemulation.pyc | 
				
			||||||
 | 
					../werkzeug/utils.pyc | 
				
			||||||
 | 
					../werkzeug/wrappers.pyc | 
				
			||||||
 | 
					../werkzeug/routing.pyc | 
				
			||||||
 | 
					../werkzeug/http.pyc | 
				
			||||||
 | 
					../werkzeug/useragents.pyc | 
				
			||||||
 | 
					../werkzeug/exceptions.pyc | 
				
			||||||
 | 
					../werkzeug/_compat.pyc | 
				
			||||||
 | 
					../werkzeug/datastructures.pyc | 
				
			||||||
 | 
					../werkzeug/urls.pyc | 
				
			||||||
 | 
					../werkzeug/websocket.pyc | 
				
			||||||
 | 
					../werkzeug/wsgi.pyc | 
				
			||||||
 | 
					../werkzeug/testapp.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/sessions.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/cache.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/__init__.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/testtools.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/wrappers.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/jsrouting.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/fixers.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/profiler.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/iterio.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/atom.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/securecookie.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/limiter.pyc | 
				
			||||||
 | 
					../werkzeug/contrib/lint.pyc | 
				
			||||||
 | 
					../werkzeug/debug/console.pyc | 
				
			||||||
 | 
					../werkzeug/debug/tbtools.pyc | 
				
			||||||
 | 
					../werkzeug/debug/__init__.pyc | 
				
			||||||
 | 
					../werkzeug/debug/repr.pyc | 
				
			||||||
 | 
					PKG-INFO | 
				
			||||||
 | 
					not-zip-safe | 
				
			||||||
 | 
					SOURCES.txt | 
				
			||||||
 | 
					requires.txt | 
				
			||||||
 | 
					top_level.txt | 
				
			||||||
 | 
					dependency_links.txt | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,12 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					[dev] | 
				
			||||||
 | 
					pytest | 
				
			||||||
 | 
					coverage | 
				
			||||||
 | 
					tox | 
				
			||||||
 | 
					sphinx | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[termcolor] | 
				
			||||||
 | 
					termcolor | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[watchdog] | 
				
			||||||
 | 
					watchdog | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					werkzeug | 
				
			||||||
@ -0,0 +1,10 @@ | 
				
			|||||||
 | 
					#!/usr/local/bin/python | 
				
			||||||
 | 
					# EASY-INSTALL-ENTRY-SCRIPT: 'Flask==1.0.2','console_scripts','flask' | 
				
			||||||
 | 
					__requires__ = 'Flask==1.0.2' | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					from pkg_resources import load_entry_point | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__': | 
				
			||||||
 | 
					    sys.exit( | 
				
			||||||
 | 
					        load_entry_point('Flask==1.0.2', 'console_scripts', 'flask')() | 
				
			||||||
 | 
					    ) | 
				
			||||||
@ -0,0 +1,13 @@ | 
				
			|||||||
 | 
					Metadata-Version: 1.1 | 
				
			||||||
 | 
					Name: click | 
				
			||||||
 | 
					Version: 6.7 | 
				
			||||||
 | 
					Summary: A simple wrapper around optparse for powerful command line utilities. | 
				
			||||||
 | 
					Home-page: http://github.com/mitsuhiko/click | 
				
			||||||
 | 
					Author: Armin Ronacher | 
				
			||||||
 | 
					Author-email: armin.ronacher@active-4.com | 
				
			||||||
 | 
					License: UNKNOWN | 
				
			||||||
 | 
					Description: UNKNOWN | 
				
			||||||
 | 
					Platform: UNKNOWN | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: BSD License | 
				
			||||||
 | 
					Classifier: Programming Language :: Python | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3 | 
				
			||||||
@ -0,0 +1,114 @@ | 
				
			|||||||
 | 
					CHANGES | 
				
			||||||
 | 
					LICENSE | 
				
			||||||
 | 
					MANIFEST.in | 
				
			||||||
 | 
					Makefile | 
				
			||||||
 | 
					README | 
				
			||||||
 | 
					setup.cfg | 
				
			||||||
 | 
					setup.py | 
				
			||||||
 | 
					artwork/logo.svg | 
				
			||||||
 | 
					click/__init__.py | 
				
			||||||
 | 
					click/_bashcomplete.py | 
				
			||||||
 | 
					click/_compat.py | 
				
			||||||
 | 
					click/_termui_impl.py | 
				
			||||||
 | 
					click/_textwrap.py | 
				
			||||||
 | 
					click/_unicodefun.py | 
				
			||||||
 | 
					click/_winconsole.py | 
				
			||||||
 | 
					click/core.py | 
				
			||||||
 | 
					click/decorators.py | 
				
			||||||
 | 
					click/exceptions.py | 
				
			||||||
 | 
					click/formatting.py | 
				
			||||||
 | 
					click/globals.py | 
				
			||||||
 | 
					click/parser.py | 
				
			||||||
 | 
					click/termui.py | 
				
			||||||
 | 
					click/testing.py | 
				
			||||||
 | 
					click/types.py | 
				
			||||||
 | 
					click/utils.py | 
				
			||||||
 | 
					click.egg-info/PKG-INFO | 
				
			||||||
 | 
					click.egg-info/SOURCES.txt | 
				
			||||||
 | 
					click.egg-info/dependency_links.txt | 
				
			||||||
 | 
					click.egg-info/top_level.txt | 
				
			||||||
 | 
					docs/Makefile | 
				
			||||||
 | 
					docs/advanced.rst | 
				
			||||||
 | 
					docs/api.rst | 
				
			||||||
 | 
					docs/arguments.rst | 
				
			||||||
 | 
					docs/bashcomplete.rst | 
				
			||||||
 | 
					docs/changelog.rst | 
				
			||||||
 | 
					docs/clickdoctools.py | 
				
			||||||
 | 
					docs/commands.rst | 
				
			||||||
 | 
					docs/complex.rst | 
				
			||||||
 | 
					docs/conf.py | 
				
			||||||
 | 
					docs/contrib.rst | 
				
			||||||
 | 
					docs/documentation.rst | 
				
			||||||
 | 
					docs/exceptions.rst | 
				
			||||||
 | 
					docs/index.rst | 
				
			||||||
 | 
					docs/license.rst | 
				
			||||||
 | 
					docs/make.bat | 
				
			||||||
 | 
					docs/options.rst | 
				
			||||||
 | 
					docs/parameters.rst | 
				
			||||||
 | 
					docs/prompts.rst | 
				
			||||||
 | 
					docs/python3.rst | 
				
			||||||
 | 
					docs/quickstart.rst | 
				
			||||||
 | 
					docs/setuptools.rst | 
				
			||||||
 | 
					docs/testing.rst | 
				
			||||||
 | 
					docs/upgrading.rst | 
				
			||||||
 | 
					docs/utils.rst | 
				
			||||||
 | 
					docs/why.rst | 
				
			||||||
 | 
					docs/wincmd.rst | 
				
			||||||
 | 
					docs/_static/click-small.png | 
				
			||||||
 | 
					docs/_static/click-small@2x.png | 
				
			||||||
 | 
					docs/_static/click.png | 
				
			||||||
 | 
					docs/_static/click@2x.png | 
				
			||||||
 | 
					docs/_templates/sidebarintro.html | 
				
			||||||
 | 
					docs/_templates/sidebarlogo.html | 
				
			||||||
 | 
					examples/README | 
				
			||||||
 | 
					examples/aliases/README | 
				
			||||||
 | 
					examples/aliases/aliases.ini | 
				
			||||||
 | 
					examples/aliases/aliases.py | 
				
			||||||
 | 
					examples/aliases/setup.py | 
				
			||||||
 | 
					examples/colors/README | 
				
			||||||
 | 
					examples/colors/colors.py | 
				
			||||||
 | 
					examples/colors/setup.py | 
				
			||||||
 | 
					examples/complex/README | 
				
			||||||
 | 
					examples/complex/setup.py | 
				
			||||||
 | 
					examples/complex/complex/__init__.py | 
				
			||||||
 | 
					examples/complex/complex/cli.py | 
				
			||||||
 | 
					examples/complex/complex/commands/__init__.py | 
				
			||||||
 | 
					examples/complex/complex/commands/cmd_init.py | 
				
			||||||
 | 
					examples/complex/complex/commands/cmd_status.py | 
				
			||||||
 | 
					examples/imagepipe/.gitignore | 
				
			||||||
 | 
					examples/imagepipe/README | 
				
			||||||
 | 
					examples/imagepipe/example01.jpg | 
				
			||||||
 | 
					examples/imagepipe/example02.jpg | 
				
			||||||
 | 
					examples/imagepipe/imagepipe.py | 
				
			||||||
 | 
					examples/imagepipe/setup.py | 
				
			||||||
 | 
					examples/inout/README | 
				
			||||||
 | 
					examples/inout/inout.py | 
				
			||||||
 | 
					examples/inout/setup.py | 
				
			||||||
 | 
					examples/naval/README | 
				
			||||||
 | 
					examples/naval/naval.py | 
				
			||||||
 | 
					examples/naval/setup.py | 
				
			||||||
 | 
					examples/repo/README | 
				
			||||||
 | 
					examples/repo/repo.py | 
				
			||||||
 | 
					examples/repo/setup.py | 
				
			||||||
 | 
					examples/termui/README | 
				
			||||||
 | 
					examples/termui/setup.py | 
				
			||||||
 | 
					examples/termui/termui.py | 
				
			||||||
 | 
					examples/validation/README | 
				
			||||||
 | 
					examples/validation/setup.py | 
				
			||||||
 | 
					examples/validation/validation.py | 
				
			||||||
 | 
					tests/conftest.py | 
				
			||||||
 | 
					tests/test_arguments.py | 
				
			||||||
 | 
					tests/test_bashcomplete.py | 
				
			||||||
 | 
					tests/test_basic.py | 
				
			||||||
 | 
					tests/test_chain.py | 
				
			||||||
 | 
					tests/test_commands.py | 
				
			||||||
 | 
					tests/test_compat.py | 
				
			||||||
 | 
					tests/test_context.py | 
				
			||||||
 | 
					tests/test_defaults.py | 
				
			||||||
 | 
					tests/test_formatting.py | 
				
			||||||
 | 
					tests/test_imports.py | 
				
			||||||
 | 
					tests/test_normalization.py | 
				
			||||||
 | 
					tests/test_options.py | 
				
			||||||
 | 
					tests/test_termui.py | 
				
			||||||
 | 
					tests/test_testing.py | 
				
			||||||
 | 
					tests/test_utils.py | 
				
			||||||
@ -0,0 +1,38 @@ | 
				
			|||||||
 | 
					../click/exceptions.py | 
				
			||||||
 | 
					../click/testing.py | 
				
			||||||
 | 
					../click/decorators.py | 
				
			||||||
 | 
					../click/parser.py | 
				
			||||||
 | 
					../click/formatting.py | 
				
			||||||
 | 
					../click/globals.py | 
				
			||||||
 | 
					../click/_termui_impl.py | 
				
			||||||
 | 
					../click/__init__.py | 
				
			||||||
 | 
					../click/_compat.py | 
				
			||||||
 | 
					../click/_winconsole.py | 
				
			||||||
 | 
					../click/_unicodefun.py | 
				
			||||||
 | 
					../click/_textwrap.py | 
				
			||||||
 | 
					../click/_bashcomplete.py | 
				
			||||||
 | 
					../click/core.py | 
				
			||||||
 | 
					../click/types.py | 
				
			||||||
 | 
					../click/termui.py | 
				
			||||||
 | 
					../click/utils.py | 
				
			||||||
 | 
					../click/exceptions.pyc | 
				
			||||||
 | 
					../click/testing.pyc | 
				
			||||||
 | 
					../click/decorators.pyc | 
				
			||||||
 | 
					../click/parser.pyc | 
				
			||||||
 | 
					../click/formatting.pyc | 
				
			||||||
 | 
					../click/globals.pyc | 
				
			||||||
 | 
					../click/_termui_impl.pyc | 
				
			||||||
 | 
					../click/__init__.pyc | 
				
			||||||
 | 
					../click/_compat.pyc | 
				
			||||||
 | 
					../click/_winconsole.pyc | 
				
			||||||
 | 
					../click/_unicodefun.pyc | 
				
			||||||
 | 
					../click/_textwrap.pyc | 
				
			||||||
 | 
					../click/_bashcomplete.pyc | 
				
			||||||
 | 
					../click/core.pyc | 
				
			||||||
 | 
					../click/types.pyc | 
				
			||||||
 | 
					../click/termui.pyc | 
				
			||||||
 | 
					../click/utils.pyc | 
				
			||||||
 | 
					SOURCES.txt | 
				
			||||||
 | 
					top_level.txt | 
				
			||||||
 | 
					PKG-INFO | 
				
			||||||
 | 
					dependency_links.txt | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					click | 
				
			||||||
@ -0,0 +1,98 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    click | 
				
			||||||
 | 
					    ~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Click is a simple Python module that wraps the stdlib's optparse to make | 
				
			||||||
 | 
					    writing command line scripts fun.  Unlike other modules, it's based around | 
				
			||||||
 | 
					    a simple API that does not come with too much magic and is composable. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    In case optparse ever gets removed from the stdlib, it will be shipped by | 
				
			||||||
 | 
					    this module. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: (c) 2014 by Armin Ronacher. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Core classes | 
				
			||||||
 | 
					from .core import Context, BaseCommand, Command, MultiCommand, Group, \ | 
				
			||||||
 | 
					     CommandCollection, Parameter, Option, Argument | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Globals | 
				
			||||||
 | 
					from .globals import get_current_context | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Decorators | 
				
			||||||
 | 
					from .decorators import pass_context, pass_obj, make_pass_decorator, \ | 
				
			||||||
 | 
					     command, group, argument, option, confirmation_option, \ | 
				
			||||||
 | 
					     password_option, version_option, help_option | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Types | 
				
			||||||
 | 
					from .types import ParamType, File, Path, Choice, IntRange, Tuple, \ | 
				
			||||||
 | 
					     STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Utilities | 
				
			||||||
 | 
					from .utils import echo, get_binary_stream, get_text_stream, open_file, \ | 
				
			||||||
 | 
					     format_filename, get_app_dir, get_os_args | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Terminal functions | 
				
			||||||
 | 
					from .termui import prompt, confirm, get_terminal_size, echo_via_pager, \ | 
				
			||||||
 | 
					     progressbar, clear, style, unstyle, secho, edit, launch, getchar, \ | 
				
			||||||
 | 
					     pause | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Exceptions | 
				
			||||||
 | 
					from .exceptions import ClickException, UsageError, BadParameter, \ | 
				
			||||||
 | 
					     FileError, Abort, NoSuchOption, BadOptionUsage, BadArgumentUsage, \ | 
				
			||||||
 | 
					     MissingParameter | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Formatting | 
				
			||||||
 | 
					from .formatting import HelpFormatter, wrap_text | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Parsing | 
				
			||||||
 | 
					from .parser import OptionParser | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__all__ = [ | 
				
			||||||
 | 
					    # Core classes | 
				
			||||||
 | 
					    'Context', 'BaseCommand', 'Command', 'MultiCommand', 'Group', | 
				
			||||||
 | 
					    'CommandCollection', 'Parameter', 'Option', 'Argument', | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Globals | 
				
			||||||
 | 
					    'get_current_context', | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Decorators | 
				
			||||||
 | 
					    'pass_context', 'pass_obj', 'make_pass_decorator', 'command', 'group', | 
				
			||||||
 | 
					    'argument', 'option', 'confirmation_option', 'password_option', | 
				
			||||||
 | 
					    'version_option', 'help_option', | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Types | 
				
			||||||
 | 
					    'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'Tuple', 'STRING', | 
				
			||||||
 | 
					    'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED', | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Utilities | 
				
			||||||
 | 
					    'echo', 'get_binary_stream', 'get_text_stream', 'open_file', | 
				
			||||||
 | 
					    'format_filename', 'get_app_dir', 'get_os_args', | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Terminal functions | 
				
			||||||
 | 
					    'prompt', 'confirm', 'get_terminal_size', 'echo_via_pager', | 
				
			||||||
 | 
					    'progressbar', 'clear', 'style', 'unstyle', 'secho', 'edit', 'launch', | 
				
			||||||
 | 
					    'getchar', 'pause', | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Exceptions | 
				
			||||||
 | 
					    'ClickException', 'UsageError', 'BadParameter', 'FileError', | 
				
			||||||
 | 
					    'Abort', 'NoSuchOption', 'BadOptionUsage', 'BadArgumentUsage', | 
				
			||||||
 | 
					    'MissingParameter', | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Formatting | 
				
			||||||
 | 
					    'HelpFormatter', 'wrap_text', | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Parsing | 
				
			||||||
 | 
					    'OptionParser', | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Controls if click should emit the warning about the use of unicode | 
				
			||||||
 | 
					# literals. | 
				
			||||||
 | 
					disable_unicode_literals_warning = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__version__ = '6.7' | 
				
			||||||
@ -0,0 +1,83 @@ | 
				
			|||||||
 | 
					import os | 
				
			||||||
 | 
					import re | 
				
			||||||
 | 
					from .utils import echo | 
				
			||||||
 | 
					from .parser import split_arg_string | 
				
			||||||
 | 
					from .core import MultiCommand, Option | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COMPLETION_SCRIPT = ''' | 
				
			||||||
 | 
					%(complete_func)s() { | 
				
			||||||
 | 
					    COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\ | 
				
			||||||
 | 
					                   COMP_CWORD=$COMP_CWORD \\ | 
				
			||||||
 | 
					                   %(autocomplete_var)s=complete $1 ) ) | 
				
			||||||
 | 
					    return 0 | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					complete -F %(complete_func)s -o default %(script_names)s | 
				
			||||||
 | 
					''' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_completion_script(prog_name, complete_var): | 
				
			||||||
 | 
					    cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_')) | 
				
			||||||
 | 
					    return (COMPLETION_SCRIPT % { | 
				
			||||||
 | 
					        'complete_func': '_%s_completion' % cf_name, | 
				
			||||||
 | 
					        'script_names': prog_name, | 
				
			||||||
 | 
					        'autocomplete_var': complete_var, | 
				
			||||||
 | 
					    }).strip() + ';' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resolve_ctx(cli, prog_name, args): | 
				
			||||||
 | 
					    ctx = cli.make_context(prog_name, args, resilient_parsing=True) | 
				
			||||||
 | 
					    while ctx.protected_args + ctx.args and isinstance(ctx.command, MultiCommand): | 
				
			||||||
 | 
					        a = ctx.protected_args + ctx.args | 
				
			||||||
 | 
					        cmd = ctx.command.get_command(ctx, a[0]) | 
				
			||||||
 | 
					        if cmd is None: | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					        ctx = cmd.make_context(a[0], a[1:], parent=ctx, resilient_parsing=True) | 
				
			||||||
 | 
					    return ctx | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_choices(cli, prog_name, args, incomplete): | 
				
			||||||
 | 
					    ctx = resolve_ctx(cli, prog_name, args) | 
				
			||||||
 | 
					    if ctx is None: | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    choices = [] | 
				
			||||||
 | 
					    if incomplete and not incomplete[:1].isalnum(): | 
				
			||||||
 | 
					        for param in ctx.command.params: | 
				
			||||||
 | 
					            if not isinstance(param, Option): | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					            choices.extend(param.opts) | 
				
			||||||
 | 
					            choices.extend(param.secondary_opts) | 
				
			||||||
 | 
					    elif isinstance(ctx.command, MultiCommand): | 
				
			||||||
 | 
					        choices.extend(ctx.command.list_commands(ctx)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for item in choices: | 
				
			||||||
 | 
					        if item.startswith(incomplete): | 
				
			||||||
 | 
					            yield item | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def do_complete(cli, prog_name): | 
				
			||||||
 | 
					    cwords = split_arg_string(os.environ['COMP_WORDS']) | 
				
			||||||
 | 
					    cword = int(os.environ['COMP_CWORD']) | 
				
			||||||
 | 
					    args = cwords[1:cword] | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        incomplete = cwords[cword] | 
				
			||||||
 | 
					    except IndexError: | 
				
			||||||
 | 
					        incomplete = '' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for item in get_choices(cli, prog_name, args, incomplete): | 
				
			||||||
 | 
					        echo(item) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def bashcomplete(cli, prog_name, complete_var, complete_instr): | 
				
			||||||
 | 
					    if complete_instr == 'source': | 
				
			||||||
 | 
					        echo(get_completion_script(prog_name, complete_var)) | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					    elif complete_instr == 'complete': | 
				
			||||||
 | 
					        return do_complete(cli, prog_name) | 
				
			||||||
 | 
					    return False | 
				
			||||||
@ -0,0 +1,648 @@ | 
				
			|||||||
 | 
					import re | 
				
			||||||
 | 
					import io | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import codecs | 
				
			||||||
 | 
					from weakref import WeakKeyDictionary | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PY2 = sys.version_info[0] == 2 | 
				
			||||||
 | 
					WIN = sys.platform.startswith('win') | 
				
			||||||
 | 
					DEFAULT_COLUMNS = 80 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_ansi_re = re.compile('\033\[((?:\d|;)*)([a-zA-Z])') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_filesystem_encoding(): | 
				
			||||||
 | 
					    return sys.getfilesystemencoding() or sys.getdefaultencoding() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _make_text_stream(stream, encoding, errors): | 
				
			||||||
 | 
					    if encoding is None: | 
				
			||||||
 | 
					        encoding = get_best_encoding(stream) | 
				
			||||||
 | 
					    if errors is None: | 
				
			||||||
 | 
					        errors = 'replace' | 
				
			||||||
 | 
					    return _NonClosingTextIOWrapper(stream, encoding, errors, | 
				
			||||||
 | 
					                                    line_buffering=True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def is_ascii_encoding(encoding): | 
				
			||||||
 | 
					    """Checks if a given encoding is ascii.""" | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        return codecs.lookup(encoding).name == 'ascii' | 
				
			||||||
 | 
					    except LookupError: | 
				
			||||||
 | 
					        return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_best_encoding(stream): | 
				
			||||||
 | 
					    """Returns the default stream encoding if not found.""" | 
				
			||||||
 | 
					    rv = getattr(stream, 'encoding', None) or sys.getdefaultencoding() | 
				
			||||||
 | 
					    if is_ascii_encoding(rv): | 
				
			||||||
 | 
					        return 'utf-8' | 
				
			||||||
 | 
					    return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _NonClosingTextIOWrapper(io.TextIOWrapper): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, stream, encoding, errors, **extra): | 
				
			||||||
 | 
					        self._stream = stream = _FixupStream(stream) | 
				
			||||||
 | 
					        io.TextIOWrapper.__init__(self, stream, encoding, errors, **extra) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # The io module is a place where the Python 3 text behavior | 
				
			||||||
 | 
					    # was forced upon Python 2, so we need to unbreak | 
				
			||||||
 | 
					    # it to look like Python 2. | 
				
			||||||
 | 
					    if PY2: | 
				
			||||||
 | 
					        def write(self, x): | 
				
			||||||
 | 
					            if isinstance(x, str) or is_bytes(x): | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    self.flush() | 
				
			||||||
 | 
					                except Exception: | 
				
			||||||
 | 
					                    pass | 
				
			||||||
 | 
					                return self.buffer.write(str(x)) | 
				
			||||||
 | 
					            return io.TextIOWrapper.write(self, x) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def writelines(self, lines): | 
				
			||||||
 | 
					            for line in lines: | 
				
			||||||
 | 
					                self.write(line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __del__(self): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.detach() | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def isatty(self): | 
				
			||||||
 | 
					        # https://bitbucket.org/pypy/pypy/issue/1803 | 
				
			||||||
 | 
					        return self._stream.isatty() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _FixupStream(object): | 
				
			||||||
 | 
					    """The new io interface needs more from streams than streams | 
				
			||||||
 | 
					    traditionally implement.  As such, this fix-up code is necessary in | 
				
			||||||
 | 
					    some circumstances. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, stream): | 
				
			||||||
 | 
					        self._stream = stream | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, name): | 
				
			||||||
 | 
					        return getattr(self._stream, name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read1(self, size): | 
				
			||||||
 | 
					        f = getattr(self._stream, 'read1', None) | 
				
			||||||
 | 
					        if f is not None: | 
				
			||||||
 | 
					            return f(size) | 
				
			||||||
 | 
					        # We only dispatch to readline instead of read in Python 2 as we | 
				
			||||||
 | 
					        # do not want cause problems with the different implementation | 
				
			||||||
 | 
					        # of line buffering. | 
				
			||||||
 | 
					        if PY2: | 
				
			||||||
 | 
					            return self._stream.readline(size) | 
				
			||||||
 | 
					        return self._stream.read(size) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def readable(self): | 
				
			||||||
 | 
					        x = getattr(self._stream, 'readable', None) | 
				
			||||||
 | 
					        if x is not None: | 
				
			||||||
 | 
					            return x() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self._stream.read(0) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def writable(self): | 
				
			||||||
 | 
					        x = getattr(self._stream, 'writable', None) | 
				
			||||||
 | 
					        if x is not None: | 
				
			||||||
 | 
					            return x() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self._stream.write('') | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                self._stream.write(b'') | 
				
			||||||
 | 
					            except Exception: | 
				
			||||||
 | 
					                return False | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def seekable(self): | 
				
			||||||
 | 
					        x = getattr(self._stream, 'seekable', None) | 
				
			||||||
 | 
					        if x is not None: | 
				
			||||||
 | 
					            return x() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self._stream.seek(self._stream.tell()) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY2: | 
				
			||||||
 | 
					    text_type = unicode | 
				
			||||||
 | 
					    bytes = str | 
				
			||||||
 | 
					    raw_input = raw_input | 
				
			||||||
 | 
					    string_types = (str, unicode) | 
				
			||||||
 | 
					    iteritems = lambda x: x.iteritems() | 
				
			||||||
 | 
					    range_type = xrange | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_bytes(x): | 
				
			||||||
 | 
					        return isinstance(x, (buffer, bytearray)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # For Windows, we need to force stdout/stdin/stderr to binary if it's | 
				
			||||||
 | 
					    # fetched for that.  This obviously is not the most correct way to do | 
				
			||||||
 | 
					    # it as it changes global state.  Unfortunately, there does not seem to | 
				
			||||||
 | 
					    # be a clear better way to do it as just reopening the file in binary | 
				
			||||||
 | 
					    # mode does not change anything. | 
				
			||||||
 | 
					    # | 
				
			||||||
 | 
					    # An option would be to do what Python 3 does and to open the file as | 
				
			||||||
 | 
					    # binary only, patch it back to the system, and then use a wrapper | 
				
			||||||
 | 
					    # stream that converts newlines.  It's not quite clear what's the | 
				
			||||||
 | 
					    # correct option here. | 
				
			||||||
 | 
					    # | 
				
			||||||
 | 
					    # This code also lives in _winconsole for the fallback to the console | 
				
			||||||
 | 
					    # emulation stream. | 
				
			||||||
 | 
					    # | 
				
			||||||
 | 
					    # There are also Windows environments where the `msvcrt` module is not | 
				
			||||||
 | 
					    # available (which is why we use try-catch instead of the WIN variable | 
				
			||||||
 | 
					    # here), such as the Google App Engine development server on Windows. In | 
				
			||||||
 | 
					    # those cases there is just nothing we can do. | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        import msvcrt | 
				
			||||||
 | 
					    except ImportError: | 
				
			||||||
 | 
					        set_binary_mode = lambda x: x | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        def set_binary_mode(f): | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                fileno = f.fileno() | 
				
			||||||
 | 
					            except Exception: | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                msvcrt.setmode(fileno, os.O_BINARY) | 
				
			||||||
 | 
					            return f | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def isidentifier(x): | 
				
			||||||
 | 
					        return _identifier_re.search(x) is not None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_binary_stdin(): | 
				
			||||||
 | 
					        return set_binary_mode(sys.stdin) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_binary_stdout(): | 
				
			||||||
 | 
					        return set_binary_mode(sys.stdout) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_binary_stderr(): | 
				
			||||||
 | 
					        return set_binary_mode(sys.stderr) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_text_stdin(encoding=None, errors=None): | 
				
			||||||
 | 
					        rv = _get_windows_console_stream(sys.stdin, encoding, errors) | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					        return _make_text_stream(sys.stdin, encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_text_stdout(encoding=None, errors=None): | 
				
			||||||
 | 
					        rv = _get_windows_console_stream(sys.stdout, encoding, errors) | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					        return _make_text_stream(sys.stdout, encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_text_stderr(encoding=None, errors=None): | 
				
			||||||
 | 
					        rv = _get_windows_console_stream(sys.stderr, encoding, errors) | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					        return _make_text_stream(sys.stderr, encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def filename_to_ui(value): | 
				
			||||||
 | 
					        if isinstance(value, bytes): | 
				
			||||||
 | 
					            value = value.decode(get_filesystem_encoding(), 'replace') | 
				
			||||||
 | 
					        return value | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import io | 
				
			||||||
 | 
					    text_type = str | 
				
			||||||
 | 
					    raw_input = input | 
				
			||||||
 | 
					    string_types = (str,) | 
				
			||||||
 | 
					    range_type = range | 
				
			||||||
 | 
					    isidentifier = lambda x: x.isidentifier() | 
				
			||||||
 | 
					    iteritems = lambda x: iter(x.items()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_bytes(x): | 
				
			||||||
 | 
					        return isinstance(x, (bytes, memoryview, bytearray)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _is_binary_reader(stream, default=False): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return isinstance(stream.read(0), bytes) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            return default | 
				
			||||||
 | 
					            # This happens in some cases where the stream was already | 
				
			||||||
 | 
					            # closed.  In this case, we assume the default. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _is_binary_writer(stream, default=False): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            stream.write(b'') | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                stream.write('') | 
				
			||||||
 | 
					                return False | 
				
			||||||
 | 
					            except Exception: | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					            return default | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _find_binary_reader(stream): | 
				
			||||||
 | 
					        # We need to figure out if the given stream is already binary. | 
				
			||||||
 | 
					        # This can happen because the official docs recommend detaching | 
				
			||||||
 | 
					        # the streams to get binary streams.  Some code might do this, so | 
				
			||||||
 | 
					        # we need to deal with this case explicitly. | 
				
			||||||
 | 
					        if _is_binary_reader(stream, False): | 
				
			||||||
 | 
					            return stream | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        buf = getattr(stream, 'buffer', None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Same situation here; this time we assume that the buffer is | 
				
			||||||
 | 
					        # actually binary in case it's closed. | 
				
			||||||
 | 
					        if buf is not None and _is_binary_reader(buf, True): | 
				
			||||||
 | 
					            return buf | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _find_binary_writer(stream): | 
				
			||||||
 | 
					        # We need to figure out if the given stream is already binary. | 
				
			||||||
 | 
					        # This can happen because the official docs recommend detatching | 
				
			||||||
 | 
					        # the streams to get binary streams.  Some code might do this, so | 
				
			||||||
 | 
					        # we need to deal with this case explicitly. | 
				
			||||||
 | 
					        if _is_binary_writer(stream, False): | 
				
			||||||
 | 
					            return stream | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        buf = getattr(stream, 'buffer', None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Same situation here; this time we assume that the buffer is | 
				
			||||||
 | 
					        # actually binary in case it's closed. | 
				
			||||||
 | 
					        if buf is not None and _is_binary_writer(buf, True): | 
				
			||||||
 | 
					            return buf | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _stream_is_misconfigured(stream): | 
				
			||||||
 | 
					        """A stream is misconfigured if its encoding is ASCII.""" | 
				
			||||||
 | 
					        # If the stream does not have an encoding set, we assume it's set | 
				
			||||||
 | 
					        # to ASCII.  This appears to happen in certain unittest | 
				
			||||||
 | 
					        # environments.  It's not quite clear what the correct behavior is | 
				
			||||||
 | 
					        # but this at least will force Click to recover somehow. | 
				
			||||||
 | 
					        return is_ascii_encoding(getattr(stream, 'encoding', None) or 'ascii') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _is_compatible_text_stream(stream, encoding, errors): | 
				
			||||||
 | 
					        stream_encoding = getattr(stream, 'encoding', None) | 
				
			||||||
 | 
					        stream_errors = getattr(stream, 'errors', None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Perfect match. | 
				
			||||||
 | 
					        if stream_encoding == encoding and stream_errors == errors: | 
				
			||||||
 | 
					            return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Otherwise, it's only a compatible stream if we did not ask for | 
				
			||||||
 | 
					        # an encoding. | 
				
			||||||
 | 
					        if encoding is None: | 
				
			||||||
 | 
					            return stream_encoding is not None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _force_correct_text_reader(text_reader, encoding, errors): | 
				
			||||||
 | 
					        if _is_binary_reader(text_reader, False): | 
				
			||||||
 | 
					            binary_reader = text_reader | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            # If there is no target encoding set, we need to verify that the | 
				
			||||||
 | 
					            # reader is not actually misconfigured. | 
				
			||||||
 | 
					            if encoding is None and not _stream_is_misconfigured(text_reader): | 
				
			||||||
 | 
					                return text_reader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if _is_compatible_text_stream(text_reader, encoding, errors): | 
				
			||||||
 | 
					                return text_reader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # If the reader has no encoding, we try to find the underlying | 
				
			||||||
 | 
					            # binary reader for it.  If that fails because the environment is | 
				
			||||||
 | 
					            # misconfigured, we silently go with the same reader because this | 
				
			||||||
 | 
					            # is too common to happen.  In that case, mojibake is better than | 
				
			||||||
 | 
					            # exceptions. | 
				
			||||||
 | 
					            binary_reader = _find_binary_reader(text_reader) | 
				
			||||||
 | 
					            if binary_reader is None: | 
				
			||||||
 | 
					                return text_reader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # At this point, we default the errors to replace instead of strict | 
				
			||||||
 | 
					        # because nobody handles those errors anyways and at this point | 
				
			||||||
 | 
					        # we're so fundamentally fucked that nothing can repair it. | 
				
			||||||
 | 
					        if errors is None: | 
				
			||||||
 | 
					            errors = 'replace' | 
				
			||||||
 | 
					        return _make_text_stream(binary_reader, encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _force_correct_text_writer(text_writer, encoding, errors): | 
				
			||||||
 | 
					        if _is_binary_writer(text_writer, False): | 
				
			||||||
 | 
					            binary_writer = text_writer | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            # If there is no target encoding set, we need to verify that the | 
				
			||||||
 | 
					            # writer is not actually misconfigured. | 
				
			||||||
 | 
					            if encoding is None and not _stream_is_misconfigured(text_writer): | 
				
			||||||
 | 
					                return text_writer | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if _is_compatible_text_stream(text_writer, encoding, errors): | 
				
			||||||
 | 
					                return text_writer | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # If the writer has no encoding, we try to find the underlying | 
				
			||||||
 | 
					            # binary writer for it.  If that fails because the environment is | 
				
			||||||
 | 
					            # misconfigured, we silently go with the same writer because this | 
				
			||||||
 | 
					            # is too common to happen.  In that case, mojibake is better than | 
				
			||||||
 | 
					            # exceptions. | 
				
			||||||
 | 
					            binary_writer = _find_binary_writer(text_writer) | 
				
			||||||
 | 
					            if binary_writer is None: | 
				
			||||||
 | 
					                return text_writer | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # At this point, we default the errors to replace instead of strict | 
				
			||||||
 | 
					        # because nobody handles those errors anyways and at this point | 
				
			||||||
 | 
					        # we're so fundamentally fucked that nothing can repair it. | 
				
			||||||
 | 
					        if errors is None: | 
				
			||||||
 | 
					            errors = 'replace' | 
				
			||||||
 | 
					        return _make_text_stream(binary_writer, encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_binary_stdin(): | 
				
			||||||
 | 
					        reader = _find_binary_reader(sys.stdin) | 
				
			||||||
 | 
					        if reader is None: | 
				
			||||||
 | 
					            raise RuntimeError('Was not able to determine binary ' | 
				
			||||||
 | 
					                               'stream for sys.stdin.') | 
				
			||||||
 | 
					        return reader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_binary_stdout(): | 
				
			||||||
 | 
					        writer = _find_binary_writer(sys.stdout) | 
				
			||||||
 | 
					        if writer is None: | 
				
			||||||
 | 
					            raise RuntimeError('Was not able to determine binary ' | 
				
			||||||
 | 
					                               'stream for sys.stdout.') | 
				
			||||||
 | 
					        return writer | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_binary_stderr(): | 
				
			||||||
 | 
					        writer = _find_binary_writer(sys.stderr) | 
				
			||||||
 | 
					        if writer is None: | 
				
			||||||
 | 
					            raise RuntimeError('Was not able to determine binary ' | 
				
			||||||
 | 
					                               'stream for sys.stderr.') | 
				
			||||||
 | 
					        return writer | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_text_stdin(encoding=None, errors=None): | 
				
			||||||
 | 
					        rv = _get_windows_console_stream(sys.stdin, encoding, errors) | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					        return _force_correct_text_reader(sys.stdin, encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_text_stdout(encoding=None, errors=None): | 
				
			||||||
 | 
					        rv = _get_windows_console_stream(sys.stdout, encoding, errors) | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					        return _force_correct_text_writer(sys.stdout, encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_text_stderr(encoding=None, errors=None): | 
				
			||||||
 | 
					        rv = _get_windows_console_stream(sys.stderr, encoding, errors) | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					        return _force_correct_text_writer(sys.stderr, encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def filename_to_ui(value): | 
				
			||||||
 | 
					        if isinstance(value, bytes): | 
				
			||||||
 | 
					            value = value.decode(get_filesystem_encoding(), 'replace') | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            value = value.encode('utf-8', 'surrogateescape') \ | 
				
			||||||
 | 
					                .decode('utf-8', 'replace') | 
				
			||||||
 | 
					        return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_streerror(e, default=None): | 
				
			||||||
 | 
					    if hasattr(e, 'strerror'): | 
				
			||||||
 | 
					        msg = e.strerror | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        if default is not None: | 
				
			||||||
 | 
					            msg = default | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            msg = str(e) | 
				
			||||||
 | 
					    if isinstance(msg, bytes): | 
				
			||||||
 | 
					        msg = msg.decode('utf-8', 'replace') | 
				
			||||||
 | 
					    return msg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def open_stream(filename, mode='r', encoding=None, errors='strict', | 
				
			||||||
 | 
					                atomic=False): | 
				
			||||||
 | 
					    # Standard streams first.  These are simple because they don't need | 
				
			||||||
 | 
					    # special handling for the atomic flag.  It's entirely ignored. | 
				
			||||||
 | 
					    if filename == '-': | 
				
			||||||
 | 
					        if 'w' in mode: | 
				
			||||||
 | 
					            if 'b' in mode: | 
				
			||||||
 | 
					                return get_binary_stdout(), False | 
				
			||||||
 | 
					            return get_text_stdout(encoding=encoding, errors=errors), False | 
				
			||||||
 | 
					        if 'b' in mode: | 
				
			||||||
 | 
					            return get_binary_stdin(), False | 
				
			||||||
 | 
					        return get_text_stdin(encoding=encoding, errors=errors), False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Non-atomic writes directly go out through the regular open functions. | 
				
			||||||
 | 
					    if not atomic: | 
				
			||||||
 | 
					        if encoding is None: | 
				
			||||||
 | 
					            return open(filename, mode), True | 
				
			||||||
 | 
					        return io.open(filename, mode, encoding=encoding, errors=errors), True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Some usability stuff for atomic writes | 
				
			||||||
 | 
					    if 'a' in mode: | 
				
			||||||
 | 
					        raise ValueError( | 
				
			||||||
 | 
					            'Appending to an existing file is not supported, because that ' | 
				
			||||||
 | 
					            'would involve an expensive `copy`-operation to a temporary ' | 
				
			||||||
 | 
					            'file. Open the file in normal `w`-mode and copy explicitly ' | 
				
			||||||
 | 
					            'if that\'s what you\'re after.' | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					    if 'x' in mode: | 
				
			||||||
 | 
					        raise ValueError('Use the `overwrite`-parameter instead.') | 
				
			||||||
 | 
					    if 'w' not in mode: | 
				
			||||||
 | 
					        raise ValueError('Atomic writes only make sense with `w`-mode.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Atomic writes are more complicated.  They work by opening a file | 
				
			||||||
 | 
					    # as a proxy in the same folder and then using the fdopen | 
				
			||||||
 | 
					    # functionality to wrap it in a Python file.  Then we wrap it in an | 
				
			||||||
 | 
					    # atomic file that moves the file over on close. | 
				
			||||||
 | 
					    import tempfile | 
				
			||||||
 | 
					    fd, tmp_filename = tempfile.mkstemp(dir=os.path.dirname(filename), | 
				
			||||||
 | 
					                                        prefix='.__atomic-write') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if encoding is not None: | 
				
			||||||
 | 
					        f = io.open(fd, mode, encoding=encoding, errors=errors) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        f = os.fdopen(fd, mode) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return _AtomicFile(f, tmp_filename, filename), True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Used in a destructor call, needs extra protection from interpreter cleanup. | 
				
			||||||
 | 
					if hasattr(os, 'replace'): | 
				
			||||||
 | 
					    _replace = os.replace | 
				
			||||||
 | 
					    _can_replace = True | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    _replace = os.rename | 
				
			||||||
 | 
					    _can_replace = not WIN | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _AtomicFile(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, f, tmp_filename, real_filename): | 
				
			||||||
 | 
					        self._f = f | 
				
			||||||
 | 
					        self._tmp_filename = tmp_filename | 
				
			||||||
 | 
					        self._real_filename = real_filename | 
				
			||||||
 | 
					        self.closed = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def name(self): | 
				
			||||||
 | 
					        return self._real_filename | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def close(self, delete=False): | 
				
			||||||
 | 
					        if self.closed: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        self._f.close() | 
				
			||||||
 | 
					        if not _can_replace: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                os.remove(self._real_filename) | 
				
			||||||
 | 
					            except OSError: | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					        _replace(self._tmp_filename, self._real_filename) | 
				
			||||||
 | 
					        self.closed = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, name): | 
				
			||||||
 | 
					        return getattr(self._f, name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __enter__(self): | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, exc_type, exc_value, tb): | 
				
			||||||
 | 
					        self.close(delete=exc_type is not None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return repr(self._f) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto_wrap_for_ansi = None | 
				
			||||||
 | 
					colorama = None | 
				
			||||||
 | 
					get_winterm_size = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def strip_ansi(value): | 
				
			||||||
 | 
					    return _ansi_re.sub('', value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def should_strip_ansi(stream=None, color=None): | 
				
			||||||
 | 
					    if color is None: | 
				
			||||||
 | 
					        if stream is None: | 
				
			||||||
 | 
					            stream = sys.stdin | 
				
			||||||
 | 
					        return not isatty(stream) | 
				
			||||||
 | 
					    return not color | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If we're on Windows, we provide transparent integration through | 
				
			||||||
 | 
					# colorama.  This will make ANSI colors through the echo function | 
				
			||||||
 | 
					# work automatically. | 
				
			||||||
 | 
					if WIN: | 
				
			||||||
 | 
					    # Windows has a smaller terminal | 
				
			||||||
 | 
					    DEFAULT_COLUMNS = 79 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from ._winconsole import _get_windows_console_stream | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_argv_encoding(): | 
				
			||||||
 | 
					        import locale | 
				
			||||||
 | 
					        return locale.getpreferredencoding() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if PY2: | 
				
			||||||
 | 
					        def raw_input(prompt=''): | 
				
			||||||
 | 
					            sys.stderr.flush() | 
				
			||||||
 | 
					            if prompt: | 
				
			||||||
 | 
					                stdout = _default_text_stdout() | 
				
			||||||
 | 
					                stdout.write(prompt) | 
				
			||||||
 | 
					            stdin = _default_text_stdin() | 
				
			||||||
 | 
					            return stdin.readline().rstrip('\r\n') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        import colorama | 
				
			||||||
 | 
					    except ImportError: | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        _ansi_stream_wrappers = WeakKeyDictionary() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def auto_wrap_for_ansi(stream, color=None): | 
				
			||||||
 | 
					            """This function wraps a stream so that calls through colorama | 
				
			||||||
 | 
					            are issued to the win32 console API to recolor on demand.  It | 
				
			||||||
 | 
					            also ensures to reset the colors if a write call is interrupted | 
				
			||||||
 | 
					            to not destroy the console afterwards. | 
				
			||||||
 | 
					            """ | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                cached = _ansi_stream_wrappers.get(stream) | 
				
			||||||
 | 
					            except Exception: | 
				
			||||||
 | 
					                cached = None | 
				
			||||||
 | 
					            if cached is not None: | 
				
			||||||
 | 
					                return cached | 
				
			||||||
 | 
					            strip = should_strip_ansi(stream, color) | 
				
			||||||
 | 
					            ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) | 
				
			||||||
 | 
					            rv = ansi_wrapper.stream | 
				
			||||||
 | 
					            _write = rv.write | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def _safe_write(s): | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    return _write(s) | 
				
			||||||
 | 
					                except: | 
				
			||||||
 | 
					                    ansi_wrapper.reset_all() | 
				
			||||||
 | 
					                    raise | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            rv.write = _safe_write | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                _ansi_stream_wrappers[stream] = rv | 
				
			||||||
 | 
					            except Exception: | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def get_winterm_size(): | 
				
			||||||
 | 
					            win = colorama.win32.GetConsoleScreenBufferInfo( | 
				
			||||||
 | 
					                colorama.win32.STDOUT).srWindow | 
				
			||||||
 | 
					            return win.Right - win.Left, win.Bottom - win.Top | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def _get_argv_encoding(): | 
				
			||||||
 | 
					        return getattr(sys.stdin, 'encoding', None) or get_filesystem_encoding() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _get_windows_console_stream = lambda *x: None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def term_len(x): | 
				
			||||||
 | 
					    return len(strip_ansi(x)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def isatty(stream): | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        return stream.isatty() | 
				
			||||||
 | 
					    except Exception: | 
				
			||||||
 | 
					        return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _make_cached_stream_func(src_func, wrapper_func): | 
				
			||||||
 | 
					    cache = WeakKeyDictionary() | 
				
			||||||
 | 
					    def func(): | 
				
			||||||
 | 
					        stream = src_func() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            rv = cache.get(stream) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            rv = None | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					        rv = wrapper_func() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            cache[stream] = rv | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					    return func | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_default_text_stdin = _make_cached_stream_func( | 
				
			||||||
 | 
					    lambda: sys.stdin, get_text_stdin) | 
				
			||||||
 | 
					_default_text_stdout = _make_cached_stream_func( | 
				
			||||||
 | 
					    lambda: sys.stdout, get_text_stdout) | 
				
			||||||
 | 
					_default_text_stderr = _make_cached_stream_func( | 
				
			||||||
 | 
					    lambda: sys.stderr, get_text_stderr) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					binary_streams = { | 
				
			||||||
 | 
					    'stdin': get_binary_stdin, | 
				
			||||||
 | 
					    'stdout': get_binary_stdout, | 
				
			||||||
 | 
					    'stderr': get_binary_stderr, | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					text_streams = { | 
				
			||||||
 | 
					    'stdin': get_text_stdin, | 
				
			||||||
 | 
					    'stdout': get_text_stdout, | 
				
			||||||
 | 
					    'stderr': get_text_stderr, | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,547 @@ | 
				
			|||||||
 | 
					""" | 
				
			||||||
 | 
					    click._termui_impl | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This module contains implementations for the termui module.  To keep the | 
				
			||||||
 | 
					    import time of Click down, some infrequently used functionality is placed | 
				
			||||||
 | 
					    in this module and only imported as needed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: (c) 2014 by Armin Ronacher. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					import math | 
				
			||||||
 | 
					from ._compat import _default_text_stdout, range_type, PY2, isatty, \ | 
				
			||||||
 | 
					     open_stream, strip_ansi, term_len, get_best_encoding, WIN | 
				
			||||||
 | 
					from .utils import echo | 
				
			||||||
 | 
					from .exceptions import ClickException | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if os.name == 'nt': | 
				
			||||||
 | 
					    BEFORE_BAR = '\r' | 
				
			||||||
 | 
					    AFTER_BAR = '\n' | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    BEFORE_BAR = '\r\033[?25l' | 
				
			||||||
 | 
					    AFTER_BAR = '\033[?25h\n' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _length_hint(obj): | 
				
			||||||
 | 
					    """Returns the length hint of an object.""" | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        return len(obj) | 
				
			||||||
 | 
					    except (AttributeError, TypeError): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            get_hint = type(obj).__length_hint__ | 
				
			||||||
 | 
					        except AttributeError: | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            hint = get_hint(obj) | 
				
			||||||
 | 
					        except TypeError: | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					        if hint is NotImplemented or \ | 
				
			||||||
 | 
					           not isinstance(hint, (int, long)) or \ | 
				
			||||||
 | 
					           hint < 0: | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					        return hint | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProgressBar(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, iterable, length=None, fill_char='#', empty_char=' ', | 
				
			||||||
 | 
					                 bar_template='%(bar)s', info_sep='  ', show_eta=True, | 
				
			||||||
 | 
					                 show_percent=None, show_pos=False, item_show_func=None, | 
				
			||||||
 | 
					                 label=None, file=None, color=None, width=30): | 
				
			||||||
 | 
					        self.fill_char = fill_char | 
				
			||||||
 | 
					        self.empty_char = empty_char | 
				
			||||||
 | 
					        self.bar_template = bar_template | 
				
			||||||
 | 
					        self.info_sep = info_sep | 
				
			||||||
 | 
					        self.show_eta = show_eta | 
				
			||||||
 | 
					        self.show_percent = show_percent | 
				
			||||||
 | 
					        self.show_pos = show_pos | 
				
			||||||
 | 
					        self.item_show_func = item_show_func | 
				
			||||||
 | 
					        self.label = label or '' | 
				
			||||||
 | 
					        if file is None: | 
				
			||||||
 | 
					            file = _default_text_stdout() | 
				
			||||||
 | 
					        self.file = file | 
				
			||||||
 | 
					        self.color = color | 
				
			||||||
 | 
					        self.width = width | 
				
			||||||
 | 
					        self.autowidth = width == 0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if length is None: | 
				
			||||||
 | 
					            length = _length_hint(iterable) | 
				
			||||||
 | 
					        if iterable is None: | 
				
			||||||
 | 
					            if length is None: | 
				
			||||||
 | 
					                raise TypeError('iterable or length is required') | 
				
			||||||
 | 
					            iterable = range_type(length) | 
				
			||||||
 | 
					        self.iter = iter(iterable) | 
				
			||||||
 | 
					        self.length = length | 
				
			||||||
 | 
					        self.length_known = length is not None | 
				
			||||||
 | 
					        self.pos = 0 | 
				
			||||||
 | 
					        self.avg = [] | 
				
			||||||
 | 
					        self.start = self.last_eta = time.time() | 
				
			||||||
 | 
					        self.eta_known = False | 
				
			||||||
 | 
					        self.finished = False | 
				
			||||||
 | 
					        self.max_width = None | 
				
			||||||
 | 
					        self.entered = False | 
				
			||||||
 | 
					        self.current_item = None | 
				
			||||||
 | 
					        self.is_hidden = not isatty(self.file) | 
				
			||||||
 | 
					        self._last_line = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __enter__(self): | 
				
			||||||
 | 
					        self.entered = True | 
				
			||||||
 | 
					        self.render_progress() | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, exc_type, exc_value, tb): | 
				
			||||||
 | 
					        self.render_finish() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self): | 
				
			||||||
 | 
					        if not self.entered: | 
				
			||||||
 | 
					            raise RuntimeError('You need to use progress bars in a with block.') | 
				
			||||||
 | 
					        self.render_progress() | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_finish(self): | 
				
			||||||
 | 
					        if self.is_hidden: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        self.file.write(AFTER_BAR) | 
				
			||||||
 | 
					        self.file.flush() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def pct(self): | 
				
			||||||
 | 
					        if self.finished: | 
				
			||||||
 | 
					            return 1.0 | 
				
			||||||
 | 
					        return min(self.pos / (float(self.length) or 1), 1.0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def time_per_iteration(self): | 
				
			||||||
 | 
					        if not self.avg: | 
				
			||||||
 | 
					            return 0.0 | 
				
			||||||
 | 
					        return sum(self.avg) / float(len(self.avg)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def eta(self): | 
				
			||||||
 | 
					        if self.length_known and not self.finished: | 
				
			||||||
 | 
					            return self.time_per_iteration * (self.length - self.pos) | 
				
			||||||
 | 
					        return 0.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_eta(self): | 
				
			||||||
 | 
					        if self.eta_known: | 
				
			||||||
 | 
					            t = self.eta + 1 | 
				
			||||||
 | 
					            seconds = t % 60 | 
				
			||||||
 | 
					            t /= 60 | 
				
			||||||
 | 
					            minutes = t % 60 | 
				
			||||||
 | 
					            t /= 60 | 
				
			||||||
 | 
					            hours = t % 24 | 
				
			||||||
 | 
					            t /= 24 | 
				
			||||||
 | 
					            if t > 0: | 
				
			||||||
 | 
					                days = t | 
				
			||||||
 | 
					                return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                return '%02d:%02d:%02d' % (hours, minutes, seconds) | 
				
			||||||
 | 
					        return '' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_pos(self): | 
				
			||||||
 | 
					        pos = str(self.pos) | 
				
			||||||
 | 
					        if self.length_known: | 
				
			||||||
 | 
					            pos += '/%s' % self.length | 
				
			||||||
 | 
					        return pos | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_pct(self): | 
				
			||||||
 | 
					        return ('% 4d%%' % int(self.pct * 100))[1:] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_progress_line(self): | 
				
			||||||
 | 
					        show_percent = self.show_percent | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        info_bits = [] | 
				
			||||||
 | 
					        if self.length_known: | 
				
			||||||
 | 
					            bar_length = int(self.pct * self.width) | 
				
			||||||
 | 
					            bar = self.fill_char * bar_length | 
				
			||||||
 | 
					            bar += self.empty_char * (self.width - bar_length) | 
				
			||||||
 | 
					            if show_percent is None: | 
				
			||||||
 | 
					                show_percent = not self.show_pos | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            if self.finished: | 
				
			||||||
 | 
					                bar = self.fill_char * self.width | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                bar = list(self.empty_char * (self.width or 1)) | 
				
			||||||
 | 
					                if self.time_per_iteration != 0: | 
				
			||||||
 | 
					                    bar[int((math.cos(self.pos * self.time_per_iteration) | 
				
			||||||
 | 
					                        / 2.0 + 0.5) * self.width)] = self.fill_char | 
				
			||||||
 | 
					                bar = ''.join(bar) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.show_pos: | 
				
			||||||
 | 
					            info_bits.append(self.format_pos()) | 
				
			||||||
 | 
					        if show_percent: | 
				
			||||||
 | 
					            info_bits.append(self.format_pct()) | 
				
			||||||
 | 
					        if self.show_eta and self.eta_known and not self.finished: | 
				
			||||||
 | 
					            info_bits.append(self.format_eta()) | 
				
			||||||
 | 
					        if self.item_show_func is not None: | 
				
			||||||
 | 
					            item_info = self.item_show_func(self.current_item) | 
				
			||||||
 | 
					            if item_info is not None: | 
				
			||||||
 | 
					                info_bits.append(item_info) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (self.bar_template % { | 
				
			||||||
 | 
					            'label': self.label, | 
				
			||||||
 | 
					            'bar': bar, | 
				
			||||||
 | 
					            'info': self.info_sep.join(info_bits) | 
				
			||||||
 | 
					        }).rstrip() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def render_progress(self): | 
				
			||||||
 | 
					        from .termui import get_terminal_size | 
				
			||||||
 | 
					        nl = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.is_hidden: | 
				
			||||||
 | 
					            buf = [self.label] | 
				
			||||||
 | 
					            nl = True | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            buf = [] | 
				
			||||||
 | 
					            # Update width in case the terminal has been resized | 
				
			||||||
 | 
					            if self.autowidth: | 
				
			||||||
 | 
					                old_width = self.width | 
				
			||||||
 | 
					                self.width = 0 | 
				
			||||||
 | 
					                clutter_length = term_len(self.format_progress_line()) | 
				
			||||||
 | 
					                new_width = max(0, get_terminal_size()[0] - clutter_length) | 
				
			||||||
 | 
					                if new_width < old_width: | 
				
			||||||
 | 
					                    buf.append(BEFORE_BAR) | 
				
			||||||
 | 
					                    buf.append(' ' * self.max_width) | 
				
			||||||
 | 
					                    self.max_width = new_width | 
				
			||||||
 | 
					                self.width = new_width | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            clear_width = self.width | 
				
			||||||
 | 
					            if self.max_width is not None: | 
				
			||||||
 | 
					                clear_width = self.max_width | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            buf.append(BEFORE_BAR) | 
				
			||||||
 | 
					            line = self.format_progress_line() | 
				
			||||||
 | 
					            line_len = term_len(line) | 
				
			||||||
 | 
					            if self.max_width is None or self.max_width < line_len: | 
				
			||||||
 | 
					                self.max_width = line_len | 
				
			||||||
 | 
					            buf.append(line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            buf.append(' ' * (clear_width - line_len)) | 
				
			||||||
 | 
					        line = ''.join(buf) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Render the line only if it changed. | 
				
			||||||
 | 
					        if line != self._last_line: | 
				
			||||||
 | 
					            self._last_line = line | 
				
			||||||
 | 
					            echo(line, file=self.file, color=self.color, nl=nl) | 
				
			||||||
 | 
					            self.file.flush() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def make_step(self, n_steps): | 
				
			||||||
 | 
					        self.pos += n_steps | 
				
			||||||
 | 
					        if self.length_known and self.pos >= self.length: | 
				
			||||||
 | 
					            self.finished = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (time.time() - self.last_eta) < 1.0: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.last_eta = time.time() | 
				
			||||||
 | 
					        self.avg = self.avg[-6:] + [-(self.start - time.time()) / (self.pos)] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.eta_known = self.length_known | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, n_steps): | 
				
			||||||
 | 
					        self.make_step(n_steps) | 
				
			||||||
 | 
					        self.render_progress() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def finish(self): | 
				
			||||||
 | 
					        self.eta_known = 0 | 
				
			||||||
 | 
					        self.current_item = None | 
				
			||||||
 | 
					        self.finished = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def next(self): | 
				
			||||||
 | 
					        if self.is_hidden: | 
				
			||||||
 | 
					            return next(self.iter) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            rv = next(self.iter) | 
				
			||||||
 | 
					            self.current_item = rv | 
				
			||||||
 | 
					        except StopIteration: | 
				
			||||||
 | 
					            self.finish() | 
				
			||||||
 | 
					            self.render_progress() | 
				
			||||||
 | 
					            raise StopIteration() | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.update(1) | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not PY2: | 
				
			||||||
 | 
					        __next__ = next | 
				
			||||||
 | 
					        del next | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pager(text, color=None): | 
				
			||||||
 | 
					    """Decide what method to use for paging through text.""" | 
				
			||||||
 | 
					    stdout = _default_text_stdout() | 
				
			||||||
 | 
					    if not isatty(sys.stdin) or not isatty(stdout): | 
				
			||||||
 | 
					        return _nullpager(stdout, text, color) | 
				
			||||||
 | 
					    pager_cmd = (os.environ.get('PAGER', None) or '').strip() | 
				
			||||||
 | 
					    if pager_cmd: | 
				
			||||||
 | 
					        if WIN: | 
				
			||||||
 | 
					            return _tempfilepager(text, pager_cmd, color) | 
				
			||||||
 | 
					        return _pipepager(text, pager_cmd, color) | 
				
			||||||
 | 
					    if os.environ.get('TERM') in ('dumb', 'emacs'): | 
				
			||||||
 | 
					        return _nullpager(stdout, text, color) | 
				
			||||||
 | 
					    if WIN or sys.platform.startswith('os2'): | 
				
			||||||
 | 
					        return _tempfilepager(text, 'more <', color) | 
				
			||||||
 | 
					    if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: | 
				
			||||||
 | 
					        return _pipepager(text, 'less', color) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import tempfile | 
				
			||||||
 | 
					    fd, filename = tempfile.mkstemp() | 
				
			||||||
 | 
					    os.close(fd) | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: | 
				
			||||||
 | 
					            return _pipepager(text, 'more', color) | 
				
			||||||
 | 
					        return _nullpager(stdout, text, color) | 
				
			||||||
 | 
					    finally: | 
				
			||||||
 | 
					        os.unlink(filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _pipepager(text, cmd, color): | 
				
			||||||
 | 
					    """Page through text by feeding it to another program.  Invoking a | 
				
			||||||
 | 
					    pager through this might support colors. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    import subprocess | 
				
			||||||
 | 
					    env = dict(os.environ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # If we're piping to less we might support colors under the | 
				
			||||||
 | 
					    # condition that | 
				
			||||||
 | 
					    cmd_detail = cmd.rsplit('/', 1)[-1].split() | 
				
			||||||
 | 
					    if color is None and cmd_detail[0] == 'less': | 
				
			||||||
 | 
					        less_flags = os.environ.get('LESS', '') + ' '.join(cmd_detail[1:]) | 
				
			||||||
 | 
					        if not less_flags: | 
				
			||||||
 | 
					            env['LESS'] = '-R' | 
				
			||||||
 | 
					            color = True | 
				
			||||||
 | 
					        elif 'r' in less_flags or 'R' in less_flags: | 
				
			||||||
 | 
					            color = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not color: | 
				
			||||||
 | 
					        text = strip_ansi(text) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, | 
				
			||||||
 | 
					                         env=env) | 
				
			||||||
 | 
					    encoding = get_best_encoding(c.stdin) | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        c.stdin.write(text.encode(encoding, 'replace')) | 
				
			||||||
 | 
					        c.stdin.close() | 
				
			||||||
 | 
					    except (IOError, KeyboardInterrupt): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Less doesn't respect ^C, but catches it for its own UI purposes (aborting | 
				
			||||||
 | 
					    # search or other commands inside less). | 
				
			||||||
 | 
					    # | 
				
			||||||
 | 
					    # That means when the user hits ^C, the parent process (click) terminates, | 
				
			||||||
 | 
					    # but less is still alive, paging the output and messing up the terminal. | 
				
			||||||
 | 
					    # | 
				
			||||||
 | 
					    # If the user wants to make the pager exit on ^C, they should set | 
				
			||||||
 | 
					    # `LESS='-K'`. It's not our decision to make. | 
				
			||||||
 | 
					    while True: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            c.wait() | 
				
			||||||
 | 
					        except KeyboardInterrupt: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _tempfilepager(text, cmd, color): | 
				
			||||||
 | 
					    """Page through text by invoking a program on a temporary file.""" | 
				
			||||||
 | 
					    import tempfile | 
				
			||||||
 | 
					    filename = tempfile.mktemp() | 
				
			||||||
 | 
					    if not color: | 
				
			||||||
 | 
					        text = strip_ansi(text) | 
				
			||||||
 | 
					    encoding = get_best_encoding(sys.stdout) | 
				
			||||||
 | 
					    with open_stream(filename, 'wb')[0] as f: | 
				
			||||||
 | 
					        f.write(text.encode(encoding)) | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        os.system(cmd + ' "' + filename + '"') | 
				
			||||||
 | 
					    finally: | 
				
			||||||
 | 
					        os.unlink(filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _nullpager(stream, text, color): | 
				
			||||||
 | 
					    """Simply print unformatted text.  This is the ultimate fallback.""" | 
				
			||||||
 | 
					    if not color: | 
				
			||||||
 | 
					        text = strip_ansi(text) | 
				
			||||||
 | 
					    stream.write(text) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Editor(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, editor=None, env=None, require_save=True, | 
				
			||||||
 | 
					                 extension='.txt'): | 
				
			||||||
 | 
					        self.editor = editor | 
				
			||||||
 | 
					        self.env = env | 
				
			||||||
 | 
					        self.require_save = require_save | 
				
			||||||
 | 
					        self.extension = extension | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_editor(self): | 
				
			||||||
 | 
					        if self.editor is not None: | 
				
			||||||
 | 
					            return self.editor | 
				
			||||||
 | 
					        for key in 'VISUAL', 'EDITOR': | 
				
			||||||
 | 
					            rv = os.environ.get(key) | 
				
			||||||
 | 
					            if rv: | 
				
			||||||
 | 
					                return rv | 
				
			||||||
 | 
					        if WIN: | 
				
			||||||
 | 
					            return 'notepad' | 
				
			||||||
 | 
					        for editor in 'vim', 'nano': | 
				
			||||||
 | 
					            if os.system('which %s >/dev/null 2>&1' % editor) == 0: | 
				
			||||||
 | 
					                return editor | 
				
			||||||
 | 
					        return 'vi' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def edit_file(self, filename): | 
				
			||||||
 | 
					        import subprocess | 
				
			||||||
 | 
					        editor = self.get_editor() | 
				
			||||||
 | 
					        if self.env: | 
				
			||||||
 | 
					            environ = os.environ.copy() | 
				
			||||||
 | 
					            environ.update(self.env) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            environ = None | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            c = subprocess.Popen('%s "%s"' % (editor, filename), | 
				
			||||||
 | 
					                                 env=environ, shell=True) | 
				
			||||||
 | 
					            exit_code = c.wait() | 
				
			||||||
 | 
					            if exit_code != 0: | 
				
			||||||
 | 
					                raise ClickException('%s: Editing failed!' % editor) | 
				
			||||||
 | 
					        except OSError as e: | 
				
			||||||
 | 
					            raise ClickException('%s: Editing failed: %s' % (editor, e)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def edit(self, text): | 
				
			||||||
 | 
					        import tempfile | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        text = text or '' | 
				
			||||||
 | 
					        if text and not text.endswith('\n'): | 
				
			||||||
 | 
					            text += '\n' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fd, name = tempfile.mkstemp(prefix='editor-', suffix=self.extension) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            if WIN: | 
				
			||||||
 | 
					                encoding = 'utf-8-sig' | 
				
			||||||
 | 
					                text = text.replace('\n', '\r\n') | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                encoding = 'utf-8' | 
				
			||||||
 | 
					            text = text.encode(encoding) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            f = os.fdopen(fd, 'wb') | 
				
			||||||
 | 
					            f.write(text) | 
				
			||||||
 | 
					            f.close() | 
				
			||||||
 | 
					            timestamp = os.path.getmtime(name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.edit_file(name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if self.require_save \ | 
				
			||||||
 | 
					               and os.path.getmtime(name) == timestamp: | 
				
			||||||
 | 
					                return None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            f = open(name, 'rb') | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                rv = f.read() | 
				
			||||||
 | 
					            finally: | 
				
			||||||
 | 
					                f.close() | 
				
			||||||
 | 
					            return rv.decode('utf-8-sig').replace('\r\n', '\n') | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            os.unlink(name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def open_url(url, wait=False, locate=False): | 
				
			||||||
 | 
					    import subprocess | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _unquote_file(url): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            import urllib | 
				
			||||||
 | 
					        except ImportError: | 
				
			||||||
 | 
					            import urllib | 
				
			||||||
 | 
					        if url.startswith('file://'): | 
				
			||||||
 | 
					            url = urllib.unquote(url[7:]) | 
				
			||||||
 | 
					        return url | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sys.platform == 'darwin': | 
				
			||||||
 | 
					        args = ['open'] | 
				
			||||||
 | 
					        if wait: | 
				
			||||||
 | 
					            args.append('-W') | 
				
			||||||
 | 
					        if locate: | 
				
			||||||
 | 
					            args.append('-R') | 
				
			||||||
 | 
					        args.append(_unquote_file(url)) | 
				
			||||||
 | 
					        null = open('/dev/null', 'w') | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return subprocess.Popen(args, stderr=null).wait() | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            null.close() | 
				
			||||||
 | 
					    elif WIN: | 
				
			||||||
 | 
					        if locate: | 
				
			||||||
 | 
					            url = _unquote_file(url) | 
				
			||||||
 | 
					            args = 'explorer /select,"%s"' % _unquote_file( | 
				
			||||||
 | 
					                url.replace('"', '')) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            args = 'start %s "" "%s"' % ( | 
				
			||||||
 | 
					                wait and '/WAIT' or '', url.replace('"', '')) | 
				
			||||||
 | 
					        return os.system(args) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        if locate: | 
				
			||||||
 | 
					            url = os.path.dirname(_unquote_file(url)) or '.' | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            url = _unquote_file(url) | 
				
			||||||
 | 
					        c = subprocess.Popen(['xdg-open', url]) | 
				
			||||||
 | 
					        if wait: | 
				
			||||||
 | 
					            return c.wait() | 
				
			||||||
 | 
					        return 0 | 
				
			||||||
 | 
					    except OSError: | 
				
			||||||
 | 
					        if url.startswith(('http://', 'https://')) and not locate and not wait: | 
				
			||||||
 | 
					            import webbrowser | 
				
			||||||
 | 
					            webbrowser.open(url) | 
				
			||||||
 | 
					            return 0 | 
				
			||||||
 | 
					        return 1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _translate_ch_to_exc(ch): | 
				
			||||||
 | 
					    if ch == '\x03': | 
				
			||||||
 | 
					        raise KeyboardInterrupt() | 
				
			||||||
 | 
					    if ch == '\x04': | 
				
			||||||
 | 
					        raise EOFError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if WIN: | 
				
			||||||
 | 
					    import msvcrt | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getchar(echo): | 
				
			||||||
 | 
					        rv = msvcrt.getch() | 
				
			||||||
 | 
					        if echo: | 
				
			||||||
 | 
					            msvcrt.putchar(rv) | 
				
			||||||
 | 
					        _translate_ch_to_exc(rv) | 
				
			||||||
 | 
					        if PY2: | 
				
			||||||
 | 
					            enc = getattr(sys.stdin, 'encoding', None) | 
				
			||||||
 | 
					            if enc is not None: | 
				
			||||||
 | 
					                rv = rv.decode(enc, 'replace') | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                rv = rv.decode('cp1252', 'replace') | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import tty | 
				
			||||||
 | 
					    import termios | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getchar(echo): | 
				
			||||||
 | 
					        if not isatty(sys.stdin): | 
				
			||||||
 | 
					            f = open('/dev/tty') | 
				
			||||||
 | 
					            fd = f.fileno() | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            fd = sys.stdin.fileno() | 
				
			||||||
 | 
					            f = None | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            old_settings = termios.tcgetattr(fd) | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                tty.setraw(fd) | 
				
			||||||
 | 
					                ch = os.read(fd, 32) | 
				
			||||||
 | 
					                if echo and isatty(sys.stdout): | 
				
			||||||
 | 
					                    sys.stdout.write(ch) | 
				
			||||||
 | 
					            finally: | 
				
			||||||
 | 
					                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) | 
				
			||||||
 | 
					                sys.stdout.flush() | 
				
			||||||
 | 
					                if f is not None: | 
				
			||||||
 | 
					                    f.close() | 
				
			||||||
 | 
					        except termios.error: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					        _translate_ch_to_exc(ch) | 
				
			||||||
 | 
					        return ch.decode(get_best_encoding(sys.stdin), 'replace') | 
				
			||||||
@ -0,0 +1,38 @@ | 
				
			|||||||
 | 
					import textwrap | 
				
			||||||
 | 
					from contextlib import contextmanager | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TextWrapper(textwrap.TextWrapper): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): | 
				
			||||||
 | 
					        space_left = max(width - cur_len, 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.break_long_words: | 
				
			||||||
 | 
					            last = reversed_chunks[-1] | 
				
			||||||
 | 
					            cut = last[:space_left] | 
				
			||||||
 | 
					            res = last[space_left:] | 
				
			||||||
 | 
					            cur_line.append(cut) | 
				
			||||||
 | 
					            reversed_chunks[-1] = res | 
				
			||||||
 | 
					        elif not cur_line: | 
				
			||||||
 | 
					            cur_line.append(reversed_chunks.pop()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @contextmanager | 
				
			||||||
 | 
					    def extra_indent(self, indent): | 
				
			||||||
 | 
					        old_initial_indent = self.initial_indent | 
				
			||||||
 | 
					        old_subsequent_indent = self.subsequent_indent | 
				
			||||||
 | 
					        self.initial_indent += indent | 
				
			||||||
 | 
					        self.subsequent_indent += indent | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            yield | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            self.initial_indent = old_initial_indent | 
				
			||||||
 | 
					            self.subsequent_indent = old_subsequent_indent | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def indent_only(self, text): | 
				
			||||||
 | 
					        rv = [] | 
				
			||||||
 | 
					        for idx, line in enumerate(text.splitlines()): | 
				
			||||||
 | 
					            indent = self.initial_indent | 
				
			||||||
 | 
					            if idx > 0: | 
				
			||||||
 | 
					                indent = self.subsequent_indent | 
				
			||||||
 | 
					            rv.append(indent + line) | 
				
			||||||
 | 
					        return '\n'.join(rv) | 
				
			||||||
@ -0,0 +1,118 @@ | 
				
			|||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import codecs | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ._compat import PY2 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If someone wants to vendor click, we want to ensure the | 
				
			||||||
 | 
					# correct package is discovered.  Ideally we could use a | 
				
			||||||
 | 
					# relative import here but unfortunately Python does not | 
				
			||||||
 | 
					# support that. | 
				
			||||||
 | 
					click = sys.modules[__name__.rsplit('.', 1)[0]] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _find_unicode_literals_frame(): | 
				
			||||||
 | 
					    import __future__ | 
				
			||||||
 | 
					    frm = sys._getframe(1) | 
				
			||||||
 | 
					    idx = 1 | 
				
			||||||
 | 
					    while frm is not None: | 
				
			||||||
 | 
					        if frm.f_globals.get('__name__', '').startswith('click.'): | 
				
			||||||
 | 
					            frm = frm.f_back | 
				
			||||||
 | 
					            idx += 1 | 
				
			||||||
 | 
					        elif frm.f_code.co_flags & __future__.unicode_literals.compiler_flag: | 
				
			||||||
 | 
					            return idx | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            break | 
				
			||||||
 | 
					    return 0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _check_for_unicode_literals(): | 
				
			||||||
 | 
					    if not __debug__: | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					    if not PY2 or click.disable_unicode_literals_warning: | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					    bad_frame = _find_unicode_literals_frame() | 
				
			||||||
 | 
					    if bad_frame <= 0: | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					    from warnings import warn | 
				
			||||||
 | 
					    warn(Warning('Click detected the use of the unicode_literals ' | 
				
			||||||
 | 
					                 '__future__ import.  This is heavily discouraged ' | 
				
			||||||
 | 
					                 'because it can introduce subtle bugs in your ' | 
				
			||||||
 | 
					                 'code.  You should instead use explicit u"" literals ' | 
				
			||||||
 | 
					                 'for your unicode strings.  For more information see ' | 
				
			||||||
 | 
					                 'http://click.pocoo.org/python3/'), | 
				
			||||||
 | 
					         stacklevel=bad_frame) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _verify_python3_env(): | 
				
			||||||
 | 
					    """Ensures that the environment is good for unicode on Python 3.""" | 
				
			||||||
 | 
					    if PY2: | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        import locale | 
				
			||||||
 | 
					        fs_enc = codecs.lookup(locale.getpreferredencoding()).name | 
				
			||||||
 | 
					    except Exception: | 
				
			||||||
 | 
					        fs_enc = 'ascii' | 
				
			||||||
 | 
					    if fs_enc != 'ascii': | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extra = '' | 
				
			||||||
 | 
					    if os.name == 'posix': | 
				
			||||||
 | 
					        import subprocess | 
				
			||||||
 | 
					        rv = subprocess.Popen(['locale', '-a'], stdout=subprocess.PIPE, | 
				
			||||||
 | 
					                              stderr=subprocess.PIPE).communicate()[0] | 
				
			||||||
 | 
					        good_locales = set() | 
				
			||||||
 | 
					        has_c_utf8 = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Make sure we're operating on text here. | 
				
			||||||
 | 
					        if isinstance(rv, bytes): | 
				
			||||||
 | 
					            rv = rv.decode('ascii', 'replace') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for line in rv.splitlines(): | 
				
			||||||
 | 
					            locale = line.strip() | 
				
			||||||
 | 
					            if locale.lower().endswith(('.utf-8', '.utf8')): | 
				
			||||||
 | 
					                good_locales.add(locale) | 
				
			||||||
 | 
					                if locale.lower() in ('c.utf8', 'c.utf-8'): | 
				
			||||||
 | 
					                    has_c_utf8 = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        extra += '\n\n' | 
				
			||||||
 | 
					        if not good_locales: | 
				
			||||||
 | 
					            extra += ( | 
				
			||||||
 | 
					                'Additional information: on this system no suitable UTF-8\n' | 
				
			||||||
 | 
					                'locales were discovered.  This most likely requires resolving\n' | 
				
			||||||
 | 
					                'by reconfiguring the locale system.' | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					        elif has_c_utf8: | 
				
			||||||
 | 
					            extra += ( | 
				
			||||||
 | 
					                'This system supports the C.UTF-8 locale which is recommended.\n' | 
				
			||||||
 | 
					                'You might be able to resolve your issue by exporting the\n' | 
				
			||||||
 | 
					                'following environment variables:\n\n' | 
				
			||||||
 | 
					                '    export LC_ALL=C.UTF-8\n' | 
				
			||||||
 | 
					                '    export LANG=C.UTF-8' | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            extra += ( | 
				
			||||||
 | 
					                'This system lists a couple of UTF-8 supporting locales that\n' | 
				
			||||||
 | 
					                'you can pick from.  The following suitable locales where\n' | 
				
			||||||
 | 
					                'discovered: %s' | 
				
			||||||
 | 
					            ) % ', '.join(sorted(good_locales)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bad_locale = None | 
				
			||||||
 | 
					        for locale in os.environ.get('LC_ALL'), os.environ.get('LANG'): | 
				
			||||||
 | 
					            if locale and locale.lower().endswith(('.utf-8', '.utf8')): | 
				
			||||||
 | 
					                bad_locale = locale | 
				
			||||||
 | 
					            if locale is not None: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					        if bad_locale is not None: | 
				
			||||||
 | 
					            extra += ( | 
				
			||||||
 | 
					                '\n\nClick discovered that you exported a UTF-8 locale\n' | 
				
			||||||
 | 
					                'but the locale system could not pick up from it because\n' | 
				
			||||||
 | 
					                'it does not exist.  The exported locale is "%s" but it\n' | 
				
			||||||
 | 
					                'is not supported' | 
				
			||||||
 | 
					            ) % bad_locale | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    raise RuntimeError('Click will abort further execution because Python 3 ' | 
				
			||||||
 | 
					                       'was configured to use ASCII as encoding for the ' | 
				
			||||||
 | 
					                       'environment.  Consult http://click.pocoo.org/python3/' | 
				
			||||||
 | 
					                       'for mitigation steps.' + extra) | 
				
			||||||
@ -0,0 +1,273 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					# This module is based on the excellent work by Adam Bartoš who | 
				
			||||||
 | 
					# provided a lot of what went into the implementation here in | 
				
			||||||
 | 
					# the discussion to issue1602 in the Python bug tracker. | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# There are some general differences in regards to how this works | 
				
			||||||
 | 
					# compared to the original patches as we do not need to patch | 
				
			||||||
 | 
					# the entire interpreter but just work in our little world of | 
				
			||||||
 | 
					# echo and prmopt. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import zlib | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					import ctypes | 
				
			||||||
 | 
					import msvcrt | 
				
			||||||
 | 
					from click._compat import _NonClosingTextIOWrapper, text_type, PY2 | 
				
			||||||
 | 
					from ctypes import byref, POINTER, c_int, c_char, c_char_p, \ | 
				
			||||||
 | 
					     c_void_p, py_object, c_ssize_t, c_ulong, windll, WINFUNCTYPE | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    from ctypes import pythonapi | 
				
			||||||
 | 
					    PyObject_GetBuffer = pythonapi.PyObject_GetBuffer | 
				
			||||||
 | 
					    PyBuffer_Release = pythonapi.PyBuffer_Release | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    pythonapi = None | 
				
			||||||
 | 
					from ctypes.wintypes import LPWSTR, LPCWSTR | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					c_ssize_p = POINTER(c_ssize_t) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					kernel32 = windll.kernel32 | 
				
			||||||
 | 
					GetStdHandle = kernel32.GetStdHandle | 
				
			||||||
 | 
					ReadConsoleW = kernel32.ReadConsoleW | 
				
			||||||
 | 
					WriteConsoleW = kernel32.WriteConsoleW | 
				
			||||||
 | 
					GetLastError = kernel32.GetLastError | 
				
			||||||
 | 
					GetCommandLineW = WINFUNCTYPE(LPWSTR)( | 
				
			||||||
 | 
					    ('GetCommandLineW', windll.kernel32)) | 
				
			||||||
 | 
					CommandLineToArgvW = WINFUNCTYPE( | 
				
			||||||
 | 
					    POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( | 
				
			||||||
 | 
					        ('CommandLineToArgvW', windll.shell32)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STDIN_HANDLE = GetStdHandle(-10) | 
				
			||||||
 | 
					STDOUT_HANDLE = GetStdHandle(-11) | 
				
			||||||
 | 
					STDERR_HANDLE = GetStdHandle(-12) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyBUF_SIMPLE = 0 | 
				
			||||||
 | 
					PyBUF_WRITABLE = 1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ERROR_SUCCESS = 0 | 
				
			||||||
 | 
					ERROR_NOT_ENOUGH_MEMORY = 8 | 
				
			||||||
 | 
					ERROR_OPERATION_ABORTED = 995 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STDIN_FILENO = 0 | 
				
			||||||
 | 
					STDOUT_FILENO = 1 | 
				
			||||||
 | 
					STDERR_FILENO = 2 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EOF = b'\x1a' | 
				
			||||||
 | 
					MAX_BYTES_WRITTEN = 32767 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Py_buffer(ctypes.Structure): | 
				
			||||||
 | 
					    _fields_ = [ | 
				
			||||||
 | 
					        ('buf', c_void_p), | 
				
			||||||
 | 
					        ('obj', py_object), | 
				
			||||||
 | 
					        ('len', c_ssize_t), | 
				
			||||||
 | 
					        ('itemsize', c_ssize_t), | 
				
			||||||
 | 
					        ('readonly', c_int), | 
				
			||||||
 | 
					        ('ndim', c_int), | 
				
			||||||
 | 
					        ('format', c_char_p), | 
				
			||||||
 | 
					        ('shape', c_ssize_p), | 
				
			||||||
 | 
					        ('strides', c_ssize_p), | 
				
			||||||
 | 
					        ('suboffsets', c_ssize_p), | 
				
			||||||
 | 
					        ('internal', c_void_p) | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if PY2: | 
				
			||||||
 | 
					        _fields_.insert(-1, ('smalltable', c_ssize_t * 2)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# On PyPy we cannot get buffers so our ability to operate here is | 
				
			||||||
 | 
					# serverly limited. | 
				
			||||||
 | 
					if pythonapi is None: | 
				
			||||||
 | 
					    get_buffer = None | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def get_buffer(obj, writable=False): | 
				
			||||||
 | 
					        buf = Py_buffer() | 
				
			||||||
 | 
					        flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE | 
				
			||||||
 | 
					        PyObject_GetBuffer(py_object(obj), byref(buf), flags) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            buffer_type = c_char * buf.len | 
				
			||||||
 | 
					            return buffer_type.from_address(buf.buf) | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            PyBuffer_Release(byref(buf)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _WindowsConsoleRawIOBase(io.RawIOBase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, handle): | 
				
			||||||
 | 
					        self.handle = handle | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def isatty(self): | 
				
			||||||
 | 
					        io.RawIOBase.isatty(self) | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _WindowsConsoleReader(_WindowsConsoleRawIOBase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def readable(self): | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def readinto(self, b): | 
				
			||||||
 | 
					        bytes_to_be_read = len(b) | 
				
			||||||
 | 
					        if not bytes_to_be_read: | 
				
			||||||
 | 
					            return 0 | 
				
			||||||
 | 
					        elif bytes_to_be_read % 2: | 
				
			||||||
 | 
					            raise ValueError('cannot read odd number of bytes from ' | 
				
			||||||
 | 
					                             'UTF-16-LE encoded console') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        buffer = get_buffer(b, writable=True) | 
				
			||||||
 | 
					        code_units_to_be_read = bytes_to_be_read // 2 | 
				
			||||||
 | 
					        code_units_read = c_ulong() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rv = ReadConsoleW(self.handle, buffer, code_units_to_be_read, | 
				
			||||||
 | 
					                          byref(code_units_read), None) | 
				
			||||||
 | 
					        if GetLastError() == ERROR_OPERATION_ABORTED: | 
				
			||||||
 | 
					            # wait for KeyboardInterrupt | 
				
			||||||
 | 
					            time.sleep(0.1) | 
				
			||||||
 | 
					        if not rv: | 
				
			||||||
 | 
					            raise OSError('Windows error: %s' % GetLastError()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if buffer[0] == EOF: | 
				
			||||||
 | 
					            return 0 | 
				
			||||||
 | 
					        return 2 * code_units_read.value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def writable(self): | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod | 
				
			||||||
 | 
					    def _get_error_message(errno): | 
				
			||||||
 | 
					        if errno == ERROR_SUCCESS: | 
				
			||||||
 | 
					            return 'ERROR_SUCCESS' | 
				
			||||||
 | 
					        elif errno == ERROR_NOT_ENOUGH_MEMORY: | 
				
			||||||
 | 
					            return 'ERROR_NOT_ENOUGH_MEMORY' | 
				
			||||||
 | 
					        return 'Windows error %s' % errno | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write(self, b): | 
				
			||||||
 | 
					        bytes_to_be_written = len(b) | 
				
			||||||
 | 
					        buf = get_buffer(b) | 
				
			||||||
 | 
					        code_units_to_be_written = min(bytes_to_be_written, | 
				
			||||||
 | 
					                                       MAX_BYTES_WRITTEN) // 2 | 
				
			||||||
 | 
					        code_units_written = c_ulong() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        WriteConsoleW(self.handle, buf, code_units_to_be_written, | 
				
			||||||
 | 
					                      byref(code_units_written), None) | 
				
			||||||
 | 
					        bytes_written = 2 * code_units_written.value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if bytes_written == 0 and bytes_to_be_written > 0: | 
				
			||||||
 | 
					            raise OSError(self._get_error_message(GetLastError())) | 
				
			||||||
 | 
					        return bytes_written | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConsoleStream(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, text_stream, byte_stream): | 
				
			||||||
 | 
					        self._text_stream = text_stream | 
				
			||||||
 | 
					        self.buffer = byte_stream | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def name(self): | 
				
			||||||
 | 
					        return self.buffer.name | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write(self, x): | 
				
			||||||
 | 
					        if isinstance(x, text_type): | 
				
			||||||
 | 
					            return self._text_stream.write(x) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.flush() | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					        return self.buffer.write(x) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def writelines(self, lines): | 
				
			||||||
 | 
					        for line in lines: | 
				
			||||||
 | 
					            self.write(line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, name): | 
				
			||||||
 | 
					        return getattr(self._text_stream, name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def isatty(self): | 
				
			||||||
 | 
					        return self.buffer.isatty() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return '<ConsoleStream name=%r encoding=%r>' % ( | 
				
			||||||
 | 
					            self.name, | 
				
			||||||
 | 
					            self.encoding, | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _get_text_stdin(buffer_stream): | 
				
			||||||
 | 
					    text_stream = _NonClosingTextIOWrapper( | 
				
			||||||
 | 
					        io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), | 
				
			||||||
 | 
					        'utf-16-le', 'strict', line_buffering=True) | 
				
			||||||
 | 
					    return ConsoleStream(text_stream, buffer_stream) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _get_text_stdout(buffer_stream): | 
				
			||||||
 | 
					    text_stream = _NonClosingTextIOWrapper( | 
				
			||||||
 | 
					        _WindowsConsoleWriter(STDOUT_HANDLE), | 
				
			||||||
 | 
					        'utf-16-le', 'strict', line_buffering=True) | 
				
			||||||
 | 
					    return ConsoleStream(text_stream, buffer_stream) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _get_text_stderr(buffer_stream): | 
				
			||||||
 | 
					    text_stream = _NonClosingTextIOWrapper( | 
				
			||||||
 | 
					        _WindowsConsoleWriter(STDERR_HANDLE), | 
				
			||||||
 | 
					        'utf-16-le', 'strict', line_buffering=True) | 
				
			||||||
 | 
					    return ConsoleStream(text_stream, buffer_stream) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY2: | 
				
			||||||
 | 
					    def _hash_py_argv(): | 
				
			||||||
 | 
					        return zlib.crc32('\x00'.join(sys.argv[1:])) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _initial_argv_hash = _hash_py_argv() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_windows_argv(): | 
				
			||||||
 | 
					        argc = c_int(0) | 
				
			||||||
 | 
					        argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) | 
				
			||||||
 | 
					        argv = [argv_unicode[i] for i in range(0, argc.value)] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not hasattr(sys, 'frozen'): | 
				
			||||||
 | 
					            argv = argv[1:] | 
				
			||||||
 | 
					            while len(argv) > 0: | 
				
			||||||
 | 
					                arg = argv[0] | 
				
			||||||
 | 
					                if not arg.startswith('-') or arg == '-': | 
				
			||||||
 | 
					                    break | 
				
			||||||
 | 
					                argv = argv[1:] | 
				
			||||||
 | 
					                if arg.startswith(('-c', '-m')): | 
				
			||||||
 | 
					                    break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return argv[1:] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_stream_factories = { | 
				
			||||||
 | 
					    0: _get_text_stdin, | 
				
			||||||
 | 
					    1: _get_text_stdout, | 
				
			||||||
 | 
					    2: _get_text_stderr, | 
				
			||||||
 | 
					} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _get_windows_console_stream(f, encoding, errors): | 
				
			||||||
 | 
					    if get_buffer is not None and \ | 
				
			||||||
 | 
					       encoding in ('utf-16-le', None) \ | 
				
			||||||
 | 
					       and errors in ('strict', None) and \ | 
				
			||||||
 | 
					       hasattr(f, 'isatty') and f.isatty(): | 
				
			||||||
 | 
					        func = _stream_factories.get(f.fileno()) | 
				
			||||||
 | 
					        if func is not None: | 
				
			||||||
 | 
					            if not PY2: | 
				
			||||||
 | 
					                f = getattr(f, 'buffer') | 
				
			||||||
 | 
					                if f is None: | 
				
			||||||
 | 
					                    return None | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                # If we are on Python 2 we need to set the stream that we | 
				
			||||||
 | 
					                # deal with to binary mode as otherwise the exercise if a | 
				
			||||||
 | 
					                # bit moot.  The same problems apply as for | 
				
			||||||
 | 
					                # get_binary_stdin and friends from _compat. | 
				
			||||||
 | 
					                msvcrt.setmode(f.fileno(), os.O_BINARY) | 
				
			||||||
 | 
					            return func(f) | 
				
			||||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -0,0 +1,304 @@ | 
				
			|||||||
 | 
					import sys | 
				
			||||||
 | 
					import inspect | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from functools import update_wrapper | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ._compat import iteritems | 
				
			||||||
 | 
					from ._unicodefun import _check_for_unicode_literals | 
				
			||||||
 | 
					from .utils import echo | 
				
			||||||
 | 
					from .globals import get_current_context | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pass_context(f): | 
				
			||||||
 | 
					    """Marks a callback as wanting to receive the current context | 
				
			||||||
 | 
					    object as first argument. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def new_func(*args, **kwargs): | 
				
			||||||
 | 
					        return f(get_current_context(), *args, **kwargs) | 
				
			||||||
 | 
					    return update_wrapper(new_func, f) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pass_obj(f): | 
				
			||||||
 | 
					    """Similar to :func:`pass_context`, but only pass the object on the | 
				
			||||||
 | 
					    context onwards (:attr:`Context.obj`).  This is useful if that object | 
				
			||||||
 | 
					    represents the state of a nested system. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def new_func(*args, **kwargs): | 
				
			||||||
 | 
					        return f(get_current_context().obj, *args, **kwargs) | 
				
			||||||
 | 
					    return update_wrapper(new_func, f) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_pass_decorator(object_type, ensure=False): | 
				
			||||||
 | 
					    """Given an object type this creates a decorator that will work | 
				
			||||||
 | 
					    similar to :func:`pass_obj` but instead of passing the object of the | 
				
			||||||
 | 
					    current context, it will find the innermost context of type | 
				
			||||||
 | 
					    :func:`object_type`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This generates a decorator that works roughly like this:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from functools import update_wrapper | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def decorator(f): | 
				
			||||||
 | 
					            @pass_context | 
				
			||||||
 | 
					            def new_func(ctx, *args, **kwargs): | 
				
			||||||
 | 
					                obj = ctx.find_object(object_type) | 
				
			||||||
 | 
					                return ctx.invoke(f, obj, *args, **kwargs) | 
				
			||||||
 | 
					            return update_wrapper(new_func, f) | 
				
			||||||
 | 
					        return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param object_type: the type of the object to pass. | 
				
			||||||
 | 
					    :param ensure: if set to `True`, a new object will be created and | 
				
			||||||
 | 
					                   remembered on the context if it's not there yet. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def decorator(f): | 
				
			||||||
 | 
					        def new_func(*args, **kwargs): | 
				
			||||||
 | 
					            ctx = get_current_context() | 
				
			||||||
 | 
					            if ensure: | 
				
			||||||
 | 
					                obj = ctx.ensure_object(object_type) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                obj = ctx.find_object(object_type) | 
				
			||||||
 | 
					            if obj is None: | 
				
			||||||
 | 
					                raise RuntimeError('Managed to invoke callback without a ' | 
				
			||||||
 | 
					                                   'context object of type %r existing' | 
				
			||||||
 | 
					                                   % object_type.__name__) | 
				
			||||||
 | 
					            return ctx.invoke(f, obj, *args[1:], **kwargs) | 
				
			||||||
 | 
					        return update_wrapper(new_func, f) | 
				
			||||||
 | 
					    return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _make_command(f, name, attrs, cls): | 
				
			||||||
 | 
					    if isinstance(f, Command): | 
				
			||||||
 | 
					        raise TypeError('Attempted to convert a callback into a ' | 
				
			||||||
 | 
					                        'command twice.') | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        params = f.__click_params__ | 
				
			||||||
 | 
					        params.reverse() | 
				
			||||||
 | 
					        del f.__click_params__ | 
				
			||||||
 | 
					    except AttributeError: | 
				
			||||||
 | 
					        params = [] | 
				
			||||||
 | 
					    help = attrs.get('help') | 
				
			||||||
 | 
					    if help is None: | 
				
			||||||
 | 
					        help = inspect.getdoc(f) | 
				
			||||||
 | 
					        if isinstance(help, bytes): | 
				
			||||||
 | 
					            help = help.decode('utf-8') | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        help = inspect.cleandoc(help) | 
				
			||||||
 | 
					    attrs['help'] = help | 
				
			||||||
 | 
					    _check_for_unicode_literals() | 
				
			||||||
 | 
					    return cls(name=name or f.__name__.lower(), | 
				
			||||||
 | 
					               callback=f, params=params, **attrs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def command(name=None, cls=None, **attrs): | 
				
			||||||
 | 
					    """Creates a new :class:`Command` and uses the decorated function as | 
				
			||||||
 | 
					    callback.  This will also automatically attach all decorated | 
				
			||||||
 | 
					    :func:`option`\s and :func:`argument`\s as parameters to the command. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The name of the command defaults to the name of the function.  If you | 
				
			||||||
 | 
					    want to change that, you can pass the intended name as the first | 
				
			||||||
 | 
					    argument. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    All keyword arguments are forwarded to the underlying command class. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Once decorated the function turns into a :class:`Command` instance | 
				
			||||||
 | 
					    that can be invoked as a command line utility or be attached to a | 
				
			||||||
 | 
					    command :class:`Group`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param name: the name of the command.  This defaults to the function | 
				
			||||||
 | 
					                 name. | 
				
			||||||
 | 
					    :param cls: the command class to instantiate.  This defaults to | 
				
			||||||
 | 
					                :class:`Command`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if cls is None: | 
				
			||||||
 | 
					        cls = Command | 
				
			||||||
 | 
					    def decorator(f): | 
				
			||||||
 | 
					        cmd = _make_command(f, name, attrs, cls) | 
				
			||||||
 | 
					        cmd.__doc__ = f.__doc__ | 
				
			||||||
 | 
					        return cmd | 
				
			||||||
 | 
					    return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def group(name=None, **attrs): | 
				
			||||||
 | 
					    """Creates a new :class:`Group` with a function as callback.  This | 
				
			||||||
 | 
					    works otherwise the same as :func:`command` just that the `cls` | 
				
			||||||
 | 
					    parameter is set to :class:`Group`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    attrs.setdefault('cls', Group) | 
				
			||||||
 | 
					    return command(name, **attrs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _param_memo(f, param): | 
				
			||||||
 | 
					    if isinstance(f, Command): | 
				
			||||||
 | 
					        f.params.append(param) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        if not hasattr(f, '__click_params__'): | 
				
			||||||
 | 
					            f.__click_params__ = [] | 
				
			||||||
 | 
					        f.__click_params__.append(param) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def argument(*param_decls, **attrs): | 
				
			||||||
 | 
					    """Attaches an argument to the command.  All positional arguments are | 
				
			||||||
 | 
					    passed as parameter declarations to :class:`Argument`; all keyword | 
				
			||||||
 | 
					    arguments are forwarded unchanged (except ``cls``). | 
				
			||||||
 | 
					    This is equivalent to creating an :class:`Argument` instance manually | 
				
			||||||
 | 
					    and attaching it to the :attr:`Command.params` list. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param cls: the argument class to instantiate.  This defaults to | 
				
			||||||
 | 
					                :class:`Argument`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def decorator(f): | 
				
			||||||
 | 
					        ArgumentClass = attrs.pop('cls', Argument) | 
				
			||||||
 | 
					        _param_memo(f, ArgumentClass(param_decls, **attrs)) | 
				
			||||||
 | 
					        return f | 
				
			||||||
 | 
					    return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def option(*param_decls, **attrs): | 
				
			||||||
 | 
					    """Attaches an option to the command.  All positional arguments are | 
				
			||||||
 | 
					    passed as parameter declarations to :class:`Option`; all keyword | 
				
			||||||
 | 
					    arguments are forwarded unchanged (except ``cls``). | 
				
			||||||
 | 
					    This is equivalent to creating an :class:`Option` instance manually | 
				
			||||||
 | 
					    and attaching it to the :attr:`Command.params` list. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param cls: the option class to instantiate.  This defaults to | 
				
			||||||
 | 
					                :class:`Option`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def decorator(f): | 
				
			||||||
 | 
					        if 'help' in attrs: | 
				
			||||||
 | 
					            attrs['help'] = inspect.cleandoc(attrs['help']) | 
				
			||||||
 | 
					        OptionClass = attrs.pop('cls', Option) | 
				
			||||||
 | 
					        _param_memo(f, OptionClass(param_decls, **attrs)) | 
				
			||||||
 | 
					        return f | 
				
			||||||
 | 
					    return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def confirmation_option(*param_decls, **attrs): | 
				
			||||||
 | 
					    """Shortcut for confirmation prompts that can be ignored by passing | 
				
			||||||
 | 
					    ``--yes`` as parameter. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This is equivalent to decorating a function with :func:`option` with | 
				
			||||||
 | 
					    the following parameters:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def callback(ctx, param, value): | 
				
			||||||
 | 
					            if not value: | 
				
			||||||
 | 
					                ctx.abort() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @click.command() | 
				
			||||||
 | 
					        @click.option('--yes', is_flag=True, callback=callback, | 
				
			||||||
 | 
					                      expose_value=False, prompt='Do you want to continue?') | 
				
			||||||
 | 
					        def dropdb(): | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def decorator(f): | 
				
			||||||
 | 
					        def callback(ctx, param, value): | 
				
			||||||
 | 
					            if not value: | 
				
			||||||
 | 
					                ctx.abort() | 
				
			||||||
 | 
					        attrs.setdefault('is_flag', True) | 
				
			||||||
 | 
					        attrs.setdefault('callback', callback) | 
				
			||||||
 | 
					        attrs.setdefault('expose_value', False) | 
				
			||||||
 | 
					        attrs.setdefault('prompt', 'Do you want to continue?') | 
				
			||||||
 | 
					        attrs.setdefault('help', 'Confirm the action without prompting.') | 
				
			||||||
 | 
					        return option(*(param_decls or ('--yes',)), **attrs)(f) | 
				
			||||||
 | 
					    return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def password_option(*param_decls, **attrs): | 
				
			||||||
 | 
					    """Shortcut for password prompts. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This is equivalent to decorating a function with :func:`option` with | 
				
			||||||
 | 
					    the following parameters:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @click.command() | 
				
			||||||
 | 
					        @click.option('--password', prompt=True, confirmation_prompt=True, | 
				
			||||||
 | 
					                      hide_input=True) | 
				
			||||||
 | 
					        def changeadmin(password): | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def decorator(f): | 
				
			||||||
 | 
					        attrs.setdefault('prompt', True) | 
				
			||||||
 | 
					        attrs.setdefault('confirmation_prompt', True) | 
				
			||||||
 | 
					        attrs.setdefault('hide_input', True) | 
				
			||||||
 | 
					        return option(*(param_decls or ('--password',)), **attrs)(f) | 
				
			||||||
 | 
					    return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def version_option(version=None, *param_decls, **attrs): | 
				
			||||||
 | 
					    """Adds a ``--version`` option which immediately ends the program | 
				
			||||||
 | 
					    printing out the version number.  This is implemented as an eager | 
				
			||||||
 | 
					    option that prints the version and exits the program in the callback. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param version: the version number to show.  If not provided Click | 
				
			||||||
 | 
					                    attempts an auto discovery via setuptools. | 
				
			||||||
 | 
					    :param prog_name: the name of the program (defaults to autodetection) | 
				
			||||||
 | 
					    :param message: custom message to show instead of the default | 
				
			||||||
 | 
					                    (``'%(prog)s, version %(version)s'``) | 
				
			||||||
 | 
					    :param others: everything else is forwarded to :func:`option`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if version is None: | 
				
			||||||
 | 
					        module = sys._getframe(1).f_globals.get('__name__') | 
				
			||||||
 | 
					    def decorator(f): | 
				
			||||||
 | 
					        prog_name = attrs.pop('prog_name', None) | 
				
			||||||
 | 
					        message = attrs.pop('message', '%(prog)s, version %(version)s') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def callback(ctx, param, value): | 
				
			||||||
 | 
					            if not value or ctx.resilient_parsing: | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					            prog = prog_name | 
				
			||||||
 | 
					            if prog is None: | 
				
			||||||
 | 
					                prog = ctx.find_root().info_name | 
				
			||||||
 | 
					            ver = version | 
				
			||||||
 | 
					            if ver is None: | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    import pkg_resources | 
				
			||||||
 | 
					                except ImportError: | 
				
			||||||
 | 
					                    pass | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    for dist in pkg_resources.working_set: | 
				
			||||||
 | 
					                        scripts = dist.get_entry_map().get('console_scripts') or {} | 
				
			||||||
 | 
					                        for script_name, entry_point in iteritems(scripts): | 
				
			||||||
 | 
					                            if entry_point.module_name == module: | 
				
			||||||
 | 
					                                ver = dist.version | 
				
			||||||
 | 
					                                break | 
				
			||||||
 | 
					                if ver is None: | 
				
			||||||
 | 
					                    raise RuntimeError('Could not determine version') | 
				
			||||||
 | 
					            echo(message % { | 
				
			||||||
 | 
					                'prog': prog, | 
				
			||||||
 | 
					                'version': ver, | 
				
			||||||
 | 
					            }, color=ctx.color) | 
				
			||||||
 | 
					            ctx.exit() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        attrs.setdefault('is_flag', True) | 
				
			||||||
 | 
					        attrs.setdefault('expose_value', False) | 
				
			||||||
 | 
					        attrs.setdefault('is_eager', True) | 
				
			||||||
 | 
					        attrs.setdefault('help', 'Show the version and exit.') | 
				
			||||||
 | 
					        attrs['callback'] = callback | 
				
			||||||
 | 
					        return option(*(param_decls or ('--version',)), **attrs)(f) | 
				
			||||||
 | 
					    return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def help_option(*param_decls, **attrs): | 
				
			||||||
 | 
					    """Adds a ``--help`` option which immediately ends the program | 
				
			||||||
 | 
					    printing out the help page.  This is usually unnecessary to add as | 
				
			||||||
 | 
					    this is added by default to all commands unless suppressed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Like :func:`version_option`, this is implemented as eager option that | 
				
			||||||
 | 
					    prints in the callback and exits. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    All arguments are forwarded to :func:`option`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def decorator(f): | 
				
			||||||
 | 
					        def callback(ctx, param, value): | 
				
			||||||
 | 
					            if value and not ctx.resilient_parsing: | 
				
			||||||
 | 
					                echo(ctx.get_help(), color=ctx.color) | 
				
			||||||
 | 
					                ctx.exit() | 
				
			||||||
 | 
					        attrs.setdefault('is_flag', True) | 
				
			||||||
 | 
					        attrs.setdefault('expose_value', False) | 
				
			||||||
 | 
					        attrs.setdefault('help', 'Show this message and exit.') | 
				
			||||||
 | 
					        attrs.setdefault('is_eager', True) | 
				
			||||||
 | 
					        attrs['callback'] = callback | 
				
			||||||
 | 
					        return option(*(param_decls or ('--help',)), **attrs)(f) | 
				
			||||||
 | 
					    return decorator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Circular dependencies between core and decorators | 
				
			||||||
 | 
					from .core import Command, Group, Argument, Option | 
				
			||||||
@ -0,0 +1,201 @@ | 
				
			|||||||
 | 
					from ._compat import PY2, filename_to_ui, get_text_stderr | 
				
			||||||
 | 
					from .utils import echo | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ClickException(Exception): | 
				
			||||||
 | 
					    """An exception that Click can handle and show to the user.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: The exit code for this exception | 
				
			||||||
 | 
					    exit_code = 1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, message): | 
				
			||||||
 | 
					        if PY2: | 
				
			||||||
 | 
					            if message is not None: | 
				
			||||||
 | 
					                message = message.encode('utf-8') | 
				
			||||||
 | 
					        Exception.__init__(self, message) | 
				
			||||||
 | 
					        self.message = message | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_message(self): | 
				
			||||||
 | 
					        return self.message | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def show(self, file=None): | 
				
			||||||
 | 
					        if file is None: | 
				
			||||||
 | 
					            file = get_text_stderr() | 
				
			||||||
 | 
					        echo('Error: %s' % self.format_message(), file=file) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UsageError(ClickException): | 
				
			||||||
 | 
					    """An internal exception that signals a usage error.  This typically | 
				
			||||||
 | 
					    aborts any further handling. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param message: the error message to display. | 
				
			||||||
 | 
					    :param ctx: optionally the context that caused this error.  Click will | 
				
			||||||
 | 
					                fill in the context automatically in some situations. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    exit_code = 2 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, message, ctx=None): | 
				
			||||||
 | 
					        ClickException.__init__(self, message) | 
				
			||||||
 | 
					        self.ctx = ctx | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def show(self, file=None): | 
				
			||||||
 | 
					        if file is None: | 
				
			||||||
 | 
					            file = get_text_stderr() | 
				
			||||||
 | 
					        color = None | 
				
			||||||
 | 
					        if self.ctx is not None: | 
				
			||||||
 | 
					            color = self.ctx.color | 
				
			||||||
 | 
					            echo(self.ctx.get_usage() + '\n', file=file, color=color) | 
				
			||||||
 | 
					        echo('Error: %s' % self.format_message(), file=file, color=color) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BadParameter(UsageError): | 
				
			||||||
 | 
					    """An exception that formats out a standardized error message for a | 
				
			||||||
 | 
					    bad parameter.  This is useful when thrown from a callback or type as | 
				
			||||||
 | 
					    Click will attach contextual information to it (for instance, which | 
				
			||||||
 | 
					    parameter it is). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param param: the parameter object that caused this error.  This can | 
				
			||||||
 | 
					                  be left out, and Click will attach this info itself | 
				
			||||||
 | 
					                  if possible. | 
				
			||||||
 | 
					    :param param_hint: a string that shows up as parameter name.  This | 
				
			||||||
 | 
					                       can be used as alternative to `param` in cases | 
				
			||||||
 | 
					                       where custom validation should happen.  If it is | 
				
			||||||
 | 
					                       a string it's used as such, if it's a list then | 
				
			||||||
 | 
					                       each item is quoted and separated. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, message, ctx=None, param=None, | 
				
			||||||
 | 
					                 param_hint=None): | 
				
			||||||
 | 
					        UsageError.__init__(self, message, ctx) | 
				
			||||||
 | 
					        self.param = param | 
				
			||||||
 | 
					        self.param_hint = param_hint | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_message(self): | 
				
			||||||
 | 
					        if self.param_hint is not None: | 
				
			||||||
 | 
					            param_hint = self.param_hint | 
				
			||||||
 | 
					        elif self.param is not None: | 
				
			||||||
 | 
					            param_hint = self.param.opts or [self.param.human_readable_name] | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            return 'Invalid value: %s' % self.message | 
				
			||||||
 | 
					        if isinstance(param_hint, (tuple, list)): | 
				
			||||||
 | 
					            param_hint = ' / '.join('"%s"' % x for x in param_hint) | 
				
			||||||
 | 
					        return 'Invalid value for %s: %s' % (param_hint, self.message) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MissingParameter(BadParameter): | 
				
			||||||
 | 
					    """Raised if click required an option or argument but it was not | 
				
			||||||
 | 
					    provided when invoking the script. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 4.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param param_type: a string that indicates the type of the parameter. | 
				
			||||||
 | 
					                       The default is to inherit the parameter type from | 
				
			||||||
 | 
					                       the given `param`.  Valid values are ``'parameter'``, | 
				
			||||||
 | 
					                       ``'option'`` or ``'argument'``. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, message=None, ctx=None, param=None, | 
				
			||||||
 | 
					                 param_hint=None, param_type=None): | 
				
			||||||
 | 
					        BadParameter.__init__(self, message, ctx, param, param_hint) | 
				
			||||||
 | 
					        self.param_type = param_type | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_message(self): | 
				
			||||||
 | 
					        if self.param_hint is not None: | 
				
			||||||
 | 
					            param_hint = self.param_hint | 
				
			||||||
 | 
					        elif self.param is not None: | 
				
			||||||
 | 
					            param_hint = self.param.opts or [self.param.human_readable_name] | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            param_hint = None | 
				
			||||||
 | 
					        if isinstance(param_hint, (tuple, list)): | 
				
			||||||
 | 
					            param_hint = ' / '.join('"%s"' % x for x in param_hint) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        param_type = self.param_type | 
				
			||||||
 | 
					        if param_type is None and self.param is not None: | 
				
			||||||
 | 
					            param_type = self.param.param_type_name | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        msg = self.message | 
				
			||||||
 | 
					        if self.param is not None: | 
				
			||||||
 | 
					            msg_extra = self.param.type.get_missing_message(self.param) | 
				
			||||||
 | 
					            if msg_extra: | 
				
			||||||
 | 
					                if msg: | 
				
			||||||
 | 
					                    msg += '.  ' + msg_extra | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    msg = msg_extra | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return 'Missing %s%s%s%s' % ( | 
				
			||||||
 | 
					            param_type, | 
				
			||||||
 | 
					            param_hint and ' %s' % param_hint or '', | 
				
			||||||
 | 
					            msg and '.  ' or '.', | 
				
			||||||
 | 
					            msg or '', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NoSuchOption(UsageError): | 
				
			||||||
 | 
					    """Raised if click attempted to handle an option that does not | 
				
			||||||
 | 
					    exist. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 4.0 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, option_name, message=None, possibilities=None, | 
				
			||||||
 | 
					                 ctx=None): | 
				
			||||||
 | 
					        if message is None: | 
				
			||||||
 | 
					            message = 'no such option: %s' % option_name | 
				
			||||||
 | 
					        UsageError.__init__(self, message, ctx) | 
				
			||||||
 | 
					        self.option_name = option_name | 
				
			||||||
 | 
					        self.possibilities = possibilities | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_message(self): | 
				
			||||||
 | 
					        bits = [self.message] | 
				
			||||||
 | 
					        if self.possibilities: | 
				
			||||||
 | 
					            if len(self.possibilities) == 1: | 
				
			||||||
 | 
					                bits.append('Did you mean %s?' % self.possibilities[0]) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                possibilities = sorted(self.possibilities) | 
				
			||||||
 | 
					                bits.append('(Possible options: %s)' % ', '.join(possibilities)) | 
				
			||||||
 | 
					        return '  '.join(bits) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BadOptionUsage(UsageError): | 
				
			||||||
 | 
					    """Raised if an option is generally supplied but the use of the option | 
				
			||||||
 | 
					    was incorrect.  This is for instance raised if the number of arguments | 
				
			||||||
 | 
					    for an option is not correct. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 4.0 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, message, ctx=None): | 
				
			||||||
 | 
					        UsageError.__init__(self, message, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BadArgumentUsage(UsageError): | 
				
			||||||
 | 
					    """Raised if an argument is generally supplied but the use of the argument | 
				
			||||||
 | 
					    was incorrect.  This is for instance raised if the number of values | 
				
			||||||
 | 
					    for an argument is not correct. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 6.0 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, message, ctx=None): | 
				
			||||||
 | 
					        UsageError.__init__(self, message, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FileError(ClickException): | 
				
			||||||
 | 
					    """Raised if a file cannot be opened.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, filename, hint=None): | 
				
			||||||
 | 
					        ui_filename = filename_to_ui(filename) | 
				
			||||||
 | 
					        if hint is None: | 
				
			||||||
 | 
					            hint = 'unknown error' | 
				
			||||||
 | 
					        ClickException.__init__(self, hint) | 
				
			||||||
 | 
					        self.ui_filename = ui_filename | 
				
			||||||
 | 
					        self.filename = filename | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def format_message(self): | 
				
			||||||
 | 
					        return 'Could not open file %s: %s' % (self.ui_filename, self.message) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Abort(RuntimeError): | 
				
			||||||
 | 
					    """An internal signalling exception that signals Click to abort.""" | 
				
			||||||
@ -0,0 +1,256 @@ | 
				
			|||||||
 | 
					from contextlib import contextmanager | 
				
			||||||
 | 
					from .termui import get_terminal_size | 
				
			||||||
 | 
					from .parser import split_opt | 
				
			||||||
 | 
					from ._compat import term_len | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Can force a width.  This is used by the test system | 
				
			||||||
 | 
					FORCED_WIDTH = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def measure_table(rows): | 
				
			||||||
 | 
					    widths = {} | 
				
			||||||
 | 
					    for row in rows: | 
				
			||||||
 | 
					        for idx, col in enumerate(row): | 
				
			||||||
 | 
					            widths[idx] = max(widths.get(idx, 0), term_len(col)) | 
				
			||||||
 | 
					    return tuple(y for x, y in sorted(widths.items())) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def iter_rows(rows, col_count): | 
				
			||||||
 | 
					    for row in rows: | 
				
			||||||
 | 
					        row = tuple(row) | 
				
			||||||
 | 
					        yield row + ('',) * (col_count - len(row)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def wrap_text(text, width=78, initial_indent='', subsequent_indent='', | 
				
			||||||
 | 
					              preserve_paragraphs=False): | 
				
			||||||
 | 
					    """A helper function that intelligently wraps text.  By default, it | 
				
			||||||
 | 
					    assumes that it operates on a single paragraph of text but if the | 
				
			||||||
 | 
					    `preserve_paragraphs` parameter is provided it will intelligently | 
				
			||||||
 | 
					    handle paragraphs (defined by two empty lines). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If paragraphs are handled, a paragraph can be prefixed with an empty | 
				
			||||||
 | 
					    line containing the ``\\b`` character (``\\x08``) to indicate that | 
				
			||||||
 | 
					    no rewrapping should happen in that block. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param text: the text that should be rewrapped. | 
				
			||||||
 | 
					    :param width: the maximum width for the text. | 
				
			||||||
 | 
					    :param initial_indent: the initial indent that should be placed on the | 
				
			||||||
 | 
					                           first line as a string. | 
				
			||||||
 | 
					    :param subsequent_indent: the indent string that should be placed on | 
				
			||||||
 | 
					                              each consecutive line. | 
				
			||||||
 | 
					    :param preserve_paragraphs: if this flag is set then the wrapping will | 
				
			||||||
 | 
					                                intelligently handle paragraphs. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    from ._textwrap import TextWrapper | 
				
			||||||
 | 
					    text = text.expandtabs() | 
				
			||||||
 | 
					    wrapper = TextWrapper(width, initial_indent=initial_indent, | 
				
			||||||
 | 
					                          subsequent_indent=subsequent_indent, | 
				
			||||||
 | 
					                          replace_whitespace=False) | 
				
			||||||
 | 
					    if not preserve_paragraphs: | 
				
			||||||
 | 
					        return wrapper.fill(text) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    p = [] | 
				
			||||||
 | 
					    buf = [] | 
				
			||||||
 | 
					    indent = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _flush_par(): | 
				
			||||||
 | 
					        if not buf: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        if buf[0].strip() == '\b': | 
				
			||||||
 | 
					            p.append((indent or 0, True, '\n'.join(buf[1:]))) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            p.append((indent or 0, False, ' '.join(buf))) | 
				
			||||||
 | 
					        del buf[:] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for line in text.splitlines(): | 
				
			||||||
 | 
					        if not line: | 
				
			||||||
 | 
					            _flush_par() | 
				
			||||||
 | 
					            indent = None | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            if indent is None: | 
				
			||||||
 | 
					                orig_len = term_len(line) | 
				
			||||||
 | 
					                line = line.lstrip() | 
				
			||||||
 | 
					                indent = orig_len - term_len(line) | 
				
			||||||
 | 
					            buf.append(line) | 
				
			||||||
 | 
					    _flush_par() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rv = [] | 
				
			||||||
 | 
					    for indent, raw, text in p: | 
				
			||||||
 | 
					        with wrapper.extra_indent(' ' * indent): | 
				
			||||||
 | 
					            if raw: | 
				
			||||||
 | 
					                rv.append(wrapper.indent_only(text)) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                rv.append(wrapper.fill(text)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return '\n\n'.join(rv) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HelpFormatter(object): | 
				
			||||||
 | 
					    """This class helps with formatting text-based help pages.  It's | 
				
			||||||
 | 
					    usually just needed for very special internal cases, but it's also | 
				
			||||||
 | 
					    exposed so that developers can write their own fancy outputs. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    At present, it always writes into memory. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param indent_increment: the additional increment for each level. | 
				
			||||||
 | 
					    :param width: the width for the text.  This defaults to the terminal | 
				
			||||||
 | 
					                  width clamped to a maximum of 78. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, indent_increment=2, width=None, max_width=None): | 
				
			||||||
 | 
					        self.indent_increment = indent_increment | 
				
			||||||
 | 
					        if max_width is None: | 
				
			||||||
 | 
					            max_width = 80 | 
				
			||||||
 | 
					        if width is None: | 
				
			||||||
 | 
					            width = FORCED_WIDTH | 
				
			||||||
 | 
					            if width is None: | 
				
			||||||
 | 
					                width = max(min(get_terminal_size()[0], max_width) - 2, 50) | 
				
			||||||
 | 
					        self.width = width | 
				
			||||||
 | 
					        self.current_indent = 0 | 
				
			||||||
 | 
					        self.buffer = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write(self, string): | 
				
			||||||
 | 
					        """Writes a unicode string into the internal buffer.""" | 
				
			||||||
 | 
					        self.buffer.append(string) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def indent(self): | 
				
			||||||
 | 
					        """Increases the indentation.""" | 
				
			||||||
 | 
					        self.current_indent += self.indent_increment | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dedent(self): | 
				
			||||||
 | 
					        """Decreases the indentation.""" | 
				
			||||||
 | 
					        self.current_indent -= self.indent_increment | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write_usage(self, prog, args='', prefix='Usage: '): | 
				
			||||||
 | 
					        """Writes a usage line into the buffer. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param prog: the program name. | 
				
			||||||
 | 
					        :param args: whitespace separated list of arguments. | 
				
			||||||
 | 
					        :param prefix: the prefix for the first line. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        usage_prefix = '%*s%s ' % (self.current_indent, prefix, prog) | 
				
			||||||
 | 
					        text_width = self.width - self.current_indent | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if text_width >= (term_len(usage_prefix) + 20): | 
				
			||||||
 | 
					            # The arguments will fit to the right of the prefix. | 
				
			||||||
 | 
					            indent = ' ' * term_len(usage_prefix) | 
				
			||||||
 | 
					            self.write(wrap_text(args, text_width, | 
				
			||||||
 | 
					                                 initial_indent=usage_prefix, | 
				
			||||||
 | 
					                                 subsequent_indent=indent)) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            # The prefix is too long, put the arguments on the next line. | 
				
			||||||
 | 
					            self.write(usage_prefix) | 
				
			||||||
 | 
					            self.write('\n') | 
				
			||||||
 | 
					            indent = ' ' * (max(self.current_indent, term_len(prefix)) + 4) | 
				
			||||||
 | 
					            self.write(wrap_text(args, text_width, | 
				
			||||||
 | 
					                                 initial_indent=indent, | 
				
			||||||
 | 
					                                 subsequent_indent=indent)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.write('\n') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write_heading(self, heading): | 
				
			||||||
 | 
					        """Writes a heading into the buffer.""" | 
				
			||||||
 | 
					        self.write('%*s%s:\n' % (self.current_indent, '', heading)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write_paragraph(self): | 
				
			||||||
 | 
					        """Writes a paragraph into the buffer.""" | 
				
			||||||
 | 
					        if self.buffer: | 
				
			||||||
 | 
					            self.write('\n') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write_text(self, text): | 
				
			||||||
 | 
					        """Writes re-indented text into the buffer.  This rewraps and | 
				
			||||||
 | 
					        preserves paragraphs. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        text_width = max(self.width - self.current_indent, 11) | 
				
			||||||
 | 
					        indent = ' ' * self.current_indent | 
				
			||||||
 | 
					        self.write(wrap_text(text, text_width, | 
				
			||||||
 | 
					                             initial_indent=indent, | 
				
			||||||
 | 
					                             subsequent_indent=indent, | 
				
			||||||
 | 
					                             preserve_paragraphs=True)) | 
				
			||||||
 | 
					        self.write('\n') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write_dl(self, rows, col_max=30, col_spacing=2): | 
				
			||||||
 | 
					        """Writes a definition list into the buffer.  This is how options | 
				
			||||||
 | 
					        and commands are usually formatted. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param rows: a list of two item tuples for the terms and values. | 
				
			||||||
 | 
					        :param col_max: the maximum width of the first column. | 
				
			||||||
 | 
					        :param col_spacing: the number of spaces between the first and | 
				
			||||||
 | 
					                            second column. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        rows = list(rows) | 
				
			||||||
 | 
					        widths = measure_table(rows) | 
				
			||||||
 | 
					        if len(widths) != 2: | 
				
			||||||
 | 
					            raise TypeError('Expected two columns for definition list') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        first_col = min(widths[0], col_max) + col_spacing | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for first, second in iter_rows(rows, len(widths)): | 
				
			||||||
 | 
					            self.write('%*s%s' % (self.current_indent, '', first)) | 
				
			||||||
 | 
					            if not second: | 
				
			||||||
 | 
					                self.write('\n') | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					            if term_len(first) <= first_col - col_spacing: | 
				
			||||||
 | 
					                self.write(' ' * (first_col - term_len(first))) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                self.write('\n') | 
				
			||||||
 | 
					                self.write(' ' * (first_col + self.current_indent)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            text_width = max(self.width - first_col - 2, 10) | 
				
			||||||
 | 
					            lines = iter(wrap_text(second, text_width).splitlines()) | 
				
			||||||
 | 
					            if lines: | 
				
			||||||
 | 
					                self.write(next(lines) + '\n') | 
				
			||||||
 | 
					                for line in lines: | 
				
			||||||
 | 
					                    self.write('%*s%s\n' % ( | 
				
			||||||
 | 
					                        first_col + self.current_indent, '', line)) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                self.write('\n') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @contextmanager | 
				
			||||||
 | 
					    def section(self, name): | 
				
			||||||
 | 
					        """Helpful context manager that writes a paragraph, a heading, | 
				
			||||||
 | 
					        and the indents. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param name: the section name that is written as heading. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.write_paragraph() | 
				
			||||||
 | 
					        self.write_heading(name) | 
				
			||||||
 | 
					        self.indent() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            yield | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            self.dedent() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @contextmanager | 
				
			||||||
 | 
					    def indentation(self): | 
				
			||||||
 | 
					        """A context manager that increases the indentation.""" | 
				
			||||||
 | 
					        self.indent() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            yield | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            self.dedent() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getvalue(self): | 
				
			||||||
 | 
					        """Returns the buffer contents.""" | 
				
			||||||
 | 
					        return ''.join(self.buffer) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def join_options(options): | 
				
			||||||
 | 
					    """Given a list of option strings this joins them in the most appropriate | 
				
			||||||
 | 
					    way and returns them in the form ``(formatted_string, | 
				
			||||||
 | 
					    any_prefix_is_slash)`` where the second item in the tuple is a flag that | 
				
			||||||
 | 
					    indicates if any of the option prefixes was a slash. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    rv = [] | 
				
			||||||
 | 
					    any_prefix_is_slash = False | 
				
			||||||
 | 
					    for opt in options: | 
				
			||||||
 | 
					        prefix = split_opt(opt)[0] | 
				
			||||||
 | 
					        if prefix == '/': | 
				
			||||||
 | 
					            any_prefix_is_slash = True | 
				
			||||||
 | 
					        rv.append((len(prefix), opt)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rv.sort(key=lambda x: x[0]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rv = ', '.join(x[1] for x in rv) | 
				
			||||||
 | 
					    return rv, any_prefix_is_slash | 
				
			||||||
@ -0,0 +1,48 @@ | 
				
			|||||||
 | 
					from threading import local | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_local = local() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_current_context(silent=False): | 
				
			||||||
 | 
					    """Returns the current click context.  This can be used as a way to | 
				
			||||||
 | 
					    access the current context object from anywhere.  This is a more implicit | 
				
			||||||
 | 
					    alternative to the :func:`pass_context` decorator.  This function is | 
				
			||||||
 | 
					    primarily useful for helpers such as :func:`echo` which might be | 
				
			||||||
 | 
					    interested in changing it's behavior based on the current context. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    To push the current context, :meth:`Context.scope` can be used. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 5.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param silent: is set to `True` the return value is `None` if no context | 
				
			||||||
 | 
					                   is available.  The default behavior is to raise a | 
				
			||||||
 | 
					                   :exc:`RuntimeError`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        return getattr(_local, 'stack')[-1] | 
				
			||||||
 | 
					    except (AttributeError, IndexError): | 
				
			||||||
 | 
					        if not silent: | 
				
			||||||
 | 
					            raise RuntimeError('There is no active click context.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def push_context(ctx): | 
				
			||||||
 | 
					    """Pushes a new context to the current stack.""" | 
				
			||||||
 | 
					    _local.__dict__.setdefault('stack', []).append(ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pop_context(): | 
				
			||||||
 | 
					    """Removes the top level from the stack.""" | 
				
			||||||
 | 
					    _local.stack.pop() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def resolve_color_default(color=None): | 
				
			||||||
 | 
					    """"Internal helper to get the default value of the color flag.  If a | 
				
			||||||
 | 
					    value is passed it's returned unchanged, otherwise it's looked up from | 
				
			||||||
 | 
					    the current context. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if color is not None: | 
				
			||||||
 | 
					        return color | 
				
			||||||
 | 
					    ctx = get_current_context(silent=True) | 
				
			||||||
 | 
					    if ctx is not None: | 
				
			||||||
 | 
					        return ctx.color | 
				
			||||||
@ -0,0 +1,426 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    click.parser | 
				
			||||||
 | 
					    ~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This module started out as largely a copy paste from the stdlib's | 
				
			||||||
 | 
					    optparse module with the features removed that we do not need from | 
				
			||||||
 | 
					    optparse because we implement them in Click on a higher level (for | 
				
			||||||
 | 
					    instance type handling, help formatting and a lot more). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The plan is to remove more and more from here over time. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The reason this is a different module and not optparse from the stdlib | 
				
			||||||
 | 
					    is that there are differences in 2.x and 3.x about the error messages | 
				
			||||||
 | 
					    generated and optparse in the stdlib uses gettext for no good reason | 
				
			||||||
 | 
					    and might cause us issues. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					import re | 
				
			||||||
 | 
					from collections import deque | 
				
			||||||
 | 
					from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \ | 
				
			||||||
 | 
					     BadArgumentUsage | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _unpack_args(args, nargs_spec): | 
				
			||||||
 | 
					    """Given an iterable of arguments and an iterable of nargs specifications, | 
				
			||||||
 | 
					    it returns a tuple with all the unpacked arguments at the first index | 
				
			||||||
 | 
					    and all remaining arguments as the second. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The nargs specification is the number of arguments that should be consumed | 
				
			||||||
 | 
					    or `-1` to indicate that this position should eat up all the remainders. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Missing items are filled with `None`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    args = deque(args) | 
				
			||||||
 | 
					    nargs_spec = deque(nargs_spec) | 
				
			||||||
 | 
					    rv = [] | 
				
			||||||
 | 
					    spos = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _fetch(c): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            if spos is None: | 
				
			||||||
 | 
					                return c.popleft() | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                return c.pop() | 
				
			||||||
 | 
					        except IndexError: | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while nargs_spec: | 
				
			||||||
 | 
					        nargs = _fetch(nargs_spec) | 
				
			||||||
 | 
					        if nargs == 1: | 
				
			||||||
 | 
					            rv.append(_fetch(args)) | 
				
			||||||
 | 
					        elif nargs > 1: | 
				
			||||||
 | 
					            x = [_fetch(args) for _ in range(nargs)] | 
				
			||||||
 | 
					            # If we're reversed, we're pulling in the arguments in reverse, | 
				
			||||||
 | 
					            # so we need to turn them around. | 
				
			||||||
 | 
					            if spos is not None: | 
				
			||||||
 | 
					                x.reverse() | 
				
			||||||
 | 
					            rv.append(tuple(x)) | 
				
			||||||
 | 
					        elif nargs < 0: | 
				
			||||||
 | 
					            if spos is not None: | 
				
			||||||
 | 
					                raise TypeError('Cannot have two nargs < 0') | 
				
			||||||
 | 
					            spos = len(rv) | 
				
			||||||
 | 
					            rv.append(None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # spos is the position of the wildcard (star).  If it's not `None`, | 
				
			||||||
 | 
					    # we fill it with the remainder. | 
				
			||||||
 | 
					    if spos is not None: | 
				
			||||||
 | 
					        rv[spos] = tuple(args) | 
				
			||||||
 | 
					        args = [] | 
				
			||||||
 | 
					        rv[spos + 1:] = reversed(rv[spos + 1:]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return tuple(rv), list(args) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _error_opt_args(nargs, opt): | 
				
			||||||
 | 
					    if nargs == 1: | 
				
			||||||
 | 
					        raise BadOptionUsage('%s option requires an argument' % opt) | 
				
			||||||
 | 
					    raise BadOptionUsage('%s option requires %d arguments' % (opt, nargs)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def split_opt(opt): | 
				
			||||||
 | 
					    first = opt[:1] | 
				
			||||||
 | 
					    if first.isalnum(): | 
				
			||||||
 | 
					        return '', opt | 
				
			||||||
 | 
					    if opt[1:2] == first: | 
				
			||||||
 | 
					        return opt[:2], opt[2:] | 
				
			||||||
 | 
					    return first, opt[1:] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def normalize_opt(opt, ctx): | 
				
			||||||
 | 
					    if ctx is None or ctx.token_normalize_func is None: | 
				
			||||||
 | 
					        return opt | 
				
			||||||
 | 
					    prefix, opt = split_opt(opt) | 
				
			||||||
 | 
					    return prefix + ctx.token_normalize_func(opt) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def split_arg_string(string): | 
				
			||||||
 | 
					    """Given an argument string this attempts to split it into small parts.""" | 
				
			||||||
 | 
					    rv = [] | 
				
			||||||
 | 
					    for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'" | 
				
			||||||
 | 
					                             r'|"([^"\\]*(?:\\.[^"\\]*)*)"' | 
				
			||||||
 | 
					                             r'|\S+)\s*', string, re.S): | 
				
			||||||
 | 
					        arg = match.group().strip() | 
				
			||||||
 | 
					        if arg[:1] == arg[-1:] and arg[:1] in '"\'': | 
				
			||||||
 | 
					            arg = arg[1:-1].encode('ascii', 'backslashreplace') \ | 
				
			||||||
 | 
					                .decode('unicode-escape') | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            arg = type(string)(arg) | 
				
			||||||
 | 
					        except UnicodeError: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					        rv.append(arg) | 
				
			||||||
 | 
					    return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Option(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None): | 
				
			||||||
 | 
					        self._short_opts = [] | 
				
			||||||
 | 
					        self._long_opts = [] | 
				
			||||||
 | 
					        self.prefixes = set() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for opt in opts: | 
				
			||||||
 | 
					            prefix, value = split_opt(opt) | 
				
			||||||
 | 
					            if not prefix: | 
				
			||||||
 | 
					                raise ValueError('Invalid start character for option (%s)' | 
				
			||||||
 | 
					                                 % opt) | 
				
			||||||
 | 
					            self.prefixes.add(prefix[0]) | 
				
			||||||
 | 
					            if len(prefix) == 1 and len(value) == 1: | 
				
			||||||
 | 
					                self._short_opts.append(opt) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                self._long_opts.append(opt) | 
				
			||||||
 | 
					                self.prefixes.add(prefix) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if action is None: | 
				
			||||||
 | 
					            action = 'store' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.dest = dest | 
				
			||||||
 | 
					        self.action = action | 
				
			||||||
 | 
					        self.nargs = nargs | 
				
			||||||
 | 
					        self.const = const | 
				
			||||||
 | 
					        self.obj = obj | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def takes_value(self): | 
				
			||||||
 | 
					        return self.action in ('store', 'append') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def process(self, value, state): | 
				
			||||||
 | 
					        if self.action == 'store': | 
				
			||||||
 | 
					            state.opts[self.dest] = value | 
				
			||||||
 | 
					        elif self.action == 'store_const': | 
				
			||||||
 | 
					            state.opts[self.dest] = self.const | 
				
			||||||
 | 
					        elif self.action == 'append': | 
				
			||||||
 | 
					            state.opts.setdefault(self.dest, []).append(value) | 
				
			||||||
 | 
					        elif self.action == 'append_const': | 
				
			||||||
 | 
					            state.opts.setdefault(self.dest, []).append(self.const) | 
				
			||||||
 | 
					        elif self.action == 'count': | 
				
			||||||
 | 
					            state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            raise ValueError('unknown action %r' % self.action) | 
				
			||||||
 | 
					        state.order.append(self.obj) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Argument(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, dest, nargs=1, obj=None): | 
				
			||||||
 | 
					        self.dest = dest | 
				
			||||||
 | 
					        self.nargs = nargs | 
				
			||||||
 | 
					        self.obj = obj | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def process(self, value, state): | 
				
			||||||
 | 
					        if self.nargs > 1: | 
				
			||||||
 | 
					            holes = sum(1 for x in value if x is None) | 
				
			||||||
 | 
					            if holes == len(value): | 
				
			||||||
 | 
					                value = None | 
				
			||||||
 | 
					            elif holes != 0: | 
				
			||||||
 | 
					                raise BadArgumentUsage('argument %s takes %d values' | 
				
			||||||
 | 
					                                       % (self.dest, self.nargs)) | 
				
			||||||
 | 
					        state.opts[self.dest] = value | 
				
			||||||
 | 
					        state.order.append(self.obj) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ParsingState(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, rargs): | 
				
			||||||
 | 
					        self.opts = {} | 
				
			||||||
 | 
					        self.largs = [] | 
				
			||||||
 | 
					        self.rargs = rargs | 
				
			||||||
 | 
					        self.order = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OptionParser(object): | 
				
			||||||
 | 
					    """The option parser is an internal class that is ultimately used to | 
				
			||||||
 | 
					    parse options and arguments.  It's modelled after optparse and brings | 
				
			||||||
 | 
					    a similar but vastly simplified API.  It should generally not be used | 
				
			||||||
 | 
					    directly as the high level Click classes wrap it for you. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    It's not nearly as extensible as optparse or argparse as it does not | 
				
			||||||
 | 
					    implement features that are implemented on a higher level (such as | 
				
			||||||
 | 
					    types or defaults). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param ctx: optionally the :class:`~click.Context` where this parser | 
				
			||||||
 | 
					                should go with. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, ctx=None): | 
				
			||||||
 | 
					        #: The :class:`~click.Context` for this parser.  This might be | 
				
			||||||
 | 
					        #: `None` for some advanced use cases. | 
				
			||||||
 | 
					        self.ctx = ctx | 
				
			||||||
 | 
					        #: This controls how the parser deals with interspersed arguments. | 
				
			||||||
 | 
					        #: If this is set to `False`, the parser will stop on the first | 
				
			||||||
 | 
					        #: non-option.  Click uses this to implement nested subcommands | 
				
			||||||
 | 
					        #: safely. | 
				
			||||||
 | 
					        self.allow_interspersed_args = True | 
				
			||||||
 | 
					        #: This tells the parser how to deal with unknown options.  By | 
				
			||||||
 | 
					        #: default it will error out (which is sensible), but there is a | 
				
			||||||
 | 
					        #: second mode where it will ignore it and continue processing | 
				
			||||||
 | 
					        #: after shifting all the unknown options into the resulting args. | 
				
			||||||
 | 
					        self.ignore_unknown_options = False | 
				
			||||||
 | 
					        if ctx is not None: | 
				
			||||||
 | 
					            self.allow_interspersed_args = ctx.allow_interspersed_args | 
				
			||||||
 | 
					            self.ignore_unknown_options = ctx.ignore_unknown_options | 
				
			||||||
 | 
					        self._short_opt = {} | 
				
			||||||
 | 
					        self._long_opt = {} | 
				
			||||||
 | 
					        self._opt_prefixes = set(['-', '--']) | 
				
			||||||
 | 
					        self._args = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_option(self, opts, dest, action=None, nargs=1, const=None, | 
				
			||||||
 | 
					                   obj=None): | 
				
			||||||
 | 
					        """Adds a new option named `dest` to the parser.  The destination | 
				
			||||||
 | 
					        is not inferred (unlike with optparse) and needs to be explicitly | 
				
			||||||
 | 
					        provided.  Action can be any of ``store``, ``store_const``, | 
				
			||||||
 | 
					        ``append``, ``appnd_const`` or ``count``. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The `obj` can be used to identify the option in the order list | 
				
			||||||
 | 
					        that is returned from the parser. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if obj is None: | 
				
			||||||
 | 
					            obj = dest | 
				
			||||||
 | 
					        opts = [normalize_opt(opt, self.ctx) for opt in opts] | 
				
			||||||
 | 
					        option = Option(opts, dest, action=action, nargs=nargs, | 
				
			||||||
 | 
					                        const=const, obj=obj) | 
				
			||||||
 | 
					        self._opt_prefixes.update(option.prefixes) | 
				
			||||||
 | 
					        for opt in option._short_opts: | 
				
			||||||
 | 
					            self._short_opt[opt] = option | 
				
			||||||
 | 
					        for opt in option._long_opts: | 
				
			||||||
 | 
					            self._long_opt[opt] = option | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_argument(self, dest, nargs=1, obj=None): | 
				
			||||||
 | 
					        """Adds a positional argument named `dest` to the parser. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The `obj` can be used to identify the option in the order list | 
				
			||||||
 | 
					        that is returned from the parser. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if obj is None: | 
				
			||||||
 | 
					            obj = dest | 
				
			||||||
 | 
					        self._args.append(Argument(dest=dest, nargs=nargs, obj=obj)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_args(self, args): | 
				
			||||||
 | 
					        """Parses positional arguments and returns ``(values, args, order)`` | 
				
			||||||
 | 
					        for the parsed options and arguments as well as the leftover | 
				
			||||||
 | 
					        arguments if there are any.  The order is a list of objects as they | 
				
			||||||
 | 
					        appear on the command line.  If arguments appear multiple times they | 
				
			||||||
 | 
					        will be memorized multiple times as well. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        state = ParsingState(args) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self._process_args_for_options(state) | 
				
			||||||
 | 
					            self._process_args_for_args(state) | 
				
			||||||
 | 
					        except UsageError: | 
				
			||||||
 | 
					            if self.ctx is None or not self.ctx.resilient_parsing: | 
				
			||||||
 | 
					                raise | 
				
			||||||
 | 
					        return state.opts, state.largs, state.order | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _process_args_for_args(self, state): | 
				
			||||||
 | 
					        pargs, args = _unpack_args(state.largs + state.rargs, | 
				
			||||||
 | 
					                                   [x.nargs for x in self._args]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for idx, arg in enumerate(self._args): | 
				
			||||||
 | 
					            arg.process(pargs[idx], state) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        state.largs = args | 
				
			||||||
 | 
					        state.rargs = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _process_args_for_options(self, state): | 
				
			||||||
 | 
					        while state.rargs: | 
				
			||||||
 | 
					            arg = state.rargs.pop(0) | 
				
			||||||
 | 
					            arglen = len(arg) | 
				
			||||||
 | 
					            # Double dashes always handled explicitly regardless of what | 
				
			||||||
 | 
					            # prefixes are valid. | 
				
			||||||
 | 
					            if arg == '--': | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					            elif arg[:1] in self._opt_prefixes and arglen > 1: | 
				
			||||||
 | 
					                self._process_opts(arg, state) | 
				
			||||||
 | 
					            elif self.allow_interspersed_args: | 
				
			||||||
 | 
					                state.largs.append(arg) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                state.rargs.insert(0, arg) | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Say this is the original argument list: | 
				
			||||||
 | 
					        # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] | 
				
			||||||
 | 
					        #                            ^ | 
				
			||||||
 | 
					        # (we are about to process arg(i)). | 
				
			||||||
 | 
					        # | 
				
			||||||
 | 
					        # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of | 
				
			||||||
 | 
					        # [arg0, ..., arg(i-1)] (any options and their arguments will have | 
				
			||||||
 | 
					        # been removed from largs). | 
				
			||||||
 | 
					        # | 
				
			||||||
 | 
					        # The while loop will usually consume 1 or more arguments per pass. | 
				
			||||||
 | 
					        # If it consumes 1 (eg. arg is an option that takes no arguments), | 
				
			||||||
 | 
					        # then after _process_arg() is done the situation is: | 
				
			||||||
 | 
					        # | 
				
			||||||
 | 
					        #   largs = subset of [arg0, ..., arg(i)] | 
				
			||||||
 | 
					        #   rargs = [arg(i+1), ..., arg(N-1)] | 
				
			||||||
 | 
					        # | 
				
			||||||
 | 
					        # If allow_interspersed_args is false, largs will always be | 
				
			||||||
 | 
					        # *empty* -- still a subset of [arg0, ..., arg(i-1)], but | 
				
			||||||
 | 
					        # not a very interesting subset! | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _match_long_opt(self, opt, explicit_value, state): | 
				
			||||||
 | 
					        if opt not in self._long_opt: | 
				
			||||||
 | 
					            possibilities = [word for word in self._long_opt | 
				
			||||||
 | 
					                             if word.startswith(opt)] | 
				
			||||||
 | 
					            raise NoSuchOption(opt, possibilities=possibilities) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        option = self._long_opt[opt] | 
				
			||||||
 | 
					        if option.takes_value: | 
				
			||||||
 | 
					            # At this point it's safe to modify rargs by injecting the | 
				
			||||||
 | 
					            # explicit value, because no exception is raised in this | 
				
			||||||
 | 
					            # branch.  This means that the inserted value will be fully | 
				
			||||||
 | 
					            # consumed. | 
				
			||||||
 | 
					            if explicit_value is not None: | 
				
			||||||
 | 
					                state.rargs.insert(0, explicit_value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            nargs = option.nargs | 
				
			||||||
 | 
					            if len(state.rargs) < nargs: | 
				
			||||||
 | 
					                _error_opt_args(nargs, opt) | 
				
			||||||
 | 
					            elif nargs == 1: | 
				
			||||||
 | 
					                value = state.rargs.pop(0) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                value = tuple(state.rargs[:nargs]) | 
				
			||||||
 | 
					                del state.rargs[:nargs] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        elif explicit_value is not None: | 
				
			||||||
 | 
					            raise BadOptionUsage('%s option does not take a value' % opt) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            value = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        option.process(value, state) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _match_short_opt(self, arg, state): | 
				
			||||||
 | 
					        stop = False | 
				
			||||||
 | 
					        i = 1 | 
				
			||||||
 | 
					        prefix = arg[0] | 
				
			||||||
 | 
					        unknown_options = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for ch in arg[1:]: | 
				
			||||||
 | 
					            opt = normalize_opt(prefix + ch, self.ctx) | 
				
			||||||
 | 
					            option = self._short_opt.get(opt) | 
				
			||||||
 | 
					            i += 1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if not option: | 
				
			||||||
 | 
					                if self.ignore_unknown_options: | 
				
			||||||
 | 
					                    unknown_options.append(ch) | 
				
			||||||
 | 
					                    continue | 
				
			||||||
 | 
					                raise NoSuchOption(opt) | 
				
			||||||
 | 
					            if option.takes_value: | 
				
			||||||
 | 
					                # Any characters left in arg?  Pretend they're the | 
				
			||||||
 | 
					                # next arg, and stop consuming characters of arg. | 
				
			||||||
 | 
					                if i < len(arg): | 
				
			||||||
 | 
					                    state.rargs.insert(0, arg[i:]) | 
				
			||||||
 | 
					                    stop = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                nargs = option.nargs | 
				
			||||||
 | 
					                if len(state.rargs) < nargs: | 
				
			||||||
 | 
					                    _error_opt_args(nargs, opt) | 
				
			||||||
 | 
					                elif nargs == 1: | 
				
			||||||
 | 
					                    value = state.rargs.pop(0) | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    value = tuple(state.rargs[:nargs]) | 
				
			||||||
 | 
					                    del state.rargs[:nargs] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                value = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            option.process(value, state) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if stop: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # If we got any unknown options we re-combinate the string of the | 
				
			||||||
 | 
					        # remaining options and re-attach the prefix, then report that | 
				
			||||||
 | 
					        # to the state as new larg.  This way there is basic combinatorics | 
				
			||||||
 | 
					        # that can be achieved while still ignoring unknown arguments. | 
				
			||||||
 | 
					        if self.ignore_unknown_options and unknown_options: | 
				
			||||||
 | 
					            state.largs.append(prefix + ''.join(unknown_options)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _process_opts(self, arg, state): | 
				
			||||||
 | 
					        explicit_value = None | 
				
			||||||
 | 
					        # Long option handling happens in two parts.  The first part is | 
				
			||||||
 | 
					        # supporting explicitly attached values.  In any case, we will try | 
				
			||||||
 | 
					        # to long match the option first. | 
				
			||||||
 | 
					        if '=' in arg: | 
				
			||||||
 | 
					            long_opt, explicit_value = arg.split('=', 1) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            long_opt = arg | 
				
			||||||
 | 
					        norm_long_opt = normalize_opt(long_opt, self.ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # At this point we will match the (assumed) long option through | 
				
			||||||
 | 
					        # the long option matching code.  Note that this allows options | 
				
			||||||
 | 
					        # like "-foo" to be matched as long options. | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self._match_long_opt(norm_long_opt, explicit_value, state) | 
				
			||||||
 | 
					        except NoSuchOption: | 
				
			||||||
 | 
					            # At this point the long option matching failed, and we need | 
				
			||||||
 | 
					            # to try with short options.  However there is a special rule | 
				
			||||||
 | 
					            # which says, that if we have a two character options prefix | 
				
			||||||
 | 
					            # (applies to "--foo" for instance), we do not dispatch to the | 
				
			||||||
 | 
					            # short option code and will instead raise the no option | 
				
			||||||
 | 
					            # error. | 
				
			||||||
 | 
					            if arg[:2] not in self._opt_prefixes: | 
				
			||||||
 | 
					                return self._match_short_opt(arg, state) | 
				
			||||||
 | 
					            if not self.ignore_unknown_options: | 
				
			||||||
 | 
					                raise | 
				
			||||||
 | 
					            state.largs.append(arg) | 
				
			||||||
@ -0,0 +1,539 @@ | 
				
			|||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import struct | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ._compat import raw_input, text_type, string_types, \ | 
				
			||||||
 | 
					     isatty, strip_ansi, get_winterm_size, DEFAULT_COLUMNS, WIN | 
				
			||||||
 | 
					from .utils import echo | 
				
			||||||
 | 
					from .exceptions import Abort, UsageError | 
				
			||||||
 | 
					from .types import convert_type | 
				
			||||||
 | 
					from .globals import resolve_color_default | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The prompt functions to use.  The doc tools currently override these | 
				
			||||||
 | 
					# functions to customize how they work. | 
				
			||||||
 | 
					visible_prompt_func = raw_input | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_ansi_colors = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', | 
				
			||||||
 | 
					                'cyan', 'white', 'reset') | 
				
			||||||
 | 
					_ansi_reset_all = '\033[0m' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def hidden_prompt_func(prompt): | 
				
			||||||
 | 
					    import getpass | 
				
			||||||
 | 
					    return getpass.getpass(prompt) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _build_prompt(text, suffix, show_default=False, default=None): | 
				
			||||||
 | 
					    prompt = text | 
				
			||||||
 | 
					    if default is not None and show_default: | 
				
			||||||
 | 
					        prompt = '%s [%s]' % (prompt, default) | 
				
			||||||
 | 
					    return prompt + suffix | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def prompt(text, default=None, hide_input=False, | 
				
			||||||
 | 
					           confirmation_prompt=False, type=None, | 
				
			||||||
 | 
					           value_proc=None, prompt_suffix=': ', | 
				
			||||||
 | 
					           show_default=True, err=False): | 
				
			||||||
 | 
					    """Prompts a user for input.  This is a convenience function that can | 
				
			||||||
 | 
					    be used to prompt a user for input later. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If the user aborts the input by sending a interrupt signal, this | 
				
			||||||
 | 
					    function will catch it and raise a :exc:`Abort` exception. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 6.0 | 
				
			||||||
 | 
					       Added unicode support for cmd.exe on Windows. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 4.0 | 
				
			||||||
 | 
					       Added the `err` parameter. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param text: the text to show for the prompt. | 
				
			||||||
 | 
					    :param default: the default value to use if no input happens.  If this | 
				
			||||||
 | 
					                    is not given it will prompt until it's aborted. | 
				
			||||||
 | 
					    :param hide_input: if this is set to true then the input value will | 
				
			||||||
 | 
					                       be hidden. | 
				
			||||||
 | 
					    :param confirmation_prompt: asks for confirmation for the value. | 
				
			||||||
 | 
					    :param type: the type to use to check the value against. | 
				
			||||||
 | 
					    :param value_proc: if this parameter is provided it's a function that | 
				
			||||||
 | 
					                       is invoked instead of the type conversion to | 
				
			||||||
 | 
					                       convert a value. | 
				
			||||||
 | 
					    :param prompt_suffix: a suffix that should be added to the prompt. | 
				
			||||||
 | 
					    :param show_default: shows or hides the default value in the prompt. | 
				
			||||||
 | 
					    :param err: if set to true the file defaults to ``stderr`` instead of | 
				
			||||||
 | 
					                ``stdout``, the same as with echo. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    result = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def prompt_func(text): | 
				
			||||||
 | 
					        f = hide_input and hidden_prompt_func or visible_prompt_func | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            # Write the prompt separately so that we get nice | 
				
			||||||
 | 
					            # coloring through colorama on Windows | 
				
			||||||
 | 
					            echo(text, nl=False, err=err) | 
				
			||||||
 | 
					            return f('') | 
				
			||||||
 | 
					        except (KeyboardInterrupt, EOFError): | 
				
			||||||
 | 
					            # getpass doesn't print a newline if the user aborts input with ^C. | 
				
			||||||
 | 
					            # Allegedly this behavior is inherited from getpass(3). | 
				
			||||||
 | 
					            # A doc bug has been filed at https://bugs.python.org/issue24711 | 
				
			||||||
 | 
					            if hide_input: | 
				
			||||||
 | 
					                echo(None, err=err) | 
				
			||||||
 | 
					            raise Abort() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if value_proc is None: | 
				
			||||||
 | 
					        value_proc = convert_type(type, default) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    prompt = _build_prompt(text, prompt_suffix, show_default, default) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while 1: | 
				
			||||||
 | 
					        while 1: | 
				
			||||||
 | 
					            value = prompt_func(prompt) | 
				
			||||||
 | 
					            if value: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					            # If a default is set and used, then the confirmation | 
				
			||||||
 | 
					            # prompt is always skipped because that's the only thing | 
				
			||||||
 | 
					            # that really makes sense. | 
				
			||||||
 | 
					            elif default is not None: | 
				
			||||||
 | 
					                return default | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            result = value_proc(value) | 
				
			||||||
 | 
					        except UsageError as e: | 
				
			||||||
 | 
					            echo('Error: %s' % e.message, err=err) | 
				
			||||||
 | 
					            continue | 
				
			||||||
 | 
					        if not confirmation_prompt: | 
				
			||||||
 | 
					            return result | 
				
			||||||
 | 
					        while 1: | 
				
			||||||
 | 
					            value2 = prompt_func('Repeat for confirmation: ') | 
				
			||||||
 | 
					            if value2: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					        if value == value2: | 
				
			||||||
 | 
					            return result | 
				
			||||||
 | 
					        echo('Error: the two entered values do not match', err=err) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def confirm(text, default=False, abort=False, prompt_suffix=': ', | 
				
			||||||
 | 
					            show_default=True, err=False): | 
				
			||||||
 | 
					    """Prompts for confirmation (yes/no question). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If the user aborts the input by sending a interrupt signal this | 
				
			||||||
 | 
					    function will catch it and raise a :exc:`Abort` exception. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 4.0 | 
				
			||||||
 | 
					       Added the `err` parameter. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param text: the question to ask. | 
				
			||||||
 | 
					    :param default: the default for the prompt. | 
				
			||||||
 | 
					    :param abort: if this is set to `True` a negative answer aborts the | 
				
			||||||
 | 
					                  exception by raising :exc:`Abort`. | 
				
			||||||
 | 
					    :param prompt_suffix: a suffix that should be added to the prompt. | 
				
			||||||
 | 
					    :param show_default: shows or hides the default value in the prompt. | 
				
			||||||
 | 
					    :param err: if set to true the file defaults to ``stderr`` instead of | 
				
			||||||
 | 
					                ``stdout``, the same as with echo. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    prompt = _build_prompt(text, prompt_suffix, show_default, | 
				
			||||||
 | 
					                           default and 'Y/n' or 'y/N') | 
				
			||||||
 | 
					    while 1: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            # Write the prompt separately so that we get nice | 
				
			||||||
 | 
					            # coloring through colorama on Windows | 
				
			||||||
 | 
					            echo(prompt, nl=False, err=err) | 
				
			||||||
 | 
					            value = visible_prompt_func('').lower().strip() | 
				
			||||||
 | 
					        except (KeyboardInterrupt, EOFError): | 
				
			||||||
 | 
					            raise Abort() | 
				
			||||||
 | 
					        if value in ('y', 'yes'): | 
				
			||||||
 | 
					            rv = True | 
				
			||||||
 | 
					        elif value in ('n', 'no'): | 
				
			||||||
 | 
					            rv = False | 
				
			||||||
 | 
					        elif value == '': | 
				
			||||||
 | 
					            rv = default | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            echo('Error: invalid input', err=err) | 
				
			||||||
 | 
					            continue | 
				
			||||||
 | 
					        break | 
				
			||||||
 | 
					    if abort and not rv: | 
				
			||||||
 | 
					        raise Abort() | 
				
			||||||
 | 
					    return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_terminal_size(): | 
				
			||||||
 | 
					    """Returns the current size of the terminal as tuple in the form | 
				
			||||||
 | 
					    ``(width, height)`` in columns and rows. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    # If shutil has get_terminal_size() (Python 3.3 and later) use that | 
				
			||||||
 | 
					    if sys.version_info >= (3, 3): | 
				
			||||||
 | 
					        import shutil | 
				
			||||||
 | 
					        shutil_get_terminal_size = getattr(shutil, 'get_terminal_size', None) | 
				
			||||||
 | 
					        if shutil_get_terminal_size: | 
				
			||||||
 | 
					            sz = shutil_get_terminal_size() | 
				
			||||||
 | 
					            return sz.columns, sz.lines | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if get_winterm_size is not None: | 
				
			||||||
 | 
					        return get_winterm_size() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def ioctl_gwinsz(fd): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            import fcntl | 
				
			||||||
 | 
					            import termios | 
				
			||||||
 | 
					            cr = struct.unpack( | 
				
			||||||
 | 
					                'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        return cr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cr = ioctl_gwinsz(0) or ioctl_gwinsz(1) or ioctl_gwinsz(2) | 
				
			||||||
 | 
					    if not cr: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            fd = os.open(os.ctermid(), os.O_RDONLY) | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                cr = ioctl_gwinsz(fd) | 
				
			||||||
 | 
					            finally: | 
				
			||||||
 | 
					                os.close(fd) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					    if not cr or not cr[0] or not cr[1]: | 
				
			||||||
 | 
					        cr = (os.environ.get('LINES', 25), | 
				
			||||||
 | 
					              os.environ.get('COLUMNS', DEFAULT_COLUMNS)) | 
				
			||||||
 | 
					    return int(cr[1]), int(cr[0]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def echo_via_pager(text, color=None): | 
				
			||||||
 | 
					    """This function takes a text and shows it via an environment specific | 
				
			||||||
 | 
					    pager on stdout. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 3.0 | 
				
			||||||
 | 
					       Added the `color` flag. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param text: the text to page. | 
				
			||||||
 | 
					    :param color: controls if the pager supports ANSI colors or not.  The | 
				
			||||||
 | 
					                  default is autodetection. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    color = resolve_color_default(color) | 
				
			||||||
 | 
					    if not isinstance(text, string_types): | 
				
			||||||
 | 
					        text = text_type(text) | 
				
			||||||
 | 
					    from ._termui_impl import pager | 
				
			||||||
 | 
					    return pager(text + '\n', color) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def progressbar(iterable=None, length=None, label=None, show_eta=True, | 
				
			||||||
 | 
					                show_percent=None, show_pos=False, | 
				
			||||||
 | 
					                item_show_func=None, fill_char='#', empty_char='-', | 
				
			||||||
 | 
					                bar_template='%(label)s  [%(bar)s]  %(info)s', | 
				
			||||||
 | 
					                info_sep='  ', width=36, file=None, color=None): | 
				
			||||||
 | 
					    """This function creates an iterable context manager that can be used | 
				
			||||||
 | 
					    to iterate over something while showing a progress bar.  It will | 
				
			||||||
 | 
					    either iterate over the `iterable` or `length` items (that are counted | 
				
			||||||
 | 
					    up).  While iteration happens, this function will print a rendered | 
				
			||||||
 | 
					    progress bar to the given `file` (defaults to stdout) and will attempt | 
				
			||||||
 | 
					    to calculate remaining time and more.  By default, this progress bar | 
				
			||||||
 | 
					    will not be rendered if the file is not a terminal. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The context manager creates the progress bar.  When the context | 
				
			||||||
 | 
					    manager is entered the progress bar is already displayed.  With every | 
				
			||||||
 | 
					    iteration over the progress bar, the iterable passed to the bar is | 
				
			||||||
 | 
					    advanced and the bar is updated.  When the context manager exits, | 
				
			||||||
 | 
					    a newline is printed and the progress bar is finalized on screen. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    No printing must happen or the progress bar will be unintentionally | 
				
			||||||
 | 
					    destroyed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Example usage:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with progressbar(items) as bar: | 
				
			||||||
 | 
					            for item in bar: | 
				
			||||||
 | 
					                do_something_with(item) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Alternatively, if no iterable is specified, one can manually update the | 
				
			||||||
 | 
					    progress bar through the `update()` method instead of directly | 
				
			||||||
 | 
					    iterating over the progress bar.  The update method accepts the number | 
				
			||||||
 | 
					    of steps to increment the bar with:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with progressbar(length=chunks.total_bytes) as bar: | 
				
			||||||
 | 
					            for chunk in chunks: | 
				
			||||||
 | 
					                process_chunk(chunk) | 
				
			||||||
 | 
					                bar.update(chunks.bytes) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 4.0 | 
				
			||||||
 | 
					       Added the `color` parameter.  Added a `update` method to the | 
				
			||||||
 | 
					       progressbar object. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param iterable: an iterable to iterate over.  If not provided the length | 
				
			||||||
 | 
					                     is required. | 
				
			||||||
 | 
					    :param length: the number of items to iterate over.  By default the | 
				
			||||||
 | 
					                   progressbar will attempt to ask the iterator about its | 
				
			||||||
 | 
					                   length, which might or might not work.  If an iterable is | 
				
			||||||
 | 
					                   also provided this parameter can be used to override the | 
				
			||||||
 | 
					                   length.  If an iterable is not provided the progress bar | 
				
			||||||
 | 
					                   will iterate over a range of that length. | 
				
			||||||
 | 
					    :param label: the label to show next to the progress bar. | 
				
			||||||
 | 
					    :param show_eta: enables or disables the estimated time display.  This is | 
				
			||||||
 | 
					                     automatically disabled if the length cannot be | 
				
			||||||
 | 
					                     determined. | 
				
			||||||
 | 
					    :param show_percent: enables or disables the percentage display.  The | 
				
			||||||
 | 
					                         default is `True` if the iterable has a length or | 
				
			||||||
 | 
					                         `False` if not. | 
				
			||||||
 | 
					    :param show_pos: enables or disables the absolute position display.  The | 
				
			||||||
 | 
					                     default is `False`. | 
				
			||||||
 | 
					    :param item_show_func: a function called with the current item which | 
				
			||||||
 | 
					                           can return a string to show the current item | 
				
			||||||
 | 
					                           next to the progress bar.  Note that the current | 
				
			||||||
 | 
					                           item can be `None`! | 
				
			||||||
 | 
					    :param fill_char: the character to use to show the filled part of the | 
				
			||||||
 | 
					                      progress bar. | 
				
			||||||
 | 
					    :param empty_char: the character to use to show the non-filled part of | 
				
			||||||
 | 
					                       the progress bar. | 
				
			||||||
 | 
					    :param bar_template: the format string to use as template for the bar. | 
				
			||||||
 | 
					                         The parameters in it are ``label`` for the label, | 
				
			||||||
 | 
					                         ``bar`` for the progress bar and ``info`` for the | 
				
			||||||
 | 
					                         info section. | 
				
			||||||
 | 
					    :param info_sep: the separator between multiple info items (eta etc.) | 
				
			||||||
 | 
					    :param width: the width of the progress bar in characters, 0 means full | 
				
			||||||
 | 
					                  terminal width | 
				
			||||||
 | 
					    :param file: the file to write to.  If this is not a terminal then | 
				
			||||||
 | 
					                 only the label is printed. | 
				
			||||||
 | 
					    :param color: controls if the terminal supports ANSI colors or not.  The | 
				
			||||||
 | 
					                  default is autodetection.  This is only needed if ANSI | 
				
			||||||
 | 
					                  codes are included anywhere in the progress bar output | 
				
			||||||
 | 
					                  which is not the case by default. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    from ._termui_impl import ProgressBar | 
				
			||||||
 | 
					    color = resolve_color_default(color) | 
				
			||||||
 | 
					    return ProgressBar(iterable=iterable, length=length, show_eta=show_eta, | 
				
			||||||
 | 
					                       show_percent=show_percent, show_pos=show_pos, | 
				
			||||||
 | 
					                       item_show_func=item_show_func, fill_char=fill_char, | 
				
			||||||
 | 
					                       empty_char=empty_char, bar_template=bar_template, | 
				
			||||||
 | 
					                       info_sep=info_sep, file=file, label=label, | 
				
			||||||
 | 
					                       width=width, color=color) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def clear(): | 
				
			||||||
 | 
					    """Clears the terminal screen.  This will have the effect of clearing | 
				
			||||||
 | 
					    the whole visible space of the terminal and moving the cursor to the | 
				
			||||||
 | 
					    top left.  This does not do anything if not connected to a terminal. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if not isatty(sys.stdout): | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					    # If we're on Windows and we don't have colorama available, then we | 
				
			||||||
 | 
					    # clear the screen by shelling out.  Otherwise we can use an escape | 
				
			||||||
 | 
					    # sequence. | 
				
			||||||
 | 
					    if WIN: | 
				
			||||||
 | 
					        os.system('cls') | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        sys.stdout.write('\033[2J\033[1;1H') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def style(text, fg=None, bg=None, bold=None, dim=None, underline=None, | 
				
			||||||
 | 
					          blink=None, reverse=None, reset=True): | 
				
			||||||
 | 
					    """Styles a text with ANSI styles and returns the new string.  By | 
				
			||||||
 | 
					    default the styling is self contained which means that at the end | 
				
			||||||
 | 
					    of the string a reset code is issued.  This can be prevented by | 
				
			||||||
 | 
					    passing ``reset=False``. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Examples:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        click.echo(click.style('Hello World!', fg='green')) | 
				
			||||||
 | 
					        click.echo(click.style('ATTENTION!', blink=True)) | 
				
			||||||
 | 
					        click.echo(click.style('Some things', reverse=True, fg='cyan')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Supported color names: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    * ``black`` (might be a gray) | 
				
			||||||
 | 
					    * ``red`` | 
				
			||||||
 | 
					    * ``green`` | 
				
			||||||
 | 
					    * ``yellow`` (might be an orange) | 
				
			||||||
 | 
					    * ``blue`` | 
				
			||||||
 | 
					    * ``magenta`` | 
				
			||||||
 | 
					    * ``cyan`` | 
				
			||||||
 | 
					    * ``white`` (might be light gray) | 
				
			||||||
 | 
					    * ``reset`` (reset the color code only) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param text: the string to style with ansi codes. | 
				
			||||||
 | 
					    :param fg: if provided this will become the foreground color. | 
				
			||||||
 | 
					    :param bg: if provided this will become the background color. | 
				
			||||||
 | 
					    :param bold: if provided this will enable or disable bold mode. | 
				
			||||||
 | 
					    :param dim: if provided this will enable or disable dim mode.  This is | 
				
			||||||
 | 
					                badly supported. | 
				
			||||||
 | 
					    :param underline: if provided this will enable or disable underline. | 
				
			||||||
 | 
					    :param blink: if provided this will enable or disable blinking. | 
				
			||||||
 | 
					    :param reverse: if provided this will enable or disable inverse | 
				
			||||||
 | 
					                    rendering (foreground becomes background and the | 
				
			||||||
 | 
					                    other way round). | 
				
			||||||
 | 
					    :param reset: by default a reset-all code is added at the end of the | 
				
			||||||
 | 
					                  string which means that styles do not carry over.  This | 
				
			||||||
 | 
					                  can be disabled to compose styles. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    bits = [] | 
				
			||||||
 | 
					    if fg: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            bits.append('\033[%dm' % (_ansi_colors.index(fg) + 30)) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise TypeError('Unknown color %r' % fg) | 
				
			||||||
 | 
					    if bg: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            bits.append('\033[%dm' % (_ansi_colors.index(bg) + 40)) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise TypeError('Unknown color %r' % bg) | 
				
			||||||
 | 
					    if bold is not None: | 
				
			||||||
 | 
					        bits.append('\033[%dm' % (1 if bold else 22)) | 
				
			||||||
 | 
					    if dim is not None: | 
				
			||||||
 | 
					        bits.append('\033[%dm' % (2 if dim else 22)) | 
				
			||||||
 | 
					    if underline is not None: | 
				
			||||||
 | 
					        bits.append('\033[%dm' % (4 if underline else 24)) | 
				
			||||||
 | 
					    if blink is not None: | 
				
			||||||
 | 
					        bits.append('\033[%dm' % (5 if blink else 25)) | 
				
			||||||
 | 
					    if reverse is not None: | 
				
			||||||
 | 
					        bits.append('\033[%dm' % (7 if reverse else 27)) | 
				
			||||||
 | 
					    bits.append(text) | 
				
			||||||
 | 
					    if reset: | 
				
			||||||
 | 
					        bits.append(_ansi_reset_all) | 
				
			||||||
 | 
					    return ''.join(bits) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def unstyle(text): | 
				
			||||||
 | 
					    """Removes ANSI styling information from a string.  Usually it's not | 
				
			||||||
 | 
					    necessary to use this function as Click's echo function will | 
				
			||||||
 | 
					    automatically remove styling if necessary. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param text: the text to remove style information from. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    return strip_ansi(text) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def secho(text, file=None, nl=True, err=False, color=None, **styles): | 
				
			||||||
 | 
					    """This function combines :func:`echo` and :func:`style` into one | 
				
			||||||
 | 
					    call.  As such the following two calls are the same:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        click.secho('Hello World!', fg='green') | 
				
			||||||
 | 
					        click.echo(click.style('Hello World!', fg='green')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    All keyword arguments are forwarded to the underlying functions | 
				
			||||||
 | 
					    depending on which one they go with. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    return echo(style(text, **styles), file=file, nl=nl, err=err, color=color) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def edit(text=None, editor=None, env=None, require_save=True, | 
				
			||||||
 | 
					         extension='.txt', filename=None): | 
				
			||||||
 | 
					    r"""Edits the given text in the defined editor.  If an editor is given | 
				
			||||||
 | 
					    (should be the full path to the executable but the regular operating | 
				
			||||||
 | 
					    system search path is used for finding the executable) it overrides | 
				
			||||||
 | 
					    the detected editor.  Optionally, some environment variables can be | 
				
			||||||
 | 
					    used.  If the editor is closed without changes, `None` is returned.  In | 
				
			||||||
 | 
					    case a file is edited directly the return value is always `None` and | 
				
			||||||
 | 
					    `require_save` and `extension` are ignored. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If the editor cannot be opened a :exc:`UsageError` is raised. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Note for Windows: to simplify cross-platform usage, the newlines are | 
				
			||||||
 | 
					    automatically converted from POSIX to Windows and vice versa.  As such, | 
				
			||||||
 | 
					    the message here will have ``\n`` as newline markers. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param text: the text to edit. | 
				
			||||||
 | 
					    :param editor: optionally the editor to use.  Defaults to automatic | 
				
			||||||
 | 
					                   detection. | 
				
			||||||
 | 
					    :param env: environment variables to forward to the editor. | 
				
			||||||
 | 
					    :param require_save: if this is true, then not saving in the editor | 
				
			||||||
 | 
					                         will make the return value become `None`. | 
				
			||||||
 | 
					    :param extension: the extension to tell the editor about.  This defaults | 
				
			||||||
 | 
					                      to `.txt` but changing this might change syntax | 
				
			||||||
 | 
					                      highlighting. | 
				
			||||||
 | 
					    :param filename: if provided it will edit this file instead of the | 
				
			||||||
 | 
					                     provided text contents.  It will not use a temporary | 
				
			||||||
 | 
					                     file as an indirection in that case. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    from ._termui_impl import Editor | 
				
			||||||
 | 
					    editor = Editor(editor=editor, env=env, require_save=require_save, | 
				
			||||||
 | 
					                    extension=extension) | 
				
			||||||
 | 
					    if filename is None: | 
				
			||||||
 | 
					        return editor.edit(text) | 
				
			||||||
 | 
					    editor.edit_file(filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def launch(url, wait=False, locate=False): | 
				
			||||||
 | 
					    """This function launches the given URL (or filename) in the default | 
				
			||||||
 | 
					    viewer application for this file type.  If this is an executable, it | 
				
			||||||
 | 
					    might launch the executable in a new session.  The return value is | 
				
			||||||
 | 
					    the exit code of the launched application.  Usually, ``0`` indicates | 
				
			||||||
 | 
					    success. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Examples:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        click.launch('http://click.pocoo.org/') | 
				
			||||||
 | 
					        click.launch('/my/downloaded/file', locate=True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param url: URL or filename of the thing to launch. | 
				
			||||||
 | 
					    :param wait: waits for the program to stop. | 
				
			||||||
 | 
					    :param locate: if this is set to `True` then instead of launching the | 
				
			||||||
 | 
					                   application associated with the URL it will attempt to | 
				
			||||||
 | 
					                   launch a file manager with the file located.  This | 
				
			||||||
 | 
					                   might have weird effects if the URL does not point to | 
				
			||||||
 | 
					                   the filesystem. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    from ._termui_impl import open_url | 
				
			||||||
 | 
					    return open_url(url, wait=wait, locate=locate) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If this is provided, getchar() calls into this instead.  This is used | 
				
			||||||
 | 
					# for unittesting purposes. | 
				
			||||||
 | 
					_getchar = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def getchar(echo=False): | 
				
			||||||
 | 
					    """Fetches a single character from the terminal and returns it.  This | 
				
			||||||
 | 
					    will always return a unicode character and under certain rare | 
				
			||||||
 | 
					    circumstances this might return more than one character.  The | 
				
			||||||
 | 
					    situations which more than one character is returned is when for | 
				
			||||||
 | 
					    whatever reason multiple characters end up in the terminal buffer or | 
				
			||||||
 | 
					    standard input was not actually a terminal. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Note that this will always read from the terminal, even if something | 
				
			||||||
 | 
					    is piped into the standard input. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param echo: if set to `True`, the character read will also show up on | 
				
			||||||
 | 
					                 the terminal.  The default is to not show it. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    f = _getchar | 
				
			||||||
 | 
					    if f is None: | 
				
			||||||
 | 
					        from ._termui_impl import getchar as f | 
				
			||||||
 | 
					    return f(echo) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def pause(info='Press any key to continue ...', err=False): | 
				
			||||||
 | 
					    """This command stops execution and waits for the user to press any | 
				
			||||||
 | 
					    key to continue.  This is similar to the Windows batch "pause" | 
				
			||||||
 | 
					    command.  If the program is not run through a terminal, this command | 
				
			||||||
 | 
					    will instead do nothing. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 4.0 | 
				
			||||||
 | 
					       Added the `err` parameter. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param info: the info string to print before pausing. | 
				
			||||||
 | 
					    :param err: if set to message goes to ``stderr`` instead of | 
				
			||||||
 | 
					                ``stdout``, the same as with echo. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if not isatty(sys.stdin) or not isatty(sys.stdout): | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        if info: | 
				
			||||||
 | 
					            echo(info, nl=False, err=err) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            getchar() | 
				
			||||||
 | 
					        except (KeyboardInterrupt, EOFError): | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					    finally: | 
				
			||||||
 | 
					        if info: | 
				
			||||||
 | 
					            echo(err=err) | 
				
			||||||
@ -0,0 +1,322 @@ | 
				
			|||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import shutil | 
				
			||||||
 | 
					import tempfile | 
				
			||||||
 | 
					import contextlib | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ._compat import iteritems, PY2 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If someone wants to vendor click, we want to ensure the | 
				
			||||||
 | 
					# correct package is discovered.  Ideally we could use a | 
				
			||||||
 | 
					# relative import here but unfortunately Python does not | 
				
			||||||
 | 
					# support that. | 
				
			||||||
 | 
					clickpkg = sys.modules[__name__.rsplit('.', 1)[0]] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY2: | 
				
			||||||
 | 
					    from cStringIO import StringIO | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import io | 
				
			||||||
 | 
					    from ._compat import _find_binary_reader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EchoingStdin(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, input, output): | 
				
			||||||
 | 
					        self._input = input | 
				
			||||||
 | 
					        self._output = output | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, x): | 
				
			||||||
 | 
					        return getattr(self._input, x) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _echo(self, rv): | 
				
			||||||
 | 
					        self._output.write(rv) | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read(self, n=-1): | 
				
			||||||
 | 
					        return self._echo(self._input.read(n)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def readline(self, n=-1): | 
				
			||||||
 | 
					        return self._echo(self._input.readline(n)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def readlines(self): | 
				
			||||||
 | 
					        return [self._echo(x) for x in self._input.readlines()] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self): | 
				
			||||||
 | 
					        return iter(self._echo(x) for x in self._input) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return repr(self._input) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_input_stream(input, charset): | 
				
			||||||
 | 
					    # Is already an input stream. | 
				
			||||||
 | 
					    if hasattr(input, 'read'): | 
				
			||||||
 | 
					        if PY2: | 
				
			||||||
 | 
					            return input | 
				
			||||||
 | 
					        rv = _find_binary_reader(input) | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv | 
				
			||||||
 | 
					        raise TypeError('Could not find binary reader for input stream.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if input is None: | 
				
			||||||
 | 
					        input = b'' | 
				
			||||||
 | 
					    elif not isinstance(input, bytes): | 
				
			||||||
 | 
					        input = input.encode(charset) | 
				
			||||||
 | 
					    if PY2: | 
				
			||||||
 | 
					        return StringIO(input) | 
				
			||||||
 | 
					    return io.BytesIO(input) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Result(object): | 
				
			||||||
 | 
					    """Holds the captured result of an invoked CLI script.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, runner, output_bytes, exit_code, exception, | 
				
			||||||
 | 
					                 exc_info=None): | 
				
			||||||
 | 
					        #: The runner that created the result | 
				
			||||||
 | 
					        self.runner = runner | 
				
			||||||
 | 
					        #: The output as bytes. | 
				
			||||||
 | 
					        self.output_bytes = output_bytes | 
				
			||||||
 | 
					        #: The exit code as integer. | 
				
			||||||
 | 
					        self.exit_code = exit_code | 
				
			||||||
 | 
					        #: The exception that happend if one did. | 
				
			||||||
 | 
					        self.exception = exception | 
				
			||||||
 | 
					        #: The traceback | 
				
			||||||
 | 
					        self.exc_info = exc_info | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def output(self): | 
				
			||||||
 | 
					        """The output as unicode string.""" | 
				
			||||||
 | 
					        return self.output_bytes.decode(self.runner.charset, 'replace') \ | 
				
			||||||
 | 
					            .replace('\r\n', '\n') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return '<Result %s>' % ( | 
				
			||||||
 | 
					            self.exception and repr(self.exception) or 'okay', | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CliRunner(object): | 
				
			||||||
 | 
					    """The CLI runner provides functionality to invoke a Click command line | 
				
			||||||
 | 
					    script for unittesting purposes in a isolated environment.  This only | 
				
			||||||
 | 
					    works in single-threaded systems without any concurrency as it changes the | 
				
			||||||
 | 
					    global interpreter state. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param charset: the character set for the input and output data.  This is | 
				
			||||||
 | 
					                    UTF-8 by default and should not be changed currently as | 
				
			||||||
 | 
					                    the reporting to Click only works in Python 2 properly. | 
				
			||||||
 | 
					    :param env: a dictionary with environment variables for overriding. | 
				
			||||||
 | 
					    :param echo_stdin: if this is set to `True`, then reading from stdin writes | 
				
			||||||
 | 
					                       to stdout.  This is useful for showing examples in | 
				
			||||||
 | 
					                       some circumstances.  Note that regular prompts | 
				
			||||||
 | 
					                       will automatically echo the input. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, charset=None, env=None, echo_stdin=False): | 
				
			||||||
 | 
					        if charset is None: | 
				
			||||||
 | 
					            charset = 'utf-8' | 
				
			||||||
 | 
					        self.charset = charset | 
				
			||||||
 | 
					        self.env = env or {} | 
				
			||||||
 | 
					        self.echo_stdin = echo_stdin | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_default_prog_name(self, cli): | 
				
			||||||
 | 
					        """Given a command object it will return the default program name | 
				
			||||||
 | 
					        for it.  The default is the `name` attribute or ``"root"`` if not | 
				
			||||||
 | 
					        set. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return cli.name or 'root' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def make_env(self, overrides=None): | 
				
			||||||
 | 
					        """Returns the environment overrides for invoking a script.""" | 
				
			||||||
 | 
					        rv = dict(self.env) | 
				
			||||||
 | 
					        if overrides: | 
				
			||||||
 | 
					            rv.update(overrides) | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @contextlib.contextmanager | 
				
			||||||
 | 
					    def isolation(self, input=None, env=None, color=False): | 
				
			||||||
 | 
					        """A context manager that sets up the isolation for invoking of a | 
				
			||||||
 | 
					        command line tool.  This sets up stdin with the given input data | 
				
			||||||
 | 
					        and `os.environ` with the overrides from the given dictionary. | 
				
			||||||
 | 
					        This also rebinds some internals in Click to be mocked (like the | 
				
			||||||
 | 
					        prompt functionality). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This is automatically done in the :meth:`invoke` method. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 4.0 | 
				
			||||||
 | 
					           The ``color`` parameter was added. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param input: the input stream to put into sys.stdin. | 
				
			||||||
 | 
					        :param env: the environment overrides as dictionary. | 
				
			||||||
 | 
					        :param color: whether the output should contain color codes. The | 
				
			||||||
 | 
					                      application can still override this explicitly. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        input = make_input_stream(input, self.charset) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        old_stdin = sys.stdin | 
				
			||||||
 | 
					        old_stdout = sys.stdout | 
				
			||||||
 | 
					        old_stderr = sys.stderr | 
				
			||||||
 | 
					        old_forced_width = clickpkg.formatting.FORCED_WIDTH | 
				
			||||||
 | 
					        clickpkg.formatting.FORCED_WIDTH = 80 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        env = self.make_env(env) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if PY2: | 
				
			||||||
 | 
					            sys.stdout = sys.stderr = bytes_output = StringIO() | 
				
			||||||
 | 
					            if self.echo_stdin: | 
				
			||||||
 | 
					                input = EchoingStdin(input, bytes_output) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            bytes_output = io.BytesIO() | 
				
			||||||
 | 
					            if self.echo_stdin: | 
				
			||||||
 | 
					                input = EchoingStdin(input, bytes_output) | 
				
			||||||
 | 
					            input = io.TextIOWrapper(input, encoding=self.charset) | 
				
			||||||
 | 
					            sys.stdout = sys.stderr = io.TextIOWrapper( | 
				
			||||||
 | 
					                bytes_output, encoding=self.charset) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sys.stdin = input | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def visible_input(prompt=None): | 
				
			||||||
 | 
					            sys.stdout.write(prompt or '') | 
				
			||||||
 | 
					            val = input.readline().rstrip('\r\n') | 
				
			||||||
 | 
					            sys.stdout.write(val + '\n') | 
				
			||||||
 | 
					            sys.stdout.flush() | 
				
			||||||
 | 
					            return val | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def hidden_input(prompt=None): | 
				
			||||||
 | 
					            sys.stdout.write((prompt or '') + '\n') | 
				
			||||||
 | 
					            sys.stdout.flush() | 
				
			||||||
 | 
					            return input.readline().rstrip('\r\n') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def _getchar(echo): | 
				
			||||||
 | 
					            char = sys.stdin.read(1) | 
				
			||||||
 | 
					            if echo: | 
				
			||||||
 | 
					                sys.stdout.write(char) | 
				
			||||||
 | 
					                sys.stdout.flush() | 
				
			||||||
 | 
					            return char | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        default_color = color | 
				
			||||||
 | 
					        def should_strip_ansi(stream=None, color=None): | 
				
			||||||
 | 
					            if color is None: | 
				
			||||||
 | 
					                return not default_color | 
				
			||||||
 | 
					            return not color | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        old_visible_prompt_func = clickpkg.termui.visible_prompt_func | 
				
			||||||
 | 
					        old_hidden_prompt_func = clickpkg.termui.hidden_prompt_func | 
				
			||||||
 | 
					        old__getchar_func = clickpkg.termui._getchar | 
				
			||||||
 | 
					        old_should_strip_ansi = clickpkg.utils.should_strip_ansi | 
				
			||||||
 | 
					        clickpkg.termui.visible_prompt_func = visible_input | 
				
			||||||
 | 
					        clickpkg.termui.hidden_prompt_func = hidden_input | 
				
			||||||
 | 
					        clickpkg.termui._getchar = _getchar | 
				
			||||||
 | 
					        clickpkg.utils.should_strip_ansi = should_strip_ansi | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        old_env = {} | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            for key, value in iteritems(env): | 
				
			||||||
 | 
					                old_env[key] = os.environ.get(key) | 
				
			||||||
 | 
					                if value is None: | 
				
			||||||
 | 
					                    try: | 
				
			||||||
 | 
					                        del os.environ[key] | 
				
			||||||
 | 
					                    except Exception: | 
				
			||||||
 | 
					                        pass | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    os.environ[key] = value | 
				
			||||||
 | 
					            yield bytes_output | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            for key, value in iteritems(old_env): | 
				
			||||||
 | 
					                if value is None: | 
				
			||||||
 | 
					                    try: | 
				
			||||||
 | 
					                        del os.environ[key] | 
				
			||||||
 | 
					                    except Exception: | 
				
			||||||
 | 
					                        pass | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    os.environ[key] = value | 
				
			||||||
 | 
					            sys.stdout = old_stdout | 
				
			||||||
 | 
					            sys.stderr = old_stderr | 
				
			||||||
 | 
					            sys.stdin = old_stdin | 
				
			||||||
 | 
					            clickpkg.termui.visible_prompt_func = old_visible_prompt_func | 
				
			||||||
 | 
					            clickpkg.termui.hidden_prompt_func = old_hidden_prompt_func | 
				
			||||||
 | 
					            clickpkg.termui._getchar = old__getchar_func | 
				
			||||||
 | 
					            clickpkg.utils.should_strip_ansi = old_should_strip_ansi | 
				
			||||||
 | 
					            clickpkg.formatting.FORCED_WIDTH = old_forced_width | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def invoke(self, cli, args=None, input=None, env=None, | 
				
			||||||
 | 
					               catch_exceptions=True, color=False, **extra): | 
				
			||||||
 | 
					        """Invokes a command in an isolated environment.  The arguments are | 
				
			||||||
 | 
					        forwarded directly to the command line script, the `extra` keyword | 
				
			||||||
 | 
					        arguments are passed to the :meth:`~clickpkg.Command.main` function of | 
				
			||||||
 | 
					        the command. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This returns a :class:`Result` object. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 3.0 | 
				
			||||||
 | 
					           The ``catch_exceptions`` parameter was added. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionchanged:: 3.0 | 
				
			||||||
 | 
					           The result object now has an `exc_info` attribute with the | 
				
			||||||
 | 
					           traceback if available. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 4.0 | 
				
			||||||
 | 
					           The ``color`` parameter was added. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param cli: the command to invoke | 
				
			||||||
 | 
					        :param args: the arguments to invoke | 
				
			||||||
 | 
					        :param input: the input data for `sys.stdin`. | 
				
			||||||
 | 
					        :param env: the environment overrides. | 
				
			||||||
 | 
					        :param catch_exceptions: Whether to catch any other exceptions than | 
				
			||||||
 | 
					                                 ``SystemExit``. | 
				
			||||||
 | 
					        :param extra: the keyword arguments to pass to :meth:`main`. | 
				
			||||||
 | 
					        :param color: whether the output should contain color codes. The | 
				
			||||||
 | 
					                      application can still override this explicitly. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        exc_info = None | 
				
			||||||
 | 
					        with self.isolation(input=input, env=env, color=color) as out: | 
				
			||||||
 | 
					            exception = None | 
				
			||||||
 | 
					            exit_code = 0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                cli.main(args=args or (), | 
				
			||||||
 | 
					                         prog_name=self.get_default_prog_name(cli), **extra) | 
				
			||||||
 | 
					            except SystemExit as e: | 
				
			||||||
 | 
					                if e.code != 0: | 
				
			||||||
 | 
					                    exception = e | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                exc_info = sys.exc_info() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                exit_code = e.code | 
				
			||||||
 | 
					                if not isinstance(exit_code, int): | 
				
			||||||
 | 
					                    sys.stdout.write(str(exit_code)) | 
				
			||||||
 | 
					                    sys.stdout.write('\n') | 
				
			||||||
 | 
					                    exit_code = 1 | 
				
			||||||
 | 
					            except Exception as e: | 
				
			||||||
 | 
					                if not catch_exceptions: | 
				
			||||||
 | 
					                    raise | 
				
			||||||
 | 
					                exception = e | 
				
			||||||
 | 
					                exit_code = -1 | 
				
			||||||
 | 
					                exc_info = sys.exc_info() | 
				
			||||||
 | 
					            finally: | 
				
			||||||
 | 
					                sys.stdout.flush() | 
				
			||||||
 | 
					                output = out.getvalue() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Result(runner=self, | 
				
			||||||
 | 
					                      output_bytes=output, | 
				
			||||||
 | 
					                      exit_code=exit_code, | 
				
			||||||
 | 
					                      exception=exception, | 
				
			||||||
 | 
					                      exc_info=exc_info) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @contextlib.contextmanager | 
				
			||||||
 | 
					    def isolated_filesystem(self): | 
				
			||||||
 | 
					        """A context manager that creates a temporary folder and changes | 
				
			||||||
 | 
					        the current working directory to it for isolated filesystem tests. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        cwd = os.getcwd() | 
				
			||||||
 | 
					        t = tempfile.mkdtemp() | 
				
			||||||
 | 
					        os.chdir(t) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            yield t | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            os.chdir(cwd) | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                shutil.rmtree(t) | 
				
			||||||
 | 
					            except (OSError, IOError): | 
				
			||||||
 | 
					                pass | 
				
			||||||
@ -0,0 +1,550 @@ | 
				
			|||||||
 | 
					import os | 
				
			||||||
 | 
					import stat | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ._compat import open_stream, text_type, filename_to_ui, \ | 
				
			||||||
 | 
					    get_filesystem_encoding, get_streerror, _get_argv_encoding, PY2 | 
				
			||||||
 | 
					from .exceptions import BadParameter | 
				
			||||||
 | 
					from .utils import safecall, LazyFile | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ParamType(object): | 
				
			||||||
 | 
					    """Helper for converting values through types.  The following is | 
				
			||||||
 | 
					    necessary for a valid type: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *   it needs a name | 
				
			||||||
 | 
					    *   it needs to pass through None unchanged | 
				
			||||||
 | 
					    *   it needs to convert from a string | 
				
			||||||
 | 
					    *   it needs to convert its result type through unchanged | 
				
			||||||
 | 
					        (eg: needs to be idempotent) | 
				
			||||||
 | 
					    *   it needs to be able to deal with param and context being `None`. | 
				
			||||||
 | 
					        This can be the case when the object is used with prompt | 
				
			||||||
 | 
					        inputs. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    is_composite = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: the descriptive name of this type | 
				
			||||||
 | 
					    name = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: if a list of this type is expected and the value is pulled from a | 
				
			||||||
 | 
					    #: string environment variable, this is what splits it up.  `None` | 
				
			||||||
 | 
					    #: means any whitespace.  For all parameters the general rule is that | 
				
			||||||
 | 
					    #: whitespace splits them up.  The exception are paths and files which | 
				
			||||||
 | 
					    #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on | 
				
			||||||
 | 
					    #: Windows). | 
				
			||||||
 | 
					    envvar_list_splitter = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __call__(self, value, param=None, ctx=None): | 
				
			||||||
 | 
					        if value is not None: | 
				
			||||||
 | 
					            return self.convert(value, param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_metavar(self, param): | 
				
			||||||
 | 
					        """Returns the metavar default for this param if it provides one.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_missing_message(self, param): | 
				
			||||||
 | 
					        """Optionally might return extra information about a missing | 
				
			||||||
 | 
					        parameter. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 2.0 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        """Converts the value.  This is not invoked for values that are | 
				
			||||||
 | 
					        `None` (the missing value). | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def split_envvar_value(self, rv): | 
				
			||||||
 | 
					        """Given a value from an environment variable this splits it up | 
				
			||||||
 | 
					        into small chunks depending on the defined envvar list splitter. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If the splitter is set to `None`, which means that whitespace splits, | 
				
			||||||
 | 
					        then leading and trailing whitespace is ignored.  Otherwise, leading | 
				
			||||||
 | 
					        and trailing splitters usually lead to empty items being included. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return (rv or '').split(self.envvar_list_splitter) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def fail(self, message, param=None, ctx=None): | 
				
			||||||
 | 
					        """Helper method to fail with an invalid value message.""" | 
				
			||||||
 | 
					        raise BadParameter(message, ctx=ctx, param=param) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CompositeParamType(ParamType): | 
				
			||||||
 | 
					    is_composite = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def arity(self): | 
				
			||||||
 | 
					        raise NotImplementedError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FuncParamType(ParamType): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, func): | 
				
			||||||
 | 
					        self.name = func.__name__ | 
				
			||||||
 | 
					        self.func = func | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return self.func(value) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                value = text_type(value) | 
				
			||||||
 | 
					            except UnicodeError: | 
				
			||||||
 | 
					                value = str(value).decode('utf-8', 'replace') | 
				
			||||||
 | 
					            self.fail(value, param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UnprocessedParamType(ParamType): | 
				
			||||||
 | 
					    name = 'text' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return 'UNPROCESSED' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StringParamType(ParamType): | 
				
			||||||
 | 
					    name = 'text' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        if isinstance(value, bytes): | 
				
			||||||
 | 
					            enc = _get_argv_encoding() | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                value = value.decode(enc) | 
				
			||||||
 | 
					            except UnicodeError: | 
				
			||||||
 | 
					                fs_enc = get_filesystem_encoding() | 
				
			||||||
 | 
					                if fs_enc != enc: | 
				
			||||||
 | 
					                    try: | 
				
			||||||
 | 
					                        value = value.decode(fs_enc) | 
				
			||||||
 | 
					                    except UnicodeError: | 
				
			||||||
 | 
					                        value = value.decode('utf-8', 'replace') | 
				
			||||||
 | 
					            return value | 
				
			||||||
 | 
					        return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return 'STRING' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Choice(ParamType): | 
				
			||||||
 | 
					    """The choice type allows a value to be checked against a fixed set of | 
				
			||||||
 | 
					    supported values.  All of these values have to be strings. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    See :ref:`choice-opts` for an example. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    name = 'choice' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, choices): | 
				
			||||||
 | 
					        self.choices = choices | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_metavar(self, param): | 
				
			||||||
 | 
					        return '[%s]' % '|'.join(self.choices) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_missing_message(self, param): | 
				
			||||||
 | 
					        return 'Choose from %s.' % ', '.join(self.choices) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        # Exact match | 
				
			||||||
 | 
					        if value in self.choices: | 
				
			||||||
 | 
					            return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Match through normalization | 
				
			||||||
 | 
					        if ctx is not None and \ | 
				
			||||||
 | 
					           ctx.token_normalize_func is not None: | 
				
			||||||
 | 
					            value = ctx.token_normalize_func(value) | 
				
			||||||
 | 
					            for choice in self.choices: | 
				
			||||||
 | 
					                if ctx.token_normalize_func(choice) == value: | 
				
			||||||
 | 
					                    return choice | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.fail('invalid choice: %s. (choose from %s)' % | 
				
			||||||
 | 
					                  (value, ', '.join(self.choices)), param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return 'Choice(%r)' % list(self.choices) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class IntParamType(ParamType): | 
				
			||||||
 | 
					    name = 'integer' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return int(value) | 
				
			||||||
 | 
					        except (ValueError, UnicodeError): | 
				
			||||||
 | 
					            self.fail('%s is not a valid integer' % value, param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return 'INT' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class IntRange(IntParamType): | 
				
			||||||
 | 
					    """A parameter that works similar to :data:`click.INT` but restricts | 
				
			||||||
 | 
					    the value to fit into a range.  The default behavior is to fail if the | 
				
			||||||
 | 
					    value falls outside the range, but it can also be silently clamped | 
				
			||||||
 | 
					    between the two edges. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    See :ref:`ranges` for an example. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    name = 'integer range' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, min=None, max=None, clamp=False): | 
				
			||||||
 | 
					        self.min = min | 
				
			||||||
 | 
					        self.max = max | 
				
			||||||
 | 
					        self.clamp = clamp | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        rv = IntParamType.convert(self, value, param, ctx) | 
				
			||||||
 | 
					        if self.clamp: | 
				
			||||||
 | 
					            if self.min is not None and rv < self.min: | 
				
			||||||
 | 
					                return self.min | 
				
			||||||
 | 
					            if self.max is not None and rv > self.max: | 
				
			||||||
 | 
					                return self.max | 
				
			||||||
 | 
					        if self.min is not None and rv < self.min or \ | 
				
			||||||
 | 
					           self.max is not None and rv > self.max: | 
				
			||||||
 | 
					            if self.min is None: | 
				
			||||||
 | 
					                self.fail('%s is bigger than the maximum valid value ' | 
				
			||||||
 | 
					                          '%s.' % (rv, self.max), param, ctx) | 
				
			||||||
 | 
					            elif self.max is None: | 
				
			||||||
 | 
					                self.fail('%s is smaller than the minimum valid value ' | 
				
			||||||
 | 
					                          '%s.' % (rv, self.min), param, ctx) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                self.fail('%s is not in the valid range of %s to %s.' | 
				
			||||||
 | 
					                          % (rv, self.min, self.max), param, ctx) | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return 'IntRange(%r, %r)' % (self.min, self.max) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BoolParamType(ParamType): | 
				
			||||||
 | 
					    name = 'boolean' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        if isinstance(value, bool): | 
				
			||||||
 | 
					            return bool(value) | 
				
			||||||
 | 
					        value = value.lower() | 
				
			||||||
 | 
					        if value in ('true', '1', 'yes', 'y'): | 
				
			||||||
 | 
					            return True | 
				
			||||||
 | 
					        elif value in ('false', '0', 'no', 'n'): | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        self.fail('%s is not a valid boolean' % value, param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return 'BOOL' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FloatParamType(ParamType): | 
				
			||||||
 | 
					    name = 'float' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return float(value) | 
				
			||||||
 | 
					        except (UnicodeError, ValueError): | 
				
			||||||
 | 
					            self.fail('%s is not a valid floating point value' % | 
				
			||||||
 | 
					                      value, param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return 'FLOAT' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UUIDParameterType(ParamType): | 
				
			||||||
 | 
					    name = 'uuid' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        import uuid | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            if PY2 and isinstance(value, text_type): | 
				
			||||||
 | 
					                value = value.encode('ascii') | 
				
			||||||
 | 
					            return uuid.UUID(value) | 
				
			||||||
 | 
					        except (UnicodeError, ValueError): | 
				
			||||||
 | 
					            self.fail('%s is not a valid UUID value' % value, param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return 'UUID' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class File(ParamType): | 
				
			||||||
 | 
					    """Declares a parameter to be a file for reading or writing.  The file | 
				
			||||||
 | 
					    is automatically closed once the context tears down (after the command | 
				
			||||||
 | 
					    finished working). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Files can be opened for reading or writing.  The special value ``-`` | 
				
			||||||
 | 
					    indicates stdin or stdout depending on the mode. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    By default, the file is opened for reading text data, but it can also be | 
				
			||||||
 | 
					    opened in binary mode or for writing.  The encoding parameter can be used | 
				
			||||||
 | 
					    to force a specific encoding. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The `lazy` flag controls if the file should be opened immediately or | 
				
			||||||
 | 
					    upon first IO.  The default is to be non lazy for standard input and | 
				
			||||||
 | 
					    output streams as well as files opened for reading, lazy otherwise. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Starting with Click 2.0, files can also be opened atomically in which | 
				
			||||||
 | 
					    case all writes go into a separate file in the same folder and upon | 
				
			||||||
 | 
					    completion the file will be moved over to the original location.  This | 
				
			||||||
 | 
					    is useful if a file regularly read by other users is modified. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    See :ref:`file-args` for more information. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    name = 'filename' | 
				
			||||||
 | 
					    envvar_list_splitter = os.path.pathsep | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, mode='r', encoding=None, errors='strict', lazy=None, | 
				
			||||||
 | 
					                 atomic=False): | 
				
			||||||
 | 
					        self.mode = mode | 
				
			||||||
 | 
					        self.encoding = encoding | 
				
			||||||
 | 
					        self.errors = errors | 
				
			||||||
 | 
					        self.lazy = lazy | 
				
			||||||
 | 
					        self.atomic = atomic | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def resolve_lazy_flag(self, value): | 
				
			||||||
 | 
					        if self.lazy is not None: | 
				
			||||||
 | 
					            return self.lazy | 
				
			||||||
 | 
					        if value == '-': | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        elif 'w' in self.mode: | 
				
			||||||
 | 
					            return True | 
				
			||||||
 | 
					        return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            if hasattr(value, 'read') or hasattr(value, 'write'): | 
				
			||||||
 | 
					                return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            lazy = self.resolve_lazy_flag(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if lazy: | 
				
			||||||
 | 
					                f = LazyFile(value, self.mode, self.encoding, self.errors, | 
				
			||||||
 | 
					                             atomic=self.atomic) | 
				
			||||||
 | 
					                if ctx is not None: | 
				
			||||||
 | 
					                    ctx.call_on_close(f.close_intelligently) | 
				
			||||||
 | 
					                return f | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            f, should_close = open_stream(value, self.mode, | 
				
			||||||
 | 
					                                          self.encoding, self.errors, | 
				
			||||||
 | 
					                                          atomic=self.atomic) | 
				
			||||||
 | 
					            # If a context is provided, we automatically close the file | 
				
			||||||
 | 
					            # at the end of the context execution (or flush out).  If a | 
				
			||||||
 | 
					            # context does not exist, it's the caller's responsibility to | 
				
			||||||
 | 
					            # properly close the file.  This for instance happens when the | 
				
			||||||
 | 
					            # type is used with prompts. | 
				
			||||||
 | 
					            if ctx is not None: | 
				
			||||||
 | 
					                if should_close: | 
				
			||||||
 | 
					                    ctx.call_on_close(safecall(f.close)) | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    ctx.call_on_close(safecall(f.flush)) | 
				
			||||||
 | 
					            return f | 
				
			||||||
 | 
					        except (IOError, OSError) as e: | 
				
			||||||
 | 
					            self.fail('Could not open file: %s: %s' % ( | 
				
			||||||
 | 
					                filename_to_ui(value), | 
				
			||||||
 | 
					                get_streerror(e), | 
				
			||||||
 | 
					            ), param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Path(ParamType): | 
				
			||||||
 | 
					    """The path type is similar to the :class:`File` type but it performs | 
				
			||||||
 | 
					    different checks.  First of all, instead of returning an open file | 
				
			||||||
 | 
					    handle it returns just the filename.  Secondly, it can perform various | 
				
			||||||
 | 
					    basic checks about what the file or directory should be. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 6.0 | 
				
			||||||
 | 
					       `allow_dash` was added. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param exists: if set to true, the file or directory needs to exist for | 
				
			||||||
 | 
					                   this value to be valid.  If this is not required and a | 
				
			||||||
 | 
					                   file does indeed not exist, then all further checks are | 
				
			||||||
 | 
					                   silently skipped. | 
				
			||||||
 | 
					    :param file_okay: controls if a file is a possible value. | 
				
			||||||
 | 
					    :param dir_okay: controls if a directory is a possible value. | 
				
			||||||
 | 
					    :param writable: if true, a writable check is performed. | 
				
			||||||
 | 
					    :param readable: if true, a readable check is performed. | 
				
			||||||
 | 
					    :param resolve_path: if this is true, then the path is fully resolved | 
				
			||||||
 | 
					                         before the value is passed onwards.  This means | 
				
			||||||
 | 
					                         that it's absolute and symlinks are resolved. | 
				
			||||||
 | 
					    :param allow_dash: If this is set to `True`, a single dash to indicate | 
				
			||||||
 | 
					                       standard streams is permitted. | 
				
			||||||
 | 
					    :param type: optionally a string type that should be used to | 
				
			||||||
 | 
					                 represent the path.  The default is `None` which | 
				
			||||||
 | 
					                 means the return value will be either bytes or | 
				
			||||||
 | 
					                 unicode depending on what makes most sense given the | 
				
			||||||
 | 
					                 input data Click deals with. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    envvar_list_splitter = os.path.pathsep | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, exists=False, file_okay=True, dir_okay=True, | 
				
			||||||
 | 
					                 writable=False, readable=True, resolve_path=False, | 
				
			||||||
 | 
					                 allow_dash=False, path_type=None): | 
				
			||||||
 | 
					        self.exists = exists | 
				
			||||||
 | 
					        self.file_okay = file_okay | 
				
			||||||
 | 
					        self.dir_okay = dir_okay | 
				
			||||||
 | 
					        self.writable = writable | 
				
			||||||
 | 
					        self.readable = readable | 
				
			||||||
 | 
					        self.resolve_path = resolve_path | 
				
			||||||
 | 
					        self.allow_dash = allow_dash | 
				
			||||||
 | 
					        self.type = path_type | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.file_okay and not self.dir_okay: | 
				
			||||||
 | 
					            self.name = 'file' | 
				
			||||||
 | 
					            self.path_type = 'File' | 
				
			||||||
 | 
					        if self.dir_okay and not self.file_okay: | 
				
			||||||
 | 
					            self.name = 'directory' | 
				
			||||||
 | 
					            self.path_type = 'Directory' | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.name = 'path' | 
				
			||||||
 | 
					            self.path_type = 'Path' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def coerce_path_result(self, rv): | 
				
			||||||
 | 
					        if self.type is not None and not isinstance(rv, self.type): | 
				
			||||||
 | 
					            if self.type is text_type: | 
				
			||||||
 | 
					                rv = rv.decode(get_filesystem_encoding()) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                rv = rv.encode(get_filesystem_encoding()) | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        rv = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        is_dash = self.file_okay and self.allow_dash and rv in (b'-', '-') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not is_dash: | 
				
			||||||
 | 
					            if self.resolve_path: | 
				
			||||||
 | 
					                rv = os.path.realpath(rv) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                st = os.stat(rv) | 
				
			||||||
 | 
					            except OSError: | 
				
			||||||
 | 
					                if not self.exists: | 
				
			||||||
 | 
					                    return self.coerce_path_result(rv) | 
				
			||||||
 | 
					                self.fail('%s "%s" does not exist.' % ( | 
				
			||||||
 | 
					                    self.path_type, | 
				
			||||||
 | 
					                    filename_to_ui(value) | 
				
			||||||
 | 
					                ), param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if not self.file_okay and stat.S_ISREG(st.st_mode): | 
				
			||||||
 | 
					                self.fail('%s "%s" is a file.' % ( | 
				
			||||||
 | 
					                    self.path_type, | 
				
			||||||
 | 
					                    filename_to_ui(value) | 
				
			||||||
 | 
					                ), param, ctx) | 
				
			||||||
 | 
					            if not self.dir_okay and stat.S_ISDIR(st.st_mode): | 
				
			||||||
 | 
					                self.fail('%s "%s" is a directory.' % ( | 
				
			||||||
 | 
					                    self.path_type, | 
				
			||||||
 | 
					                    filename_to_ui(value) | 
				
			||||||
 | 
					                ), param, ctx) | 
				
			||||||
 | 
					            if self.writable and not os.access(value, os.W_OK): | 
				
			||||||
 | 
					                self.fail('%s "%s" is not writable.' % ( | 
				
			||||||
 | 
					                    self.path_type, | 
				
			||||||
 | 
					                    filename_to_ui(value) | 
				
			||||||
 | 
					                ), param, ctx) | 
				
			||||||
 | 
					            if self.readable and not os.access(value, os.R_OK): | 
				
			||||||
 | 
					                self.fail('%s "%s" is not readable.' % ( | 
				
			||||||
 | 
					                    self.path_type, | 
				
			||||||
 | 
					                    filename_to_ui(value) | 
				
			||||||
 | 
					                ), param, ctx) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.coerce_path_result(rv) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Tuple(CompositeParamType): | 
				
			||||||
 | 
					    """The default behavior of Click is to apply a type on a value directly. | 
				
			||||||
 | 
					    This works well in most cases, except for when `nargs` is set to a fixed | 
				
			||||||
 | 
					    count and different types should be used for different items.  In this | 
				
			||||||
 | 
					    case the :class:`Tuple` type can be used.  This type can only be used | 
				
			||||||
 | 
					    if `nargs` is set to a fixed number. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    For more information see :ref:`tuple-type`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This can be selected by using a Python tuple literal as a type. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param types: a list of types that should be used for the tuple items. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, types): | 
				
			||||||
 | 
					        self.types = [convert_type(ty) for ty in types] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def name(self): | 
				
			||||||
 | 
					        return "<" + " ".join(ty.name for ty in self.types) + ">" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def arity(self): | 
				
			||||||
 | 
					        return len(self.types) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        if len(value) != len(self.types): | 
				
			||||||
 | 
					            raise TypeError('It would appear that nargs is set to conflict ' | 
				
			||||||
 | 
					                            'with the composite type arity.') | 
				
			||||||
 | 
					        return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def convert_type(ty, default=None): | 
				
			||||||
 | 
					    """Converts a callable or python ty into the most appropriate param | 
				
			||||||
 | 
					    ty. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    guessed_type = False | 
				
			||||||
 | 
					    if ty is None and default is not None: | 
				
			||||||
 | 
					        if isinstance(default, tuple): | 
				
			||||||
 | 
					            ty = tuple(map(type, default)) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            ty = type(default) | 
				
			||||||
 | 
					        guessed_type = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if isinstance(ty, tuple): | 
				
			||||||
 | 
					        return Tuple(ty) | 
				
			||||||
 | 
					    if isinstance(ty, ParamType): | 
				
			||||||
 | 
					        return ty | 
				
			||||||
 | 
					    if ty is text_type or ty is str or ty is None: | 
				
			||||||
 | 
					        return STRING | 
				
			||||||
 | 
					    if ty is int: | 
				
			||||||
 | 
					        return INT | 
				
			||||||
 | 
					    # Booleans are only okay if not guessed.  This is done because for | 
				
			||||||
 | 
					    # flags the default value is actually a bit of a lie in that it | 
				
			||||||
 | 
					    # indicates which of the flags is the one we want.  See get_default() | 
				
			||||||
 | 
					    # for more information. | 
				
			||||||
 | 
					    if ty is bool and not guessed_type: | 
				
			||||||
 | 
					        return BOOL | 
				
			||||||
 | 
					    if ty is float: | 
				
			||||||
 | 
					        return FLOAT | 
				
			||||||
 | 
					    if guessed_type: | 
				
			||||||
 | 
					        return STRING | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Catch a common mistake | 
				
			||||||
 | 
					    if __debug__: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            if issubclass(ty, ParamType): | 
				
			||||||
 | 
					                raise AssertionError('Attempted to use an uninstantiated ' | 
				
			||||||
 | 
					                                     'parameter type (%s).' % ty) | 
				
			||||||
 | 
					        except TypeError: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					    return FuncParamType(ty) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: A dummy parameter type that just does nothing.  From a user's | 
				
			||||||
 | 
					#: perspective this appears to just be the same as `STRING` but internally | 
				
			||||||
 | 
					#: no string conversion takes place.  This is necessary to achieve the | 
				
			||||||
 | 
					#: same bytes/unicode behavior on Python 2/3 in situations where you want | 
				
			||||||
 | 
					#: to not convert argument types.  This is usually useful when working | 
				
			||||||
 | 
					#: with file paths as they can appear in bytes and unicode. | 
				
			||||||
 | 
					#: | 
				
			||||||
 | 
					#: For path related uses the :class:`Path` type is a better choice but | 
				
			||||||
 | 
					#: there are situations where an unprocessed type is useful which is why | 
				
			||||||
 | 
					#: it is is provided. | 
				
			||||||
 | 
					#: | 
				
			||||||
 | 
					#: .. versionadded:: 4.0 | 
				
			||||||
 | 
					UNPROCESSED = UnprocessedParamType() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: A unicode string parameter type which is the implicit default.  This | 
				
			||||||
 | 
					#: can also be selected by using ``str`` as type. | 
				
			||||||
 | 
					STRING = StringParamType() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: An integer parameter.  This can also be selected by using ``int`` as | 
				
			||||||
 | 
					#: type. | 
				
			||||||
 | 
					INT = IntParamType() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: A floating point value parameter.  This can also be selected by using | 
				
			||||||
 | 
					#: ``float`` as type. | 
				
			||||||
 | 
					FLOAT = FloatParamType() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: A boolean parameter.  This is the default for boolean flags.  This can | 
				
			||||||
 | 
					#: also be selected by using ``bool`` as a type. | 
				
			||||||
 | 
					BOOL = BoolParamType() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: A UUID parameter. | 
				
			||||||
 | 
					UUID = UUIDParameterType() | 
				
			||||||
@ -0,0 +1,415 @@ | 
				
			|||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .globals import resolve_color_default | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ._compat import text_type, open_stream, get_filesystem_encoding, \ | 
				
			||||||
 | 
					    get_streerror, string_types, PY2, binary_streams, text_streams, \ | 
				
			||||||
 | 
					    filename_to_ui, auto_wrap_for_ansi, strip_ansi, should_strip_ansi, \ | 
				
			||||||
 | 
					    _default_text_stdout, _default_text_stderr, is_bytes, WIN | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if not PY2: | 
				
			||||||
 | 
					    from ._compat import _find_binary_writer | 
				
			||||||
 | 
					elif WIN: | 
				
			||||||
 | 
					    from ._winconsole import _get_windows_argv, \ | 
				
			||||||
 | 
					         _hash_py_argv, _initial_argv_hash | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo_native_types = string_types + (bytes, bytearray) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _posixify(name): | 
				
			||||||
 | 
					    return '-'.join(name.split()).lower() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def safecall(func): | 
				
			||||||
 | 
					    """Wraps a function so that it swallows exceptions.""" | 
				
			||||||
 | 
					    def wrapper(*args, **kwargs): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return func(*args, **kwargs) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					    return wrapper | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_str(value): | 
				
			||||||
 | 
					    """Converts a value into a valid string.""" | 
				
			||||||
 | 
					    if isinstance(value, bytes): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return value.decode(get_filesystem_encoding()) | 
				
			||||||
 | 
					        except UnicodeError: | 
				
			||||||
 | 
					            return value.decode('utf-8', 'replace') | 
				
			||||||
 | 
					    return text_type(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_default_short_help(help, max_length=45): | 
				
			||||||
 | 
					    words = help.split() | 
				
			||||||
 | 
					    total_length = 0 | 
				
			||||||
 | 
					    result = [] | 
				
			||||||
 | 
					    done = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for word in words: | 
				
			||||||
 | 
					        if word[-1:] == '.': | 
				
			||||||
 | 
					            done = True | 
				
			||||||
 | 
					        new_length = result and 1 + len(word) or len(word) | 
				
			||||||
 | 
					        if total_length + new_length > max_length: | 
				
			||||||
 | 
					            result.append('...') | 
				
			||||||
 | 
					            done = True | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            if result: | 
				
			||||||
 | 
					                result.append(' ') | 
				
			||||||
 | 
					            result.append(word) | 
				
			||||||
 | 
					        if done: | 
				
			||||||
 | 
					            break | 
				
			||||||
 | 
					        total_length += new_length | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ''.join(result) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LazyFile(object): | 
				
			||||||
 | 
					    """A lazy file works like a regular file but it does not fully open | 
				
			||||||
 | 
					    the file but it does perform some basic checks early to see if the | 
				
			||||||
 | 
					    filename parameter does make sense.  This is useful for safely opening | 
				
			||||||
 | 
					    files for writing. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, filename, mode='r', encoding=None, errors='strict', | 
				
			||||||
 | 
					                 atomic=False): | 
				
			||||||
 | 
					        self.name = filename | 
				
			||||||
 | 
					        self.mode = mode | 
				
			||||||
 | 
					        self.encoding = encoding | 
				
			||||||
 | 
					        self.errors = errors | 
				
			||||||
 | 
					        self.atomic = atomic | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if filename == '-': | 
				
			||||||
 | 
					            self._f, self.should_close = open_stream(filename, mode, | 
				
			||||||
 | 
					                                                     encoding, errors) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            if 'r' in mode: | 
				
			||||||
 | 
					                # Open and close the file in case we're opening it for | 
				
			||||||
 | 
					                # reading so that we can catch at least some errors in | 
				
			||||||
 | 
					                # some cases early. | 
				
			||||||
 | 
					                open(filename, mode).close() | 
				
			||||||
 | 
					            self._f = None | 
				
			||||||
 | 
					            self.should_close = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, name): | 
				
			||||||
 | 
					        return getattr(self.open(), name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        if self._f is not None: | 
				
			||||||
 | 
					            return repr(self._f) | 
				
			||||||
 | 
					        return '<unopened file %r %s>' % (self.name, self.mode) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def open(self): | 
				
			||||||
 | 
					        """Opens the file if it's not yet open.  This call might fail with | 
				
			||||||
 | 
					        a :exc:`FileError`.  Not handling this error will produce an error | 
				
			||||||
 | 
					        that Click shows. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if self._f is not None: | 
				
			||||||
 | 
					            return self._f | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            rv, self.should_close = open_stream(self.name, self.mode, | 
				
			||||||
 | 
					                                                self.encoding, | 
				
			||||||
 | 
					                                                self.errors, | 
				
			||||||
 | 
					                                                atomic=self.atomic) | 
				
			||||||
 | 
					        except (IOError, OSError) as e: | 
				
			||||||
 | 
					            from .exceptions import FileError | 
				
			||||||
 | 
					            raise FileError(self.name, hint=get_streerror(e)) | 
				
			||||||
 | 
					        self._f = rv | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def close(self): | 
				
			||||||
 | 
					        """Closes the underlying file, no matter what.""" | 
				
			||||||
 | 
					        if self._f is not None: | 
				
			||||||
 | 
					            self._f.close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def close_intelligently(self): | 
				
			||||||
 | 
					        """This function only closes the file if it was opened by the lazy | 
				
			||||||
 | 
					        file wrapper.  For instance this will never close stdin. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if self.should_close: | 
				
			||||||
 | 
					            self.close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __enter__(self): | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, exc_type, exc_value, tb): | 
				
			||||||
 | 
					        self.close_intelligently() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self): | 
				
			||||||
 | 
					        self.open() | 
				
			||||||
 | 
					        return iter(self._f) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class KeepOpenFile(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, file): | 
				
			||||||
 | 
					        self._file = file | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, name): | 
				
			||||||
 | 
					        return getattr(self._file, name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __enter__(self): | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, exc_type, exc_value, tb): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self): | 
				
			||||||
 | 
					        return repr(self._file) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self): | 
				
			||||||
 | 
					        return iter(self._file) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def echo(message=None, file=None, nl=True, err=False, color=None): | 
				
			||||||
 | 
					    """Prints a message plus a newline to the given file or stdout.  On | 
				
			||||||
 | 
					    first sight, this looks like the print function, but it has improved | 
				
			||||||
 | 
					    support for handling Unicode and binary data that does not fail no | 
				
			||||||
 | 
					    matter how badly configured the system is. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Primarily it means that you can print binary data as well as Unicode | 
				
			||||||
 | 
					    data on both 2.x and 3.x to the given file in the most appropriate way | 
				
			||||||
 | 
					    possible.  This is a very carefree function as in that it will try its | 
				
			||||||
 | 
					    best to not fail.  As of Click 6.0 this includes support for unicode | 
				
			||||||
 | 
					    output on the Windows console. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    In addition to that, if `colorama`_ is installed, the echo function will | 
				
			||||||
 | 
					    also support clever handling of ANSI codes.  Essentially it will then | 
				
			||||||
 | 
					    do the following: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -   add transparent handling of ANSI color codes on Windows. | 
				
			||||||
 | 
					    -   hide ANSI codes automatically if the destination file is not a | 
				
			||||||
 | 
					        terminal. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. _colorama: http://pypi.python.org/pypi/colorama | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 6.0 | 
				
			||||||
 | 
					       As of Click 6.0 the echo function will properly support unicode | 
				
			||||||
 | 
					       output on the windows console.  Not that click does not modify | 
				
			||||||
 | 
					       the interpreter in any way which means that `sys.stdout` or the | 
				
			||||||
 | 
					       print statement or function will still not provide unicode support. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 2.0 | 
				
			||||||
 | 
					       Starting with version 2.0 of Click, the echo function will work | 
				
			||||||
 | 
					       with colorama if it's installed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 3.0 | 
				
			||||||
 | 
					       The `err` parameter was added. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 4.0 | 
				
			||||||
 | 
					       Added the `color` flag. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param message: the message to print | 
				
			||||||
 | 
					    :param file: the file to write to (defaults to ``stdout``) | 
				
			||||||
 | 
					    :param err: if set to true the file defaults to ``stderr`` instead of | 
				
			||||||
 | 
					                ``stdout``.  This is faster and easier than calling | 
				
			||||||
 | 
					                :func:`get_text_stderr` yourself. | 
				
			||||||
 | 
					    :param nl: if set to `True` (the default) a newline is printed afterwards. | 
				
			||||||
 | 
					    :param color: controls if the terminal supports ANSI colors or not.  The | 
				
			||||||
 | 
					                  default is autodetection. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if file is None: | 
				
			||||||
 | 
					        if err: | 
				
			||||||
 | 
					            file = _default_text_stderr() | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            file = _default_text_stdout() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Convert non bytes/text into the native string type. | 
				
			||||||
 | 
					    if message is not None and not isinstance(message, echo_native_types): | 
				
			||||||
 | 
					        message = text_type(message) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if nl: | 
				
			||||||
 | 
					        message = message or u'' | 
				
			||||||
 | 
					        if isinstance(message, text_type): | 
				
			||||||
 | 
					            message += u'\n' | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            message += b'\n' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # If there is a message, and we're in Python 3, and the value looks | 
				
			||||||
 | 
					    # like bytes, we manually need to find the binary stream and write the | 
				
			||||||
 | 
					    # message in there.  This is done separately so that most stream | 
				
			||||||
 | 
					    # types will work as you would expect.  Eg: you can write to StringIO | 
				
			||||||
 | 
					    # for other cases. | 
				
			||||||
 | 
					    if message and not PY2 and is_bytes(message): | 
				
			||||||
 | 
					        binary_file = _find_binary_writer(file) | 
				
			||||||
 | 
					        if binary_file is not None: | 
				
			||||||
 | 
					            file.flush() | 
				
			||||||
 | 
					            binary_file.write(message) | 
				
			||||||
 | 
					            binary_file.flush() | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # ANSI-style support.  If there is no message or we are dealing with | 
				
			||||||
 | 
					    # bytes nothing is happening.  If we are connected to a file we want | 
				
			||||||
 | 
					    # to strip colors.  If we are on windows we either wrap the stream | 
				
			||||||
 | 
					    # to strip the color or we use the colorama support to translate the | 
				
			||||||
 | 
					    # ansi codes to API calls. | 
				
			||||||
 | 
					    if message and not is_bytes(message): | 
				
			||||||
 | 
					        color = resolve_color_default(color) | 
				
			||||||
 | 
					        if should_strip_ansi(file, color): | 
				
			||||||
 | 
					            message = strip_ansi(message) | 
				
			||||||
 | 
					        elif WIN: | 
				
			||||||
 | 
					            if auto_wrap_for_ansi is not None: | 
				
			||||||
 | 
					                file = auto_wrap_for_ansi(file) | 
				
			||||||
 | 
					            elif not color: | 
				
			||||||
 | 
					                message = strip_ansi(message) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if message: | 
				
			||||||
 | 
					        file.write(message) | 
				
			||||||
 | 
					    file.flush() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_binary_stream(name): | 
				
			||||||
 | 
					    """Returns a system stream for byte processing.  This essentially | 
				
			||||||
 | 
					    returns the stream from the sys module with the given name but it | 
				
			||||||
 | 
					    solves some compatibility issues between different Python versions. | 
				
			||||||
 | 
					    Primarily this function is necessary for getting binary streams on | 
				
			||||||
 | 
					    Python 3. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param name: the name of the stream to open.  Valid names are ``'stdin'``, | 
				
			||||||
 | 
					                 ``'stdout'`` and ``'stderr'`` | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    opener = binary_streams.get(name) | 
				
			||||||
 | 
					    if opener is None: | 
				
			||||||
 | 
					        raise TypeError('Unknown standard stream %r' % name) | 
				
			||||||
 | 
					    return opener() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_text_stream(name, encoding=None, errors='strict'): | 
				
			||||||
 | 
					    """Returns a system stream for text processing.  This usually returns | 
				
			||||||
 | 
					    a wrapped stream around a binary stream returned from | 
				
			||||||
 | 
					    :func:`get_binary_stream` but it also can take shortcuts on Python 3 | 
				
			||||||
 | 
					    for already correctly configured streams. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param name: the name of the stream to open.  Valid names are ``'stdin'``, | 
				
			||||||
 | 
					                 ``'stdout'`` and ``'stderr'`` | 
				
			||||||
 | 
					    :param encoding: overrides the detected default encoding. | 
				
			||||||
 | 
					    :param errors: overrides the default error mode. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    opener = text_streams.get(name) | 
				
			||||||
 | 
					    if opener is None: | 
				
			||||||
 | 
					        raise TypeError('Unknown standard stream %r' % name) | 
				
			||||||
 | 
					    return opener(encoding, errors) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def open_file(filename, mode='r', encoding=None, errors='strict', | 
				
			||||||
 | 
					              lazy=False, atomic=False): | 
				
			||||||
 | 
					    """This is similar to how the :class:`File` works but for manual | 
				
			||||||
 | 
					    usage.  Files are opened non lazy by default.  This can open regular | 
				
			||||||
 | 
					    files as well as stdin/stdout if ``'-'`` is passed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If stdin/stdout is returned the stream is wrapped so that the context | 
				
			||||||
 | 
					    manager will not close the stream accidentally.  This makes it possible | 
				
			||||||
 | 
					    to always use the function like this without having to worry to | 
				
			||||||
 | 
					    accidentally close a standard stream:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with open_file(filename) as f: | 
				
			||||||
 | 
					            ... | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 3.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param filename: the name of the file to open (or ``'-'`` for stdin/stdout). | 
				
			||||||
 | 
					    :param mode: the mode in which to open the file. | 
				
			||||||
 | 
					    :param encoding: the encoding to use. | 
				
			||||||
 | 
					    :param errors: the error handling for this file. | 
				
			||||||
 | 
					    :param lazy: can be flipped to true to open the file lazily. | 
				
			||||||
 | 
					    :param atomic: in atomic mode writes go into a temporary file and it's | 
				
			||||||
 | 
					                   moved on close. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if lazy: | 
				
			||||||
 | 
					        return LazyFile(filename, mode, encoding, errors, atomic=atomic) | 
				
			||||||
 | 
					    f, should_close = open_stream(filename, mode, encoding, errors, | 
				
			||||||
 | 
					                                  atomic=atomic) | 
				
			||||||
 | 
					    if not should_close: | 
				
			||||||
 | 
					        f = KeepOpenFile(f) | 
				
			||||||
 | 
					    return f | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_os_args(): | 
				
			||||||
 | 
					    """This returns the argument part of sys.argv in the most appropriate | 
				
			||||||
 | 
					    form for processing.  What this means is that this return value is in | 
				
			||||||
 | 
					    a format that works for Click to process but does not necessarily | 
				
			||||||
 | 
					    correspond well to what's actually standard for the interpreter. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    On most environments the return value is ``sys.argv[:1]`` unchanged. | 
				
			||||||
 | 
					    However if you are on Windows and running Python 2 the return value | 
				
			||||||
 | 
					    will actually be a list of unicode strings instead because the | 
				
			||||||
 | 
					    default behavior on that platform otherwise will not be able to | 
				
			||||||
 | 
					    carry all possible values that sys.argv can have. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 6.0 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    # We can only extract the unicode argv if sys.argv has not been | 
				
			||||||
 | 
					    # changed since the startup of the application. | 
				
			||||||
 | 
					    if PY2 and WIN and _initial_argv_hash == _hash_py_argv(): | 
				
			||||||
 | 
					        return _get_windows_argv() | 
				
			||||||
 | 
					    return sys.argv[1:] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def format_filename(filename, shorten=False): | 
				
			||||||
 | 
					    """Formats a filename for user display.  The main purpose of this | 
				
			||||||
 | 
					    function is to ensure that the filename can be displayed at all.  This | 
				
			||||||
 | 
					    will decode the filename to unicode if necessary in a way that it will | 
				
			||||||
 | 
					    not fail.  Optionally, it can shorten the filename to not include the | 
				
			||||||
 | 
					    full path to the filename. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param filename: formats a filename for UI display.  This will also convert | 
				
			||||||
 | 
					                     the filename into unicode without failing. | 
				
			||||||
 | 
					    :param shorten: this optionally shortens the filename to strip of the | 
				
			||||||
 | 
					                    path that leads up to it. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if shorten: | 
				
			||||||
 | 
					        filename = os.path.basename(filename) | 
				
			||||||
 | 
					    return filename_to_ui(filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_app_dir(app_name, roaming=True, force_posix=False): | 
				
			||||||
 | 
					    r"""Returns the config folder for the application.  The default behavior | 
				
			||||||
 | 
					    is to return whatever is most appropriate for the operating system. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    To give you an idea, for an app called ``"Foo Bar"``, something like | 
				
			||||||
 | 
					    the following folders could be returned: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Mac OS X: | 
				
			||||||
 | 
					      ``~/Library/Application Support/Foo Bar`` | 
				
			||||||
 | 
					    Mac OS X (POSIX): | 
				
			||||||
 | 
					      ``~/.foo-bar`` | 
				
			||||||
 | 
					    Unix: | 
				
			||||||
 | 
					      ``~/.config/foo-bar`` | 
				
			||||||
 | 
					    Unix (POSIX): | 
				
			||||||
 | 
					      ``~/.foo-bar`` | 
				
			||||||
 | 
					    Win XP (roaming): | 
				
			||||||
 | 
					      ``C:\Documents and Settings\<user>\Local Settings\Application Data\Foo Bar`` | 
				
			||||||
 | 
					    Win XP (not roaming): | 
				
			||||||
 | 
					      ``C:\Documents and Settings\<user>\Application Data\Foo Bar`` | 
				
			||||||
 | 
					    Win 7 (roaming): | 
				
			||||||
 | 
					      ``C:\Users\<user>\AppData\Roaming\Foo Bar`` | 
				
			||||||
 | 
					    Win 7 (not roaming): | 
				
			||||||
 | 
					      ``C:\Users\<user>\AppData\Local\Foo Bar`` | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param app_name: the application name.  This should be properly capitalized | 
				
			||||||
 | 
					                     and can contain whitespace. | 
				
			||||||
 | 
					    :param roaming: controls if the folder should be roaming or not on Windows. | 
				
			||||||
 | 
					                    Has no affect otherwise. | 
				
			||||||
 | 
					    :param force_posix: if this is set to `True` then on any POSIX system the | 
				
			||||||
 | 
					                        folder will be stored in the home folder with a leading | 
				
			||||||
 | 
					                        dot instead of the XDG config home or darwin's | 
				
			||||||
 | 
					                        application support folder. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if WIN: | 
				
			||||||
 | 
					        key = roaming and 'APPDATA' or 'LOCALAPPDATA' | 
				
			||||||
 | 
					        folder = os.environ.get(key) | 
				
			||||||
 | 
					        if folder is None: | 
				
			||||||
 | 
					            folder = os.path.expanduser('~') | 
				
			||||||
 | 
					        return os.path.join(folder, app_name) | 
				
			||||||
 | 
					    if force_posix: | 
				
			||||||
 | 
					        return os.path.join(os.path.expanduser('~/.' + _posixify(app_name))) | 
				
			||||||
 | 
					    if sys.platform == 'darwin': | 
				
			||||||
 | 
					        return os.path.join(os.path.expanduser( | 
				
			||||||
 | 
					            '~/Library/Application Support'), app_name) | 
				
			||||||
 | 
					    return os.path.join( | 
				
			||||||
 | 
					        os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), | 
				
			||||||
 | 
					        _posixify(app_name)) | 
				
			||||||
@ -0,0 +1,49 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask | 
				
			||||||
 | 
					    ~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A microframework based on Werkzeug.  It's extensively documented | 
				
			||||||
 | 
					    and follows best practice patterns. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__version__ = '1.0.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 | 
				
			||||||
@ -0,0 +1,14 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.__main__ | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Alias for flask.run for the command line. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__': | 
				
			||||||
 | 
					    from .cli import main | 
				
			||||||
 | 
					    main(as_module=True) | 
				
			||||||
@ -0,0 +1,99 @@ | 
				
			|||||||
 | 
					# -*- 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: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :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 inspect import getfullargspec as getargspec | 
				
			||||||
 | 
					    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 inspect import getargspec | 
				
			||||||
 | 
					    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 | 
				
			||||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -0,0 +1,448 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.blueprints | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Blueprints are the recommended way to implement larger or more | 
				
			||||||
 | 
					    pluggable applications in Flask 0.7 and later. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					from functools import update_wrapper | 
				
			||||||
 | 
					from werkzeug.urls import url_join | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 is not None: | 
				
			||||||
 | 
					            if rule: | 
				
			||||||
 | 
					                rule = '/'.join(( | 
				
			||||||
 | 
					                    self.url_prefix.rstrip('/'), rule.lstrip('/'))) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                rule = self.url_prefix | 
				
			||||||
 | 
					        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 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: Blueprint local JSON decoder class to use. | 
				
			||||||
 | 
					    #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`. | 
				
			||||||
 | 
					    json_encoder = None | 
				
			||||||
 | 
					    #: Blueprint local JSON decoder class to use. | 
				
			||||||
 | 
					    #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`. | 
				
			||||||
 | 
					    json_decoder = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # TODO remove the next three attrs when Sphinx :inherited-members: works | 
				
			||||||
 | 
					    # https://github.com/sphinx-doc/sphinx/issues/741 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: The name of the package or module that this app belongs to. Do not | 
				
			||||||
 | 
					    #: change this once it is set by the constructor. | 
				
			||||||
 | 
					    import_name = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: Location of the template files to be added to the template lookup. | 
				
			||||||
 | 
					    #: ``None`` if templates should not be added. | 
				
			||||||
 | 
					    template_folder = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: Absolute path to the package on the filesystem. Used to look up | 
				
			||||||
 | 
					    #: resources contained in the package. | 
				
			||||||
 | 
					    root_path = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 all views | 
				
			||||||
 | 
					        and callbacks registered on the blueprint with the application. Creates | 
				
			||||||
 | 
					        a :class:`.BlueprintSetupState` and calls each :meth:`record` callback | 
				
			||||||
 | 
					        with it. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param app: The application this blueprint is being registered with. | 
				
			||||||
 | 
					        :param options: Keyword arguments forwarded from | 
				
			||||||
 | 
					            :meth:`~Flask.register_blueprint`. | 
				
			||||||
 | 
					        :param first_registration: Whether this is the first time this | 
				
			||||||
 | 
					            blueprint has been registered on the application. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        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 + '/<path:filename>', | 
				
			||||||
 | 
					                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" | 
				
			||||||
 | 
					        if view_func and hasattr(view_func, '__name__'): | 
				
			||||||
 | 
					            assert '.' not in view_func.__name__, "Blueprint view function name 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)) | 
				
			||||||
@ -0,0 +1,898 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.cli | 
				
			||||||
 | 
					    ~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A simple command line application to run flask apps. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from __future__ import print_function | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ast | 
				
			||||||
 | 
					import inspect | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import re | 
				
			||||||
 | 
					import ssl | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import traceback | 
				
			||||||
 | 
					from functools import update_wrapper | 
				
			||||||
 | 
					from operator import attrgetter | 
				
			||||||
 | 
					from threading import Lock, Thread | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import click | 
				
			||||||
 | 
					from werkzeug.utils import import_string | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import __version__ | 
				
			||||||
 | 
					from ._compat import getargspec, iteritems, reraise, text_type | 
				
			||||||
 | 
					from .globals import current_app | 
				
			||||||
 | 
					from .helpers import get_debug_flag, get_env, get_load_dotenv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    import dotenv | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    dotenv = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NoAppException(click.UsageError): | 
				
			||||||
 | 
					    """Raised if an application cannot be found or loaded.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def find_best_app(script_info, 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 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] | 
				
			||||||
 | 
					    elif len(matches) > 1: | 
				
			||||||
 | 
					        raise NoAppException( | 
				
			||||||
 | 
					            'Detected multiple Flask applications in module "{module}". Use ' | 
				
			||||||
 | 
					            '"FLASK_APP={module}:name" to specify the correct ' | 
				
			||||||
 | 
					            'one.'.format(module=module.__name__) | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Search for app factory functions. | 
				
			||||||
 | 
					    for attr_name in ('create_app', 'make_app'): | 
				
			||||||
 | 
					        app_factory = getattr(module, attr_name, None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if inspect.isfunction(app_factory): | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                app = call_factory(script_info, app_factory) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if isinstance(app, Flask): | 
				
			||||||
 | 
					                    return app | 
				
			||||||
 | 
					            except TypeError: | 
				
			||||||
 | 
					                if not _called_with_wrong_args(app_factory): | 
				
			||||||
 | 
					                    raise | 
				
			||||||
 | 
					                raise NoAppException( | 
				
			||||||
 | 
					                    'Detected factory "{factory}" in module "{module}", but ' | 
				
			||||||
 | 
					                    'could not call it without arguments. Use ' | 
				
			||||||
 | 
					                    '"FLASK_APP=\'{module}:{factory}(args)\'" to specify ' | 
				
			||||||
 | 
					                    'arguments.'.format( | 
				
			||||||
 | 
					                        factory=attr_name, module=module.__name__ | 
				
			||||||
 | 
					                    ) | 
				
			||||||
 | 
					                ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    raise NoAppException( | 
				
			||||||
 | 
					        'Failed to find Flask application or factory in module "{module}". ' | 
				
			||||||
 | 
					        'Use "FLASK_APP={module}:name to specify one.'.format( | 
				
			||||||
 | 
					            module=module.__name__ | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def call_factory(script_info, app_factory, arguments=()): | 
				
			||||||
 | 
					    """Takes an app factory, a ``script_info` object and  optionally a tuple | 
				
			||||||
 | 
					    of arguments. Checks for the existence of a script_info argument and calls | 
				
			||||||
 | 
					    the app_factory depending on that and the arguments provided. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    args_spec = getargspec(app_factory) | 
				
			||||||
 | 
					    arg_names = args_spec.args | 
				
			||||||
 | 
					    arg_defaults = args_spec.defaults | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if 'script_info' in arg_names: | 
				
			||||||
 | 
					        return app_factory(*arguments, script_info=script_info) | 
				
			||||||
 | 
					    elif arguments: | 
				
			||||||
 | 
					        return app_factory(*arguments) | 
				
			||||||
 | 
					    elif not arguments and len(arg_names) == 1 and arg_defaults is None: | 
				
			||||||
 | 
					        return app_factory(script_info) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return app_factory() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _called_with_wrong_args(factory): | 
				
			||||||
 | 
					    """Check whether calling a function raised a ``TypeError`` because | 
				
			||||||
 | 
					    the call failed or because something in the factory raised the | 
				
			||||||
 | 
					    error. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param factory: the factory function that was called | 
				
			||||||
 | 
					    :return: true if the call failed | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    tb = sys.exc_info()[2] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        while tb is not None: | 
				
			||||||
 | 
					            if tb.tb_frame.f_code is factory.__code__: | 
				
			||||||
 | 
					                # in the factory, it was called successfully | 
				
			||||||
 | 
					                return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tb = tb.tb_next | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # didn't reach the factory | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					    finally: | 
				
			||||||
 | 
					        del tb | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def find_app_by_string(script_info, module, app_name): | 
				
			||||||
 | 
					    """Checks if the given string is a variable name or a function. If it is a | 
				
			||||||
 | 
					    function, it checks for specified arguments and whether it takes a | 
				
			||||||
 | 
					    ``script_info`` argument and calls the function with the appropriate | 
				
			||||||
 | 
					    arguments. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    from flask import Flask | 
				
			||||||
 | 
					    match = re.match(r'^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$', app_name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not match: | 
				
			||||||
 | 
					        raise NoAppException( | 
				
			||||||
 | 
					            '"{name}" is not a valid variable name or function ' | 
				
			||||||
 | 
					            'expression.'.format(name=app_name) | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name, args = match.groups() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        attr = getattr(module, name) | 
				
			||||||
 | 
					    except AttributeError as e: | 
				
			||||||
 | 
					        raise NoAppException(e.args[0]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if inspect.isfunction(attr): | 
				
			||||||
 | 
					        if args: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                args = ast.literal_eval('({args},)'.format(args=args)) | 
				
			||||||
 | 
					            except (ValueError, SyntaxError)as e: | 
				
			||||||
 | 
					                raise NoAppException( | 
				
			||||||
 | 
					                    'Could not parse the arguments in ' | 
				
			||||||
 | 
					                    '"{app_name}".'.format(e=e, app_name=app_name) | 
				
			||||||
 | 
					                ) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            args = () | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            app = call_factory(script_info, attr, args) | 
				
			||||||
 | 
					        except TypeError as e: | 
				
			||||||
 | 
					            if not _called_with_wrong_args(attr): | 
				
			||||||
 | 
					                raise | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            raise NoAppException( | 
				
			||||||
 | 
					                '{e}\nThe factory "{app_name}" in module "{module}" could not ' | 
				
			||||||
 | 
					                'be called with the specified arguments.'.format( | 
				
			||||||
 | 
					                    e=e, app_name=app_name, module=module.__name__ | 
				
			||||||
 | 
					                ) | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        app = attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if isinstance(app, Flask): | 
				
			||||||
 | 
					        return app | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    raise NoAppException( | 
				
			||||||
 | 
					        'A valid Flask application was not obtained from ' | 
				
			||||||
 | 
					        '"{module}:{app_name}".'.format( | 
				
			||||||
 | 
					            module=module.__name__, app_name=app_name | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def prepare_import(path): | 
				
			||||||
 | 
					    """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. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    path = os.path.realpath(path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if os.path.splitext(path)[1] == '.py': | 
				
			||||||
 | 
					        path = os.path.splitext(path)[0] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if os.path.basename(path) == '__init__': | 
				
			||||||
 | 
					        path = os.path.dirname(path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    module_name = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # move up until outside package structure (no __init__.py) | 
				
			||||||
 | 
					    while True: | 
				
			||||||
 | 
					        path, name = os.path.split(path) | 
				
			||||||
 | 
					        module_name.append(name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not os.path.exists(os.path.join(path, '__init__.py')): | 
				
			||||||
 | 
					            break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sys.path[0] != path: | 
				
			||||||
 | 
					        sys.path.insert(0, path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return '.'.join(module_name[::-1]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def locate_app(script_info, module_name, app_name, raise_if_not_found=True): | 
				
			||||||
 | 
					    __traceback_hide__ = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        __import__(module_name) | 
				
			||||||
 | 
					    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 NoAppException( | 
				
			||||||
 | 
					                'While importing "{name}", an ImportError was raised:' | 
				
			||||||
 | 
					                '\n\n{tb}'.format(name=module_name, tb=traceback.format_exc()) | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					        elif raise_if_not_found: | 
				
			||||||
 | 
					            raise NoAppException( | 
				
			||||||
 | 
					                'Could not import "{name}".'.format(name=module_name) | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    module = sys.modules[module_name] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if app_name is None: | 
				
			||||||
 | 
					        return find_best_app(script_info, module) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        return find_app_by_string(script_info, module, app_name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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): | 
				
			||||||
 | 
					        #: Optionally the import path for the Flask application. | 
				
			||||||
 | 
					        self.app_import_path = app_import_path or os.environ.get('FLASK_APP') | 
				
			||||||
 | 
					        #: 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 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.create_app is not None: | 
				
			||||||
 | 
					            app = call_factory(self, self.create_app) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            if self.app_import_path: | 
				
			||||||
 | 
					                path, name = (self.app_import_path.split(':', 1) + [None])[:2] | 
				
			||||||
 | 
					                import_name = prepare_import(path) | 
				
			||||||
 | 
					                app = locate_app(self, import_name, name) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                for path in ('wsgi.py', 'app.py'): | 
				
			||||||
 | 
					                    import_name = prepare_import(path) | 
				
			||||||
 | 
					                    app = locate_app(self, import_name, None, | 
				
			||||||
 | 
					                                     raise_if_not_found=False) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if app: | 
				
			||||||
 | 
					                        break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not app: | 
				
			||||||
 | 
					            raise NoAppException( | 
				
			||||||
 | 
					                'Could not locate a Flask application. You did not provide ' | 
				
			||||||
 | 
					                'the "FLASK_APP" environment variable, and a "wsgi.py" or ' | 
				
			||||||
 | 
					                '"app.py" module was not found in the current directory.' | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug = get_debug_flag() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Update the app's debug flag through the descriptor so that other | 
				
			||||||
 | 
					        # values repopulate as well. | 
				
			||||||
 | 
					        if debug is not None: | 
				
			||||||
 | 
					            app.debug = debug | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._loaded_app = app | 
				
			||||||
 | 
					        return app | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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. | 
				
			||||||
 | 
					    :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` | 
				
			||||||
 | 
					        files to set environment variables. Will also change the working | 
				
			||||||
 | 
					        directory to the directory containing the first file found. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 1.0 | 
				
			||||||
 | 
					        If installed, python-dotenv will be used to load environment variables | 
				
			||||||
 | 
					        from :file:`.env` and :file:`.flaskenv` files. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, add_default_commands=True, create_app=None, | 
				
			||||||
 | 
					                 add_version_option=True, load_dotenv=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 | 
				
			||||||
 | 
					        self.load_dotenv = load_dotenv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if add_default_commands: | 
				
			||||||
 | 
					            self.add_command(run_command) | 
				
			||||||
 | 
					            self.add_command(shell_command) | 
				
			||||||
 | 
					            self.add_command(routes_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. | 
				
			||||||
 | 
					            # However, we will not do so silently because that would confuse | 
				
			||||||
 | 
					            # users. | 
				
			||||||
 | 
					            traceback.print_exc() | 
				
			||||||
 | 
					        return sorted(rv) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def main(self, *args, **kwargs): | 
				
			||||||
 | 
					        # Set a global flag that indicates that we were invoked from the | 
				
			||||||
 | 
					        # command line interface. This is detected by Flask.run to make the | 
				
			||||||
 | 
					        # call into a no-op. This is necessary to avoid ugly errors when the | 
				
			||||||
 | 
					        # script that is loaded here also attempts to start a server. | 
				
			||||||
 | 
					        os.environ['FLASK_RUN_FROM_CLI'] = 'true' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if get_load_dotenv(self.load_dotenv): | 
				
			||||||
 | 
					            load_dotenv() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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 super(FlaskGroup, self).main(*args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _path_is_ancestor(path, other): | 
				
			||||||
 | 
					    """Take ``other`` and remove the length of ``path`` from it. Then join it | 
				
			||||||
 | 
					    to ``path``. If it is the original value, ``path`` is an ancestor of | 
				
			||||||
 | 
					    ``other``.""" | 
				
			||||||
 | 
					    return os.path.join(path, other[len(path):].lstrip(os.sep)) == other | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def load_dotenv(path=None): | 
				
			||||||
 | 
					    """Load "dotenv" files in order of precedence to set environment variables. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If an env var is already set it is not overwritten, so earlier files in the | 
				
			||||||
 | 
					    list are preferred over later files. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Changes the current working directory to the location of the first file | 
				
			||||||
 | 
					    found, with the assumption that it is in the top level project directory | 
				
			||||||
 | 
					    and will be where the Python path should import local packages from. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This is a no-op if `python-dotenv`_ is not installed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param path: Load the file at this location instead of searching. | 
				
			||||||
 | 
					    :return: ``True`` if a file was loaded. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 1.0 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if dotenv is None: | 
				
			||||||
 | 
					        if path or os.path.exists('.env') or os.path.exists('.flaskenv'): | 
				
			||||||
 | 
					            click.secho( | 
				
			||||||
 | 
					                ' * Tip: There are .env files present.' | 
				
			||||||
 | 
					                ' Do "pip install python-dotenv" to use them.', | 
				
			||||||
 | 
					                fg='yellow') | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if path is not None: | 
				
			||||||
 | 
					        return dotenv.load_dotenv(path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    new_dir = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for name in ('.env', '.flaskenv'): | 
				
			||||||
 | 
					        path = dotenv.find_dotenv(name, usecwd=True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not path: | 
				
			||||||
 | 
					            continue | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if new_dir is None: | 
				
			||||||
 | 
					            new_dir = os.path.dirname(path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dotenv.load_dotenv(path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if new_dir and os.getcwd() != new_dir: | 
				
			||||||
 | 
					        os.chdir(new_dir) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return new_dir is not None  # at least one file was located and loaded | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def show_server_banner(env, debug, app_import_path, eager_loading): | 
				
			||||||
 | 
					    """Show extra startup messages the first time the server is run, | 
				
			||||||
 | 
					    ignoring the reloader. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if app_import_path is not None: | 
				
			||||||
 | 
					        message = ' * Serving Flask app "{0}"'.format(app_import_path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not eager_loading: | 
				
			||||||
 | 
					            message += ' (lazy loading)' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        click.echo(message) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    click.echo(' * Environment: {0}'.format(env)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if env == 'production': | 
				
			||||||
 | 
					        click.secho( | 
				
			||||||
 | 
					            '   WARNING: Do not use the development server in a production' | 
				
			||||||
 | 
					            ' environment.', fg='red') | 
				
			||||||
 | 
					        click.secho('   Use a production WSGI server instead.', dim=True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if debug is not None: | 
				
			||||||
 | 
					        click.echo(' * Debug mode: {0}'.format('on' if debug else 'off')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CertParamType(click.ParamType): | 
				
			||||||
 | 
					    """Click option type for the ``--cert`` option. Allows either an | 
				
			||||||
 | 
					    existing file, the string ``'adhoc'``, or an import for a | 
				
			||||||
 | 
					    :class:`~ssl.SSLContext` object. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = 'path' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self): | 
				
			||||||
 | 
					        self.path_type = click.Path( | 
				
			||||||
 | 
					            exists=True, dir_okay=False, resolve_path=True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def convert(self, value, param, ctx): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return self.path_type(value, param, ctx) | 
				
			||||||
 | 
					        except click.BadParameter: | 
				
			||||||
 | 
					            value = click.STRING(value, param, ctx).lower() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if value == 'adhoc': | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    import OpenSSL | 
				
			||||||
 | 
					                except ImportError: | 
				
			||||||
 | 
					                    raise click.BadParameter( | 
				
			||||||
 | 
					                        'Using ad-hoc certificates requires pyOpenSSL.', | 
				
			||||||
 | 
					                        ctx, param) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            obj = import_string(value, silent=True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if sys.version_info < (2, 7): | 
				
			||||||
 | 
					                if obj: | 
				
			||||||
 | 
					                    return obj | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                if isinstance(obj, ssl.SSLContext): | 
				
			||||||
 | 
					                    return obj | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            raise | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _validate_key(ctx, param, value): | 
				
			||||||
 | 
					    """The ``--key`` option must be specified when ``--cert`` is a file. | 
				
			||||||
 | 
					    Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    cert = ctx.params.get('cert') | 
				
			||||||
 | 
					    is_adhoc = cert == 'adhoc' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sys.version_info < (2, 7): | 
				
			||||||
 | 
					        is_context = cert and not isinstance(cert, (text_type, bytes)) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        is_context = isinstance(cert, ssl.SSLContext) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if value is not None: | 
				
			||||||
 | 
					        if is_adhoc: | 
				
			||||||
 | 
					            raise click.BadParameter( | 
				
			||||||
 | 
					                'When "--cert" is "adhoc", "--key" is not used.', | 
				
			||||||
 | 
					                ctx, param) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if is_context: | 
				
			||||||
 | 
					            raise click.BadParameter( | 
				
			||||||
 | 
					                'When "--cert" is an SSLContext object, "--key is not used.', | 
				
			||||||
 | 
					                ctx, param) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not cert: | 
				
			||||||
 | 
					            raise click.BadParameter( | 
				
			||||||
 | 
					                '"--cert" must also be specified.', | 
				
			||||||
 | 
					                ctx, param) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ctx.params['cert'] = cert, value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        if cert and not (is_adhoc or is_context): | 
				
			||||||
 | 
					            raise click.BadParameter( | 
				
			||||||
 | 
					                'Required when using "--cert".', | 
				
			||||||
 | 
					                ctx, param) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@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('--cert', type=CertParamType(), | 
				
			||||||
 | 
					              help='Specify a certificate file to use HTTPS.') | 
				
			||||||
 | 
					@click.option('--key', | 
				
			||||||
 | 
					              type=click.Path(exists=True, dir_okay=False, resolve_path=True), | 
				
			||||||
 | 
					              callback=_validate_key, expose_value=False, | 
				
			||||||
 | 
					              help='The key file to use when specifying a certificate.') | 
				
			||||||
 | 
					@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=True, | 
				
			||||||
 | 
					              help='Enable or disable multithreading.') | 
				
			||||||
 | 
					@pass_script_info | 
				
			||||||
 | 
					def run_command(info, host, port, reload, debugger, eager_loading, | 
				
			||||||
 | 
					                with_threads, cert): | 
				
			||||||
 | 
					    """Run a local development server. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This server is for development purposes only. It does not provide | 
				
			||||||
 | 
					    the stability, security, or performance of production WSGI servers. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The reloader and debugger are enabled by default if | 
				
			||||||
 | 
					    FLASK_ENV=development or FLASK_DEBUG=1. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    debug = get_debug_flag() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if reload is None: | 
				
			||||||
 | 
					        reload = debug | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if debugger is None: | 
				
			||||||
 | 
					        debugger = debug | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if eager_loading is None: | 
				
			||||||
 | 
					        eager_loading = not reload | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    show_server_banner(get_env(), debug, info.app_import_path, eager_loading) | 
				
			||||||
 | 
					    app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from werkzeug.serving import run_simple | 
				
			||||||
 | 
					    run_simple(host, port, app, use_reloader=reload, use_debugger=debugger, | 
				
			||||||
 | 
					               threaded=with_threads, ssl_context=cert) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@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 configure 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.env, | 
				
			||||||
 | 
					        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) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@click.command('routes', short_help='Show the routes for the app.') | 
				
			||||||
 | 
					@click.option( | 
				
			||||||
 | 
					    '--sort', '-s', | 
				
			||||||
 | 
					    type=click.Choice(('endpoint', 'methods', 'rule', 'match')), | 
				
			||||||
 | 
					    default='endpoint', | 
				
			||||||
 | 
					    help=( | 
				
			||||||
 | 
					        'Method to sort routes by. "match" is the order that Flask will match ' | 
				
			||||||
 | 
					        'routes when dispatching a request.' | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					@click.option( | 
				
			||||||
 | 
					    '--all-methods', | 
				
			||||||
 | 
					    is_flag=True, | 
				
			||||||
 | 
					    help="Show HEAD and OPTIONS methods." | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					@with_appcontext | 
				
			||||||
 | 
					def routes_command(sort, all_methods): | 
				
			||||||
 | 
					    """Show all registered routes with endpoints and methods.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rules = list(current_app.url_map.iter_rules()) | 
				
			||||||
 | 
					    if not rules: | 
				
			||||||
 | 
					        click.echo('No routes were registered.') | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sort in ('endpoint', 'rule'): | 
				
			||||||
 | 
					        rules = sorted(rules, key=attrgetter(sort)) | 
				
			||||||
 | 
					    elif sort == 'methods': | 
				
			||||||
 | 
					        rules = sorted(rules, key=lambda rule: sorted(rule.methods)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rule_methods = [ | 
				
			||||||
 | 
					        ', '.join(sorted(rule.methods - ignored_methods)) for rule in rules | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    headers = ('Endpoint', 'Methods', 'Rule') | 
				
			||||||
 | 
					    widths = ( | 
				
			||||||
 | 
					        max(len(rule.endpoint) for rule in rules), | 
				
			||||||
 | 
					        max(len(methods) for methods in rule_methods), | 
				
			||||||
 | 
					        max(len(rule.rule) for rule in rules), | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					    widths = [max(len(h), w) for h, w in zip(headers, widths)] | 
				
			||||||
 | 
					    row = '{{0:<{0}}}  {{1:<{1}}}  {{2:<{2}}}'.format(*widths) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    click.echo(row.format(*headers).strip()) | 
				
			||||||
 | 
					    click.echo(row.format(*('-' * width for width in widths))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for rule, methods in zip(rules, rule_methods): | 
				
			||||||
 | 
					        click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cli = FlaskGroup(help="""\ | 
				
			||||||
 | 
					A general utility script for Flask applications. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Provides commands from Flask, extensions, and the application. Loads the | 
				
			||||||
 | 
					application defined in the FLASK_APP environment variable, or from a wsgi.py | 
				
			||||||
 | 
					file. Setting the FLASK_ENV environment variable to 'development' will enable | 
				
			||||||
 | 
					debug mode. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					\b | 
				
			||||||
 | 
					  {prefix}{cmd} FLASK_APP=hello.py | 
				
			||||||
 | 
					  {prefix}{cmd} FLASK_ENV=development | 
				
			||||||
 | 
					  {prefix}flask run | 
				
			||||||
 | 
					""".format( | 
				
			||||||
 | 
					    cmd='export' if os.name == 'posix' else 'set', | 
				
			||||||
 | 
					    prefix='$ ' if os.name == 'posix' else '> ' | 
				
			||||||
 | 
					)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main(as_module=False): | 
				
			||||||
 | 
					    args = sys.argv[1:] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if as_module: | 
				
			||||||
 | 
					        this_module = 'flask' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if sys.version_info < (2, 7): | 
				
			||||||
 | 
					            this_module += '.cli' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        name = 'python -m ' + this_module | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Python rewrites "python -m flask" to the path to the file in argv. | 
				
			||||||
 | 
					        # Restore the original command so that the reloader works. | 
				
			||||||
 | 
					        sys.argv = ['-m', this_module] + args | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        name = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cli.main(args=args, prog_name=name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__': | 
				
			||||||
 | 
					    main(as_module=True) | 
				
			||||||
@ -0,0 +1,265 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.config | 
				
			||||||
 | 
					    ~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Implements the configuration related objects. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :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, errno.ENOTDIR | 
				
			||||||
 | 
					            ): | 
				
			||||||
 | 
					                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)) | 
				
			||||||
@ -0,0 +1,457 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.ctx | 
				
			||||||
 | 
					    ~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Implements the objects required to keep the context. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :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. Used as a namespace for storing data during an | 
				
			||||||
 | 
					    application context. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Creating an app context automatically creates this object, which is | 
				
			||||||
 | 
					    made available as the :data:`g` proxy. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. describe:: 'key' in g | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Check whether an attribute is present. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 0.10 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. describe:: iter(g) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Return an iterator over the attribute names. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 0.10 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, name, default=None): | 
				
			||||||
 | 
					        """Get an attribute by name, or a default value. Like | 
				
			||||||
 | 
					        :meth:`dict.get`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param name: Name of attribute to get. | 
				
			||||||
 | 
					        :param default: Value to return if the attribute is not present. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 0.10 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return self.__dict__.get(name, default) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def pop(self, name, default=_sentinel): | 
				
			||||||
 | 
					        """Get and remove an attribute by name. Like :meth:`dict.pop`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param name: Name of attribute to pop. | 
				
			||||||
 | 
					        :param default: Value to return if the attribute is not present, | 
				
			||||||
 | 
					            instead of raise a ``KeyError``. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 0.11 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if default is _sentinel: | 
				
			||||||
 | 
					            return self.__dict__.pop(name) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            return self.__dict__.pop(name, default) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setdefault(self, name, default=None): | 
				
			||||||
 | 
					        """Get the value of an attribute if it is present, otherwise | 
				
			||||||
 | 
					        set and return a default value. Like :meth:`dict.setdefault`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param name: Name of attribute to get. | 
				
			||||||
 | 
					        :param: default: Value to set and return if the attribute is not | 
				
			||||||
 | 
					            present. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 0.11 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        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 '<flask.g of %r>' % 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. | 
				
			||||||
 | 
					        # Only open a new session if this is the first time the request was | 
				
			||||||
 | 
					        # pushed, otherwise stream_with_context loses the session. | 
				
			||||||
 | 
					        if self.session is None: | 
				
			||||||
 | 
					            session_interface = self.app.session_interface | 
				
			||||||
 | 
					            self.session = session_interface.open_session( | 
				
			||||||
 | 
					                self.app, self.request | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if self.session is None: | 
				
			||||||
 | 
					                self.session = session_interface.make_null_session(self.app) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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, | 
				
			||||||
 | 
					        ) | 
				
			||||||
@ -0,0 +1,168 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.debughelpers | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Various helpers to make the development experience better. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					from warnings import warn | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 '<string>') | 
				
			||||||
 | 
					            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)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def explain_ignored_app_run(): | 
				
			||||||
 | 
					    if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': | 
				
			||||||
 | 
					        warn(Warning('Silently ignoring app.run() because the ' | 
				
			||||||
 | 
					                     'application is run from the flask command line ' | 
				
			||||||
 | 
					                     'executable.  Consider putting app.run() behind an ' | 
				
			||||||
 | 
					                     'if __name__ == "__main__" guard to silence this ' | 
				
			||||||
 | 
					                     'warning.'), stacklevel=3) | 
				
			||||||
@ -0,0 +1,61 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.globals | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Defines all the global objects that are proxies to the current | 
				
			||||||
 | 
					    active context. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :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 some 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')) | 
				
			||||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -0,0 +1,327 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					flask.json | 
				
			||||||
 | 
					~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					:license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					import codecs | 
				
			||||||
 | 
					import io | 
				
			||||||
 | 
					import uuid | 
				
			||||||
 | 
					from datetime import date, datetime | 
				
			||||||
 | 
					from flask.globals import current_app, request | 
				
			||||||
 | 
					from flask._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, datetime): | 
				
			||||||
 | 
					            return http_date(o.utctimetuple()) | 
				
			||||||
 | 
					        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: | 
				
			||||||
 | 
					        bp = current_app.blueprints.get(request.blueprint) if request else None | 
				
			||||||
 | 
					        kwargs.setdefault( | 
				
			||||||
 | 
					            'cls', | 
				
			||||||
 | 
					            bp.json_encoder if bp and bp.json_encoder | 
				
			||||||
 | 
					                else 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: | 
				
			||||||
 | 
					        bp = current_app.blueprints.get(request.blueprint) if request else None | 
				
			||||||
 | 
					        kwargs.setdefault( | 
				
			||||||
 | 
					            'cls', | 
				
			||||||
 | 
					            bp.json_decoder if bp and bp.json_decoder | 
				
			||||||
 | 
					                else current_app.json_decoder | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        kwargs.setdefault('cls', JSONDecoder) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def detect_encoding(data): | 
				
			||||||
 | 
					    """Detect which UTF codec was used to encode the given bytes. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The latest JSON standard (:rfc:`8259`) suggests that only UTF-8 is | 
				
			||||||
 | 
					    accepted. Older documents allowed 8, 16, or 32. 16 and 32 can be big | 
				
			||||||
 | 
					    or little endian. Some editors or libraries may prepend a BOM. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param data: Bytes in unknown UTF encoding. | 
				
			||||||
 | 
					    :return: UTF encoding name | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    head = data[:4] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if head[:3] == codecs.BOM_UTF8: | 
				
			||||||
 | 
					        return 'utf-8-sig' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if b'\x00' not in head: | 
				
			||||||
 | 
					        return 'utf-8' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if head in (codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE): | 
				
			||||||
 | 
					        return 'utf-32' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if head[:2] in (codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE): | 
				
			||||||
 | 
					        return 'utf-16' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if len(head) == 4: | 
				
			||||||
 | 
					        if head[:3] == b'\x00\x00\x00': | 
				
			||||||
 | 
					            return 'utf-32-be' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if head[::2] == b'\x00\x00': | 
				
			||||||
 | 
					            return 'utf-16-be' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if head[1:] == b'\x00\x00\x00': | 
				
			||||||
 | 
					            return 'utf-32-le' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if head[1::2] == b'\x00\x00': | 
				
			||||||
 | 
					            return 'utf-16-le' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if len(head) == 2: | 
				
			||||||
 | 
					        return 'utf-16-be' if head.startswith(b'\x00') else 'utf-16-le' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 'utf-8' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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): | 
				
			||||||
 | 
					        encoding = kwargs.pop('encoding', None) | 
				
			||||||
 | 
					        if encoding is None: | 
				
			||||||
 | 
					            encoding = detect_encoding(s) | 
				
			||||||
 | 
					        s = s.decode(encoding) | 
				
			||||||
 | 
					    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 ``<script>`` | 
				
			||||||
 | 
					    tags.  It accepts the same arguments and returns a JSON string.  Note that | 
				
			||||||
 | 
					    this is available in templates through the ``|tojson`` filter which will | 
				
			||||||
 | 
					    also mark the result as safe.  Due to how this function escapes certain | 
				
			||||||
 | 
					    characters this is safe even if used outside of ``<script>`` tags. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The following characters are escaped in strings: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -   ``<`` | 
				
			||||||
 | 
					    -   ``>`` | 
				
			||||||
 | 
					    -   ``&`` | 
				
			||||||
 | 
					    -   ``'`` | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This makes it safe to embed such strings in any place in HTML with the | 
				
			||||||
 | 
					    notable exception of double quoted attributes.  In that case single | 
				
			||||||
 | 
					    quote your attributes or HTML escape it in addition. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 0.10 | 
				
			||||||
 | 
					       This function's return value is now always safe for HTML usage, even | 
				
			||||||
 | 
					       if outside of script tags or if used in XHTML.  This rule does not | 
				
			||||||
 | 
					       hold true when using this function in HTML attributes that are double | 
				
			||||||
 | 
					       quoted.  Always single quote attributes if you use the ``|tojson`` | 
				
			||||||
 | 
					       filter.  Alternatively use ``|tojson|forceescape``. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    rv = dumps(obj, **kwargs) \ | 
				
			||||||
 | 
					        .replace(u'<', u'\\u003c') \ | 
				
			||||||
 | 
					        .replace(u'>', u'\\u003e') \ | 
				
			||||||
 | 
					        .replace(u'&', u'\\u0026') \ | 
				
			||||||
 | 
					        .replace(u"'", u'\\u0027') | 
				
			||||||
 | 
					    if not _slash_escape: | 
				
			||||||
 | 
					        rv = rv.replace('\\/', '/') | 
				
			||||||
 | 
					    return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def htmlsafe_dump(obj, fp, **kwargs): | 
				
			||||||
 | 
					    """Like :func:`htmlsafe_dumps` but writes into a file object.""" | 
				
			||||||
 | 
					    fp.write(text_type(htmlsafe_dumps(obj, **kwargs))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def jsonify(*args, **kwargs): | 
				
			||||||
 | 
					    """This function wraps :func:`dumps` to add a few enhancements that make | 
				
			||||||
 | 
					    life easier.  It turns the JSON output into a :class:`~flask.Response` | 
				
			||||||
 | 
					    object with the :mimetype:`application/json` mimetype.  For convenience, it | 
				
			||||||
 | 
					    also converts multiple arguments into an array or multiple keyword arguments | 
				
			||||||
 | 
					    into a dict.  This means that both ``jsonify(1,2,3)`` and | 
				
			||||||
 | 
					    ``jsonify([1,2,3])`` serialize to ``[1,2,3]``. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    For clarity, the JSON serialization behavior has the following differences | 
				
			||||||
 | 
					    from :func:`dumps`: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    1. Single argument: Passed straight through to :func:`dumps`. | 
				
			||||||
 | 
					    2. Multiple arguments: Converted to an array before being passed to | 
				
			||||||
 | 
					       :func:`dumps`. | 
				
			||||||
 | 
					    3. Multiple keyword arguments: Converted to a dict before being passed to | 
				
			||||||
 | 
					       :func:`dumps`. | 
				
			||||||
 | 
					    4. Both args and kwargs: Behavior undefined and will throw an exception. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Example usage:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from flask import jsonify | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @app.route('/_get_current_user') | 
				
			||||||
 | 
					        def get_current_user(): | 
				
			||||||
 | 
					            return jsonify(username=g.user.username, | 
				
			||||||
 | 
					                           email=g.user.email, | 
				
			||||||
 | 
					                           id=g.user.id) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This will send a JSON response like this to the browser:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        { | 
				
			||||||
 | 
					            "username": "admin", | 
				
			||||||
 | 
					            "email": "admin@localhost", | 
				
			||||||
 | 
					            "id": 42 | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 0.11 | 
				
			||||||
 | 
					       Added support for serializing top-level arrays. This introduces a | 
				
			||||||
 | 
					       security risk in ancient browsers. See :ref:`json-security` for details. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This function's response will be pretty printed if the | 
				
			||||||
 | 
					    ``JSONIFY_PRETTYPRINT_REGULAR`` config parameter is set to True or the | 
				
			||||||
 | 
					    Flask app is running in debug mode. Compressed (not pretty) formatting | 
				
			||||||
 | 
					    currently means no indents and no spaces after separators. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 0.2 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    indent = None | 
				
			||||||
 | 
					    separators = (',', ':') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug: | 
				
			||||||
 | 
					        indent = 2 | 
				
			||||||
 | 
					        separators = (', ', ': ') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if args and kwargs: | 
				
			||||||
 | 
					        raise TypeError('jsonify() behavior undefined when passed both args and kwargs') | 
				
			||||||
 | 
					    elif len(args) == 1:  # single args are passed directly to dumps() | 
				
			||||||
 | 
					        data = args[0] | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        data = args or kwargs | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return current_app.response_class( | 
				
			||||||
 | 
					        dumps(data, indent=indent, separators=separators) + '\n', | 
				
			||||||
 | 
					        mimetype=current_app.config['JSONIFY_MIMETYPE'] | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def tojson_filter(obj, **kwargs): | 
				
			||||||
 | 
					    return Markup(htmlsafe_dumps(obj, **kwargs)) | 
				
			||||||
@ -0,0 +1,300 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					Tagged JSON | 
				
			||||||
 | 
					~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A compact representation for lossless serialization of non-standard JSON types. | 
				
			||||||
 | 
					:class:`~flask.sessions.SecureCookieSessionInterface` uses this to serialize | 
				
			||||||
 | 
					the session data, but it may be useful in other places. It can be extended to | 
				
			||||||
 | 
					support other types. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: TaggedJSONSerializer | 
				
			||||||
 | 
					    :members: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: JSONTag | 
				
			||||||
 | 
					    :members: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Let's seen an example that adds support for :class:`~collections.OrderedDict`. | 
				
			||||||
 | 
					Dicts don't have an order in Python or JSON, so to handle this we will dump | 
				
			||||||
 | 
					the items as a list of ``[key, value]`` pairs. Subclass :class:`JSONTag` and | 
				
			||||||
 | 
					give it the new key ``' od'`` to identify the type. The session serializer | 
				
			||||||
 | 
					processes dicts first, so insert the new tag at the front of the order since | 
				
			||||||
 | 
					``OrderedDict`` must be processed before ``dict``. :: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from flask.json.tag import JSONTag | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class TagOrderedDict(JSONTag): | 
				
			||||||
 | 
					        __slots__ = ('serializer',) | 
				
			||||||
 | 
					        key = ' od' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def check(self, value): | 
				
			||||||
 | 
					            return isinstance(value, OrderedDict) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def to_json(self, value): | 
				
			||||||
 | 
					            return [[k, self.serializer.tag(v)] for k, v in iteritems(value)] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def to_python(self, value): | 
				
			||||||
 | 
					            return OrderedDict(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app.session_interface.serializer.register(TagOrderedDict, index=0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					:license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from base64 import b64decode, b64encode | 
				
			||||||
 | 
					from datetime import datetime | 
				
			||||||
 | 
					from uuid import UUID | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from jinja2 import Markup | 
				
			||||||
 | 
					from werkzeug.http import http_date, parse_date | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from flask._compat import iteritems, text_type | 
				
			||||||
 | 
					from flask.json import dumps, loads | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONTag(object): | 
				
			||||||
 | 
					    """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __slots__ = ('serializer',) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: The tag to mark the serialized object with. If ``None``, this tag is | 
				
			||||||
 | 
					    #: only used as an intermediate step during tagging. | 
				
			||||||
 | 
					    key = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, serializer): | 
				
			||||||
 | 
					        """Create a tagger for the given serializer.""" | 
				
			||||||
 | 
					        self.serializer = serializer | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        """Check if the given value should be tagged by this tag.""" | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        """Convert the Python object to an object that is a valid JSON type. | 
				
			||||||
 | 
					        The tag will be added later.""" | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value): | 
				
			||||||
 | 
					        """Convert the JSON representation back to the correct type. The tag | 
				
			||||||
 | 
					        will already be removed.""" | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tag(self, value): | 
				
			||||||
 | 
					        """Convert the value to a valid JSON type and add the tag structure | 
				
			||||||
 | 
					        around it.""" | 
				
			||||||
 | 
					        return {self.key: self.to_json(value)} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TagDict(JSONTag): | 
				
			||||||
 | 
					    """Tag for 1-item dicts whose only key matches a registered tag. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Internally, the dict key is suffixed with `__`, and the suffix is removed | 
				
			||||||
 | 
					    when deserializing. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __slots__ = () | 
				
			||||||
 | 
					    key = ' di' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        return ( | 
				
			||||||
 | 
					            isinstance(value, dict) | 
				
			||||||
 | 
					            and len(value) == 1 | 
				
			||||||
 | 
					            and next(iter(value)) in self.serializer.tags | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        key = next(iter(value)) | 
				
			||||||
 | 
					        return {key + '__': self.serializer.tag(value[key])} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value): | 
				
			||||||
 | 
					        key = next(iter(value)) | 
				
			||||||
 | 
					        return {key[:-2]: value[key]} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PassDict(JSONTag): | 
				
			||||||
 | 
					    __slots__ = () | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        return isinstance(value, dict) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        # JSON objects may only have string keys, so don't bother tagging the | 
				
			||||||
 | 
					        # key here. | 
				
			||||||
 | 
					        return dict((k, self.serializer.tag(v)) for k, v in iteritems(value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tag = to_json | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TagTuple(JSONTag): | 
				
			||||||
 | 
					    __slots__ = () | 
				
			||||||
 | 
					    key = ' t' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        return isinstance(value, tuple) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        return [self.serializer.tag(item) for item in value] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value): | 
				
			||||||
 | 
					        return tuple(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PassList(JSONTag): | 
				
			||||||
 | 
					    __slots__ = () | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        return isinstance(value, list) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        return [self.serializer.tag(item) for item in value] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tag = to_json | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TagBytes(JSONTag): | 
				
			||||||
 | 
					    __slots__ = () | 
				
			||||||
 | 
					    key = ' b' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        return isinstance(value, bytes) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        return b64encode(value).decode('ascii') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value): | 
				
			||||||
 | 
					        return b64decode(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TagMarkup(JSONTag): | 
				
			||||||
 | 
					    """Serialize anything matching the :class:`~flask.Markup` API by | 
				
			||||||
 | 
					    having a ``__html__`` method to the result of that method. Always | 
				
			||||||
 | 
					    deserializes to an instance of :class:`~flask.Markup`.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __slots__ = () | 
				
			||||||
 | 
					    key = ' m' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        return callable(getattr(value, '__html__', None)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        return text_type(value.__html__()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value): | 
				
			||||||
 | 
					        return Markup(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TagUUID(JSONTag): | 
				
			||||||
 | 
					    __slots__ = () | 
				
			||||||
 | 
					    key = ' u' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        return isinstance(value, UUID) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        return value.hex | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value): | 
				
			||||||
 | 
					        return UUID(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TagDateTime(JSONTag): | 
				
			||||||
 | 
					    __slots__ = () | 
				
			||||||
 | 
					    key = ' d' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check(self, value): | 
				
			||||||
 | 
					        return isinstance(value, datetime) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_json(self, value): | 
				
			||||||
 | 
					        return http_date(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def to_python(self, value): | 
				
			||||||
 | 
					        return parse_date(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TaggedJSONSerializer(object): | 
				
			||||||
 | 
					    """Serializer that uses a tag system to compactly represent objects that | 
				
			||||||
 | 
					    are not JSON types. Passed as the intermediate serializer to | 
				
			||||||
 | 
					    :class:`itsdangerous.Serializer`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The following extra types are supported: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    * :class:`dict` | 
				
			||||||
 | 
					    * :class:`tuple` | 
				
			||||||
 | 
					    * :class:`bytes` | 
				
			||||||
 | 
					    * :class:`~flask.Markup` | 
				
			||||||
 | 
					    * :class:`~uuid.UUID` | 
				
			||||||
 | 
					    * :class:`~datetime.datetime` | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __slots__ = ('tags', 'order') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: Tag classes to bind when creating the serializer. Other tags can be | 
				
			||||||
 | 
					    #: added later using :meth:`~register`. | 
				
			||||||
 | 
					    default_tags = [ | 
				
			||||||
 | 
					        TagDict, PassDict, TagTuple, PassList, TagBytes, TagMarkup, TagUUID, | 
				
			||||||
 | 
					        TagDateTime, | 
				
			||||||
 | 
					    ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self): | 
				
			||||||
 | 
					        self.tags = {} | 
				
			||||||
 | 
					        self.order = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for cls in self.default_tags: | 
				
			||||||
 | 
					            self.register(cls) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def register(self, tag_class, force=False, index=None): | 
				
			||||||
 | 
					        """Register a new tag with this serializer. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param tag_class: tag class to register. Will be instantiated with this | 
				
			||||||
 | 
					            serializer instance. | 
				
			||||||
 | 
					        :param force: overwrite an existing tag. If false (default), a | 
				
			||||||
 | 
					            :exc:`KeyError` is raised. | 
				
			||||||
 | 
					        :param index: index to insert the new tag in the tag order. Useful when | 
				
			||||||
 | 
					            the new tag is a special case of an existing tag. If ``None`` | 
				
			||||||
 | 
					            (default), the tag is appended to the end of the order. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :raise KeyError: if the tag key is already registered and ``force`` is | 
				
			||||||
 | 
					            not true. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        tag = tag_class(self) | 
				
			||||||
 | 
					        key = tag.key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if key is not None: | 
				
			||||||
 | 
					            if not force and key in self.tags: | 
				
			||||||
 | 
					                raise KeyError("Tag '{0}' is already registered.".format(key)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.tags[key] = tag | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if index is None: | 
				
			||||||
 | 
					            self.order.append(tag) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.order.insert(index, tag) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tag(self, value): | 
				
			||||||
 | 
					        """Convert a value to a tagged representation if necessary.""" | 
				
			||||||
 | 
					        for tag in self.order: | 
				
			||||||
 | 
					            if tag.check(value): | 
				
			||||||
 | 
					                return tag.tag(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def untag(self, value): | 
				
			||||||
 | 
					        """Convert a tagged representation back to the original type.""" | 
				
			||||||
 | 
					        if len(value) != 1: | 
				
			||||||
 | 
					            return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        key = next(iter(value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if key not in self.tags: | 
				
			||||||
 | 
					            return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.tags[key].to_python(value[key]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dumps(self, value): | 
				
			||||||
 | 
					        """Tag the value and dump it to a compact JSON string.""" | 
				
			||||||
 | 
					        return dumps(self.tag(value), separators=(',', ':')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def loads(self, value): | 
				
			||||||
 | 
					        """Load data from a JSON string and deserialized any tagged objects.""" | 
				
			||||||
 | 
					        return loads(value, object_hook=self.untag) | 
				
			||||||
@ -0,0 +1,78 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					flask.logging | 
				
			||||||
 | 
					~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					:license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from __future__ import absolute_import | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import logging | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from werkzeug.local import LocalProxy | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .globals import request | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@LocalProxy | 
				
			||||||
 | 
					def wsgi_errors_stream(): | 
				
			||||||
 | 
					    """Find the most appropriate error stream for the application. If a request | 
				
			||||||
 | 
					    is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If you configure your own :class:`logging.StreamHandler`, you may want to | 
				
			||||||
 | 
					    use this for the stream. If you are using file or dict configuration and | 
				
			||||||
 | 
					    can't import this directly, you can refer to it as | 
				
			||||||
 | 
					    ``ext://flask.logging.wsgi_errors_stream``. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    return request.environ['wsgi.errors'] if request else sys.stderr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def has_level_handler(logger): | 
				
			||||||
 | 
					    """Check if there is a handler in the logging chain that will handle the | 
				
			||||||
 | 
					    given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    level = logger.getEffectiveLevel() | 
				
			||||||
 | 
					    current = logger | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while current: | 
				
			||||||
 | 
					        if any(handler.level <= level for handler in current.handlers): | 
				
			||||||
 | 
					            return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not current.propagate: | 
				
			||||||
 | 
					            break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        current = current.parent | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format | 
				
			||||||
 | 
					#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. | 
				
			||||||
 | 
					default_handler = logging.StreamHandler(wsgi_errors_stream) | 
				
			||||||
 | 
					default_handler.setFormatter(logging.Formatter( | 
				
			||||||
 | 
					    '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' | 
				
			||||||
 | 
					)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_logger(app): | 
				
			||||||
 | 
					    """Get the ``'flask.app'`` logger and configure it if needed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    When :attr:`~flask.Flask.debug` is enabled, set the logger level to | 
				
			||||||
 | 
					    :data:`logging.DEBUG` if it is not set. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If there is no handler for the logger's effective level, add a | 
				
			||||||
 | 
					    :class:`~logging.StreamHandler` for | 
				
			||||||
 | 
					    :func:`~flask.logging.wsgi_errors_stream` with a basic format. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    logger = logging.getLogger('flask.app') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if app.debug and logger.level == logging.NOTSET: | 
				
			||||||
 | 
					        logger.setLevel(logging.DEBUG) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not has_level_handler(logger): | 
				
			||||||
 | 
					        logger.addHandler(default_handler) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return logger | 
				
			||||||
@ -0,0 +1,385 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.sessions | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Implements cookie based sessions based on itsdangerous. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import hashlib | 
				
			||||||
 | 
					import warnings | 
				
			||||||
 | 
					from collections import MutableMapping | 
				
			||||||
 | 
					from datetime import datetime | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from itsdangerous import BadSignature, URLSafeTimedSerializer | 
				
			||||||
 | 
					from werkzeug.datastructures import CallbackDict | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from flask.helpers import is_ip, total_seconds | 
				
			||||||
 | 
					from flask.json.tag import TaggedJSONSerializer | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SessionMixin(MutableMapping): | 
				
			||||||
 | 
					    """Expands a basic dictionary with session attributes.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def permanent(self): | 
				
			||||||
 | 
					        """This reflects the ``'_permanent'`` key in the dict.""" | 
				
			||||||
 | 
					        return self.get('_permanent', False) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @permanent.setter | 
				
			||||||
 | 
					    def permanent(self, value): | 
				
			||||||
 | 
					        self['_permanent'] = bool(value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: Some implementations can detect whether a session is newly | 
				
			||||||
 | 
					    #: created, but that is not guaranteed. Use with caution. The mixin | 
				
			||||||
 | 
					    # default is hard-coded ``False``. | 
				
			||||||
 | 
					    new = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: Some implementations can detect changes to the session and set | 
				
			||||||
 | 
					    #: this when that happens. The mixin default is hard coded to | 
				
			||||||
 | 
					    #: ``True``. | 
				
			||||||
 | 
					    modified = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: Some implementations can detect when session data is read or | 
				
			||||||
 | 
					    #: written and set this when that happens. The mixin default is hard | 
				
			||||||
 | 
					    #: coded to ``True``. | 
				
			||||||
 | 
					    accessed = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SecureCookieSession(CallbackDict, SessionMixin): | 
				
			||||||
 | 
					    """Base class for sessions based on signed cookies. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This session backend will set the :attr:`modified` and | 
				
			||||||
 | 
					    :attr:`accessed` attributes. It cannot reliably track whether a | 
				
			||||||
 | 
					    session is new (vs. empty), so :attr:`new` remains hard coded to | 
				
			||||||
 | 
					    ``False``. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: When data is changed, this is set to ``True``. Only the session | 
				
			||||||
 | 
					    #: dictionary itself is tracked; if the session contains mutable | 
				
			||||||
 | 
					    #: data (for example a nested dict) then this must be set to | 
				
			||||||
 | 
					    #: ``True`` manually when modifying that data. The session cookie | 
				
			||||||
 | 
					    #: will only be written to the response if this is ``True``. | 
				
			||||||
 | 
					    modified = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: When data is read or written, this is set to ``True``. Used by | 
				
			||||||
 | 
					    # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie`` | 
				
			||||||
 | 
					    #: header, which allows caching proxies to cache different pages for | 
				
			||||||
 | 
					    #: different users. | 
				
			||||||
 | 
					    accessed = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, initial=None): | 
				
			||||||
 | 
					        def on_update(self): | 
				
			||||||
 | 
					            self.modified = True | 
				
			||||||
 | 
					            self.accessed = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(SecureCookieSession, self).__init__(initial, on_update) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getitem__(self, key): | 
				
			||||||
 | 
					        self.accessed = True | 
				
			||||||
 | 
					        return super(SecureCookieSession, self).__getitem__(key) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, key, default=None): | 
				
			||||||
 | 
					        self.accessed = True | 
				
			||||||
 | 
					        return super(SecureCookieSession, self).get(key, default) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setdefault(self, key, default=None): | 
				
			||||||
 | 
					        self.accessed = True | 
				
			||||||
 | 
					        return super(SecureCookieSession, self).setdefault(key, default) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NullSession(SecureCookieSession): | 
				
			||||||
 | 
					    """Class used to generate nicer error messages if sessions are not | 
				
			||||||
 | 
					    available.  Will still allow read-only access to the empty session | 
				
			||||||
 | 
					    but fail on setting. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _fail(self, *args, **kwargs): | 
				
			||||||
 | 
					        raise RuntimeError('The session is unavailable because no secret ' | 
				
			||||||
 | 
					                           'key was set.  Set the secret_key on the ' | 
				
			||||||
 | 
					                           'application to something unique and secret.') | 
				
			||||||
 | 
					    __setitem__ = __delitem__ = clear = pop = popitem = \ | 
				
			||||||
 | 
					        update = setdefault = _fail | 
				
			||||||
 | 
					    del _fail | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SessionInterface(object): | 
				
			||||||
 | 
					    """The basic interface you have to implement in order to replace the | 
				
			||||||
 | 
					    default session interface which uses werkzeug's securecookie | 
				
			||||||
 | 
					    implementation.  The only methods you have to implement are | 
				
			||||||
 | 
					    :meth:`open_session` and :meth:`save_session`, the others have | 
				
			||||||
 | 
					    useful defaults which you don't need to change. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The session object returned by the :meth:`open_session` method has to | 
				
			||||||
 | 
					    provide a dictionary like interface plus the properties and methods | 
				
			||||||
 | 
					    from the :class:`SessionMixin`.  We recommend just subclassing a dict | 
				
			||||||
 | 
					    and adding that mixin:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class Session(dict, SessionMixin): | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If :meth:`open_session` returns ``None`` Flask will call into | 
				
			||||||
 | 
					    :meth:`make_null_session` to create a session that acts as replacement | 
				
			||||||
 | 
					    if the session support cannot work because some requirement is not | 
				
			||||||
 | 
					    fulfilled.  The default :class:`NullSession` class that is created | 
				
			||||||
 | 
					    will complain that the secret key was not set. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    To replace the session interface on an application all you have to do | 
				
			||||||
 | 
					    is to assign :attr:`flask.Flask.session_interface`:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app = Flask(__name__) | 
				
			||||||
 | 
					        app.session_interface = MySessionInterface() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 0.8 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: :meth:`make_null_session` will look here for the class that should | 
				
			||||||
 | 
					    #: be created when a null session is requested.  Likewise the | 
				
			||||||
 | 
					    #: :meth:`is_null_session` method will perform a typecheck against | 
				
			||||||
 | 
					    #: this type. | 
				
			||||||
 | 
					    null_session_class = NullSession | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: A flag that indicates if the session interface is pickle based. | 
				
			||||||
 | 
					    #: This can be used by Flask extensions to make a decision in regards | 
				
			||||||
 | 
					    #: to how to deal with the session object. | 
				
			||||||
 | 
					    #: | 
				
			||||||
 | 
					    #: .. versionadded:: 0.10 | 
				
			||||||
 | 
					    pickle_based = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def make_null_session(self, app): | 
				
			||||||
 | 
					        """Creates a null session which acts as a replacement object if the | 
				
			||||||
 | 
					        real session support could not be loaded due to a configuration | 
				
			||||||
 | 
					        error.  This mainly aids the user experience because the job of the | 
				
			||||||
 | 
					        null session is to still support lookup without complaining but | 
				
			||||||
 | 
					        modifications are answered with a helpful error message of what | 
				
			||||||
 | 
					        failed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This creates an instance of :attr:`null_session_class` by default. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return self.null_session_class() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_null_session(self, obj): | 
				
			||||||
 | 
					        """Checks if a given object is a null session.  Null sessions are | 
				
			||||||
 | 
					        not asked to be saved. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This checks if the object is an instance of :attr:`null_session_class` | 
				
			||||||
 | 
					        by default. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return isinstance(obj, self.null_session_class) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_cookie_domain(self, app): | 
				
			||||||
 | 
					        """Returns the domain that should be set for the session cookie. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise | 
				
			||||||
 | 
					        falls back to detecting the domain based on ``SERVER_NAME``. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` is | 
				
			||||||
 | 
					        updated to avoid re-running the logic. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rv = app.config['SESSION_COOKIE_DOMAIN'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set explicitly, or cached from SERVER_NAME detection | 
				
			||||||
 | 
					        # if False, return None | 
				
			||||||
 | 
					        if rv is not None: | 
				
			||||||
 | 
					            return rv if rv else None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rv = app.config['SERVER_NAME'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # server name not set, cache False to return none next time | 
				
			||||||
 | 
					        if not rv: | 
				
			||||||
 | 
					            app.config['SESSION_COOKIE_DOMAIN'] = False | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # chop off the port which is usually not supported by browsers | 
				
			||||||
 | 
					        # remove any leading '.' since we'll add that later | 
				
			||||||
 | 
					        rv = rv.rsplit(':', 1)[0].lstrip('.') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if '.' not in rv: | 
				
			||||||
 | 
					            # Chrome doesn't allow names without a '.' | 
				
			||||||
 | 
					            # this should only come up with localhost | 
				
			||||||
 | 
					            # hack around this by not setting the name, and show a warning | 
				
			||||||
 | 
					            warnings.warn( | 
				
			||||||
 | 
					                '"{rv}" is not a valid cookie domain, it must contain a ".".' | 
				
			||||||
 | 
					                ' Add an entry to your hosts file, for example' | 
				
			||||||
 | 
					                ' "{rv}.localdomain", and use that instead.'.format(rv=rv) | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					            app.config['SESSION_COOKIE_DOMAIN'] = False | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ip = is_ip(rv) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ip: | 
				
			||||||
 | 
					            warnings.warn( | 
				
			||||||
 | 
					                'The session cookie domain is an IP address. This may not work' | 
				
			||||||
 | 
					                ' as intended in some browsers. Add an entry to your hosts' | 
				
			||||||
 | 
					                ' file, for example "localhost.localdomain", and use that' | 
				
			||||||
 | 
					                ' instead.' | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # if this is not an ip and app is mounted at the root, allow subdomain | 
				
			||||||
 | 
					        # matching by adding a '.' prefix | 
				
			||||||
 | 
					        if self.get_cookie_path(app) == '/' and not ip: | 
				
			||||||
 | 
					            rv = '.' + rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.config['SESSION_COOKIE_DOMAIN'] = rv | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_cookie_path(self, app): | 
				
			||||||
 | 
					        """Returns the path for which the cookie should be valid.  The | 
				
			||||||
 | 
					        default implementation uses the value from the ``SESSION_COOKIE_PATH`` | 
				
			||||||
 | 
					        config var if it's set, and falls back to ``APPLICATION_ROOT`` or | 
				
			||||||
 | 
					        uses ``/`` if it's ``None``. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return app.config['SESSION_COOKIE_PATH'] \ | 
				
			||||||
 | 
					               or app.config['APPLICATION_ROOT'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_cookie_httponly(self, app): | 
				
			||||||
 | 
					        """Returns True if the session cookie should be httponly.  This | 
				
			||||||
 | 
					        currently just returns the value of the ``SESSION_COOKIE_HTTPONLY`` | 
				
			||||||
 | 
					        config var. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return app.config['SESSION_COOKIE_HTTPONLY'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_cookie_secure(self, app): | 
				
			||||||
 | 
					        """Returns True if the cookie should be secure.  This currently | 
				
			||||||
 | 
					        just returns the value of the ``SESSION_COOKIE_SECURE`` setting. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return app.config['SESSION_COOKIE_SECURE'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_cookie_samesite(self, app): | 
				
			||||||
 | 
					        """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the | 
				
			||||||
 | 
					        ``SameSite`` attribute. This currently just returns the value of | 
				
			||||||
 | 
					        the :data:`SESSION_COOKIE_SAMESITE` setting. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return app.config['SESSION_COOKIE_SAMESITE'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_expiration_time(self, app, session): | 
				
			||||||
 | 
					        """A helper method that returns an expiration date for the session | 
				
			||||||
 | 
					        or ``None`` if the session is linked to the browser session.  The | 
				
			||||||
 | 
					        default implementation returns now + the permanent session | 
				
			||||||
 | 
					        lifetime configured on the application. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if session.permanent: | 
				
			||||||
 | 
					            return datetime.utcnow() + app.permanent_session_lifetime | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def should_set_cookie(self, app, session): | 
				
			||||||
 | 
					        """Used by session backends to determine if a ``Set-Cookie`` header | 
				
			||||||
 | 
					        should be set for this session cookie for this response. If the session | 
				
			||||||
 | 
					        has been modified, the cookie is set. If the session is permanent and | 
				
			||||||
 | 
					        the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is | 
				
			||||||
 | 
					        always set. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This check is usually skipped if the session was deleted. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 0.11 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return session.modified or ( | 
				
			||||||
 | 
					            session.permanent and app.config['SESSION_REFRESH_EACH_REQUEST'] | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def open_session(self, app, request): | 
				
			||||||
 | 
					        """This method has to be implemented and must either return ``None`` | 
				
			||||||
 | 
					        in case the loading failed because of a configuration error or an | 
				
			||||||
 | 
					        instance of a session object which implements a dictionary like | 
				
			||||||
 | 
					        interface + the methods and attributes on :class:`SessionMixin`. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save_session(self, app, session, response): | 
				
			||||||
 | 
					        """This is called for actual sessions returned by :meth:`open_session` | 
				
			||||||
 | 
					        at the end of the request.  This is still called during a request | 
				
			||||||
 | 
					        context so if you absolutely need access to the request you can do | 
				
			||||||
 | 
					        that. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					session_json_serializer = TaggedJSONSerializer() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SecureCookieSessionInterface(SessionInterface): | 
				
			||||||
 | 
					    """The default session interface that stores sessions in signed cookies | 
				
			||||||
 | 
					    through the :mod:`itsdangerous` module. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    #: the salt that should be applied on top of the secret key for the | 
				
			||||||
 | 
					    #: signing of cookie based sessions. | 
				
			||||||
 | 
					    salt = 'cookie-session' | 
				
			||||||
 | 
					    #: the hash function to use for the signature.  The default is sha1 | 
				
			||||||
 | 
					    digest_method = staticmethod(hashlib.sha1) | 
				
			||||||
 | 
					    #: the name of the itsdangerous supported key derivation.  The default | 
				
			||||||
 | 
					    #: is hmac. | 
				
			||||||
 | 
					    key_derivation = 'hmac' | 
				
			||||||
 | 
					    #: A python serializer for the payload.  The default is a compact | 
				
			||||||
 | 
					    #: JSON derived serializer with support for some extra Python types | 
				
			||||||
 | 
					    #: such as datetime objects or tuples. | 
				
			||||||
 | 
					    serializer = session_json_serializer | 
				
			||||||
 | 
					    session_class = SecureCookieSession | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_signing_serializer(self, app): | 
				
			||||||
 | 
					        if not app.secret_key: | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					        signer_kwargs = dict( | 
				
			||||||
 | 
					            key_derivation=self.key_derivation, | 
				
			||||||
 | 
					            digest_method=self.digest_method | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        return URLSafeTimedSerializer(app.secret_key, salt=self.salt, | 
				
			||||||
 | 
					                                      serializer=self.serializer, | 
				
			||||||
 | 
					                                      signer_kwargs=signer_kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def open_session(self, app, request): | 
				
			||||||
 | 
					        s = self.get_signing_serializer(app) | 
				
			||||||
 | 
					        if s is None: | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					        val = request.cookies.get(app.session_cookie_name) | 
				
			||||||
 | 
					        if not val: | 
				
			||||||
 | 
					            return self.session_class() | 
				
			||||||
 | 
					        max_age = total_seconds(app.permanent_session_lifetime) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            data = s.loads(val, max_age=max_age) | 
				
			||||||
 | 
					            return self.session_class(data) | 
				
			||||||
 | 
					        except BadSignature: | 
				
			||||||
 | 
					            return self.session_class() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def save_session(self, app, session, response): | 
				
			||||||
 | 
					        domain = self.get_cookie_domain(app) | 
				
			||||||
 | 
					        path = self.get_cookie_path(app) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # If the session is modified to be empty, remove the cookie. | 
				
			||||||
 | 
					        # If the session is empty, return without setting the cookie. | 
				
			||||||
 | 
					        if not session: | 
				
			||||||
 | 
					            if session.modified: | 
				
			||||||
 | 
					                response.delete_cookie( | 
				
			||||||
 | 
					                    app.session_cookie_name, | 
				
			||||||
 | 
					                    domain=domain, | 
				
			||||||
 | 
					                    path=path | 
				
			||||||
 | 
					                ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Add a "Vary: Cookie" header if the session was accessed at all. | 
				
			||||||
 | 
					        if session.accessed: | 
				
			||||||
 | 
					            response.vary.add('Cookie') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.should_set_cookie(app, session): | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        httponly = self.get_cookie_httponly(app) | 
				
			||||||
 | 
					        secure = self.get_cookie_secure(app) | 
				
			||||||
 | 
					        samesite = self.get_cookie_samesite(app) | 
				
			||||||
 | 
					        expires = self.get_expiration_time(app, session) | 
				
			||||||
 | 
					        val = self.get_signing_serializer(app).dumps(dict(session)) | 
				
			||||||
 | 
					        response.set_cookie( | 
				
			||||||
 | 
					            app.session_cookie_name, | 
				
			||||||
 | 
					            val, | 
				
			||||||
 | 
					            expires=expires, | 
				
			||||||
 | 
					            httponly=httponly, | 
				
			||||||
 | 
					            domain=domain, | 
				
			||||||
 | 
					            path=path, | 
				
			||||||
 | 
					            secure=secure, | 
				
			||||||
 | 
					            samesite=samesite | 
				
			||||||
 | 
					        ) | 
				
			||||||
@ -0,0 +1,57 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.signals | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Implements signals based on blinker if available, otherwise | 
				
			||||||
 | 
					    falls silently back to a noop. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					signals_available = False | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    from blinker import Namespace | 
				
			||||||
 | 
					    signals_available = True | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    class Namespace(object): | 
				
			||||||
 | 
					        def signal(self, name, doc=None): | 
				
			||||||
 | 
					            return _FakeSignal(name, doc) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class _FakeSignal(object): | 
				
			||||||
 | 
					        """If blinker is unavailable, create a fake class with the same | 
				
			||||||
 | 
					        interface that allows sending of signals but will fail with an | 
				
			||||||
 | 
					        error on anything else.  Instead of doing anything on send, it | 
				
			||||||
 | 
					        will just ignore the arguments and do nothing instead. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def __init__(self, name, doc=None): | 
				
			||||||
 | 
					            self.name = name | 
				
			||||||
 | 
					            self.__doc__ = doc | 
				
			||||||
 | 
					        def _fail(self, *args, **kwargs): | 
				
			||||||
 | 
					            raise RuntimeError('signalling support is unavailable ' | 
				
			||||||
 | 
					                               'because the blinker library is ' | 
				
			||||||
 | 
					                               'not installed.') | 
				
			||||||
 | 
					        send = lambda *a, **kw: None | 
				
			||||||
 | 
					        connect = disconnect = has_receivers_for = receivers_for = \ | 
				
			||||||
 | 
					            temporarily_connected_to = connected_to = _fail | 
				
			||||||
 | 
					        del _fail | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The namespace for code signals.  If you are not Flask code, do | 
				
			||||||
 | 
					# not put signals in here.  Create your own namespace instead. | 
				
			||||||
 | 
					_signals = Namespace() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Core signals.  For usage examples grep the source code or consult | 
				
			||||||
 | 
					# the API documentation in docs/api.rst as well as docs/signals.rst | 
				
			||||||
 | 
					template_rendered = _signals.signal('template-rendered') | 
				
			||||||
 | 
					before_render_template = _signals.signal('before-render-template') | 
				
			||||||
 | 
					request_started = _signals.signal('request-started') | 
				
			||||||
 | 
					request_finished = _signals.signal('request-finished') | 
				
			||||||
 | 
					request_tearing_down = _signals.signal('request-tearing-down') | 
				
			||||||
 | 
					got_request_exception = _signals.signal('got-request-exception') | 
				
			||||||
 | 
					appcontext_tearing_down = _signals.signal('appcontext-tearing-down') | 
				
			||||||
 | 
					appcontext_pushed = _signals.signal('appcontext-pushed') | 
				
			||||||
 | 
					appcontext_popped = _signals.signal('appcontext-popped') | 
				
			||||||
 | 
					message_flashed = _signals.signal('message-flashed') | 
				
			||||||
@ -0,0 +1,150 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.templating | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Implements the bridge to Jinja2. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from jinja2 import BaseLoader, Environment as BaseEnvironment, \ | 
				
			||||||
 | 
					     TemplateNotFound | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .globals import _request_ctx_stack, _app_ctx_stack | 
				
			||||||
 | 
					from .signals import template_rendered, before_render_template | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _default_template_ctx_processor(): | 
				
			||||||
 | 
					    """Default template context processor.  Injects `request`, | 
				
			||||||
 | 
					    `session` and `g`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    reqctx = _request_ctx_stack.top | 
				
			||||||
 | 
					    appctx = _app_ctx_stack.top | 
				
			||||||
 | 
					    rv = {} | 
				
			||||||
 | 
					    if appctx is not None: | 
				
			||||||
 | 
					        rv['g'] = appctx.g | 
				
			||||||
 | 
					    if reqctx is not None: | 
				
			||||||
 | 
					        rv['request'] = reqctx.request | 
				
			||||||
 | 
					        rv['session'] = reqctx.session | 
				
			||||||
 | 
					    return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Environment(BaseEnvironment): | 
				
			||||||
 | 
					    """Works like a regular Jinja2 environment but has some additional | 
				
			||||||
 | 
					    knowledge of how Flask's blueprint works so that it can prepend the | 
				
			||||||
 | 
					    name of the blueprint to referenced templates if necessary. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, app, **options): | 
				
			||||||
 | 
					        if 'loader' not in options: | 
				
			||||||
 | 
					            options['loader'] = app.create_global_jinja_loader() | 
				
			||||||
 | 
					        BaseEnvironment.__init__(self, **options) | 
				
			||||||
 | 
					        self.app = app | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DispatchingJinjaLoader(BaseLoader): | 
				
			||||||
 | 
					    """A loader that looks for templates in the application and all | 
				
			||||||
 | 
					    the blueprint folders. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, app): | 
				
			||||||
 | 
					        self.app = app | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_source(self, environment, template): | 
				
			||||||
 | 
					        if self.app.config['EXPLAIN_TEMPLATE_LOADING']: | 
				
			||||||
 | 
					            return self._get_source_explained(environment, template) | 
				
			||||||
 | 
					        return self._get_source_fast(environment, template) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_source_explained(self, environment, template): | 
				
			||||||
 | 
					        attempts = [] | 
				
			||||||
 | 
					        trv = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for srcobj, loader in self._iter_loaders(template): | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                rv = loader.get_source(environment, template) | 
				
			||||||
 | 
					                if trv is None: | 
				
			||||||
 | 
					                    trv = rv | 
				
			||||||
 | 
					            except TemplateNotFound: | 
				
			||||||
 | 
					                rv = None | 
				
			||||||
 | 
					            attempts.append((loader, srcobj, rv)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from .debughelpers import explain_template_loading_attempts | 
				
			||||||
 | 
					        explain_template_loading_attempts(self.app, template, attempts) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if trv is not None: | 
				
			||||||
 | 
					            return trv | 
				
			||||||
 | 
					        raise TemplateNotFound(template) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_source_fast(self, environment, template): | 
				
			||||||
 | 
					        for srcobj, loader in self._iter_loaders(template): | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                return loader.get_source(environment, template) | 
				
			||||||
 | 
					            except TemplateNotFound: | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					        raise TemplateNotFound(template) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _iter_loaders(self, template): | 
				
			||||||
 | 
					        loader = self.app.jinja_loader | 
				
			||||||
 | 
					        if loader is not None: | 
				
			||||||
 | 
					            yield self.app, loader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for blueprint in self.app.iter_blueprints(): | 
				
			||||||
 | 
					            loader = blueprint.jinja_loader | 
				
			||||||
 | 
					            if loader is not None: | 
				
			||||||
 | 
					                yield blueprint, loader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def list_templates(self): | 
				
			||||||
 | 
					        result = set() | 
				
			||||||
 | 
					        loader = self.app.jinja_loader | 
				
			||||||
 | 
					        if loader is not None: | 
				
			||||||
 | 
					            result.update(loader.list_templates()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for blueprint in self.app.iter_blueprints(): | 
				
			||||||
 | 
					            loader = blueprint.jinja_loader | 
				
			||||||
 | 
					            if loader is not None: | 
				
			||||||
 | 
					                for template in loader.list_templates(): | 
				
			||||||
 | 
					                    result.add(template) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return list(result) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _render(template, context, app): | 
				
			||||||
 | 
					    """Renders the template and fires the signal""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    before_render_template.send(app, template=template, context=context) | 
				
			||||||
 | 
					    rv = template.render(context) | 
				
			||||||
 | 
					    template_rendered.send(app, template=template, context=context) | 
				
			||||||
 | 
					    return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def render_template(template_name_or_list, **context): | 
				
			||||||
 | 
					    """Renders a template from the template folder with the given | 
				
			||||||
 | 
					    context. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param template_name_or_list: the name of the template to be | 
				
			||||||
 | 
					                                  rendered, or an iterable with template names | 
				
			||||||
 | 
					                                  the first one existing will be rendered | 
				
			||||||
 | 
					    :param context: the variables that should be available in the | 
				
			||||||
 | 
					                    context of the template. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    ctx = _app_ctx_stack.top | 
				
			||||||
 | 
					    ctx.app.update_template_context(context) | 
				
			||||||
 | 
					    return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list), | 
				
			||||||
 | 
					                   context, ctx.app) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def render_template_string(source, **context): | 
				
			||||||
 | 
					    """Renders a template from the given template source string | 
				
			||||||
 | 
					    with the given context. Template variables will be autoescaped. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param source: the source code of the template to be | 
				
			||||||
 | 
					                   rendered | 
				
			||||||
 | 
					    :param context: the variables that should be available in the | 
				
			||||||
 | 
					                    context of the template. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    ctx = _app_ctx_stack.top | 
				
			||||||
 | 
					    ctx.app.update_template_context(context) | 
				
			||||||
 | 
					    return _render(ctx.app.jinja_env.from_string(source), | 
				
			||||||
 | 
					                   context, ctx.app) | 
				
			||||||
@ -0,0 +1,250 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.testing | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Implements test support helpers.  This module is lazily imported | 
				
			||||||
 | 
					    and usually not used in production environments. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import werkzeug | 
				
			||||||
 | 
					from contextlib import contextmanager | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from click.testing import CliRunner | 
				
			||||||
 | 
					from flask.cli import ScriptInfo | 
				
			||||||
 | 
					from werkzeug.test import Client, EnvironBuilder | 
				
			||||||
 | 
					from flask import _request_ctx_stack | 
				
			||||||
 | 
					from flask.json import dumps as json_dumps | 
				
			||||||
 | 
					from werkzeug.urls import url_parse | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_test_environ_builder( | 
				
			||||||
 | 
					    app, path='/', base_url=None, subdomain=None, url_scheme=None, | 
				
			||||||
 | 
					    *args, **kwargs | 
				
			||||||
 | 
					): | 
				
			||||||
 | 
					    """Create a :class:`~werkzeug.test.EnvironBuilder`, taking some | 
				
			||||||
 | 
					    defaults from the application. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param app: The Flask application to configure the environment from. | 
				
			||||||
 | 
					    :param path: URL path being requested. | 
				
			||||||
 | 
					    :param base_url: Base URL where the app is being served, which | 
				
			||||||
 | 
					        ``path`` is relative to. If not given, built from | 
				
			||||||
 | 
					        :data:`PREFERRED_URL_SCHEME`, ``subdomain``, | 
				
			||||||
 | 
					        :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. | 
				
			||||||
 | 
					    :param subdomain: Subdomain name to append to :data:`SERVER_NAME`. | 
				
			||||||
 | 
					    :param url_scheme: Scheme to use instead of | 
				
			||||||
 | 
					        :data:`PREFERRED_URL_SCHEME`. | 
				
			||||||
 | 
					    :param json: If given, this is serialized as JSON and passed as | 
				
			||||||
 | 
					        ``data``. Also defaults ``content_type`` to | 
				
			||||||
 | 
					        ``application/json``. | 
				
			||||||
 | 
					    :param args: other positional arguments passed to | 
				
			||||||
 | 
					        :class:`~werkzeug.test.EnvironBuilder`. | 
				
			||||||
 | 
					    :param kwargs: other keyword arguments passed to | 
				
			||||||
 | 
					        :class:`~werkzeug.test.EnvironBuilder`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert ( | 
				
			||||||
 | 
					        not (base_url or subdomain or url_scheme) | 
				
			||||||
 | 
					        or (base_url is not None) != bool(subdomain or url_scheme) | 
				
			||||||
 | 
					    ), 'Cannot pass "subdomain" or "url_scheme" with "base_url".' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if base_url is None: | 
				
			||||||
 | 
					        http_host = app.config.get('SERVER_NAME') or 'localhost' | 
				
			||||||
 | 
					        app_root = app.config['APPLICATION_ROOT'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if subdomain: | 
				
			||||||
 | 
					            http_host = '{0}.{1}'.format(subdomain, http_host) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if url_scheme is None: | 
				
			||||||
 | 
					            url_scheme = app.config['PREFERRED_URL_SCHEME'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        url = url_parse(path) | 
				
			||||||
 | 
					        base_url = '{scheme}://{netloc}/{path}'.format( | 
				
			||||||
 | 
					            scheme=url.scheme or url_scheme, | 
				
			||||||
 | 
					            netloc=url.netloc or http_host, | 
				
			||||||
 | 
					            path=app_root.lstrip('/') | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					        path = url.path | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if url.query: | 
				
			||||||
 | 
					            sep = b'?' if isinstance(url.query, bytes) else '?' | 
				
			||||||
 | 
					            path += sep + url.query | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if 'json' in kwargs: | 
				
			||||||
 | 
					        assert 'data' not in kwargs, ( | 
				
			||||||
 | 
					            "Client cannot provide both 'json' and 'data'." | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # push a context so flask.json can use app's json attributes | 
				
			||||||
 | 
					        with app.app_context(): | 
				
			||||||
 | 
					            kwargs['data'] = json_dumps(kwargs.pop('json')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'content_type' not in kwargs: | 
				
			||||||
 | 
					            kwargs['content_type'] = 'application/json' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return EnvironBuilder(path, base_url, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FlaskClient(Client): | 
				
			||||||
 | 
					    """Works like a regular Werkzeug test client but has some knowledge about | 
				
			||||||
 | 
					    how Flask works to defer the cleanup of the request context stack to the | 
				
			||||||
 | 
					    end of a ``with`` body when used in a ``with`` statement.  For general | 
				
			||||||
 | 
					    information about how to use this class refer to | 
				
			||||||
 | 
					    :class:`werkzeug.test.Client`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 0.12 | 
				
			||||||
 | 
					       `app.test_client()` includes preset default environment, which can be | 
				
			||||||
 | 
					       set after instantiation of the `app.test_client()` object in | 
				
			||||||
 | 
					       `client.environ_base`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Basic usage is outlined in the :ref:`testing` chapter. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    preserve_context = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs): | 
				
			||||||
 | 
					        super(FlaskClient, self).__init__(*args, **kwargs) | 
				
			||||||
 | 
					        self.environ_base = { | 
				
			||||||
 | 
					            "REMOTE_ADDR": "127.0.0.1", | 
				
			||||||
 | 
					            "HTTP_USER_AGENT": "werkzeug/" + werkzeug.__version__ | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @contextmanager | 
				
			||||||
 | 
					    def session_transaction(self, *args, **kwargs): | 
				
			||||||
 | 
					        """When used in combination with a ``with`` statement this opens a | 
				
			||||||
 | 
					        session transaction.  This can be used to modify the session that | 
				
			||||||
 | 
					        the test client uses.  Once the ``with`` block is left the session is | 
				
			||||||
 | 
					        stored back. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            with client.session_transaction() as session: | 
				
			||||||
 | 
					                session['value'] = 42 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Internally this is implemented by going through a temporary test | 
				
			||||||
 | 
					        request context and since session handling could depend on | 
				
			||||||
 | 
					        request variables this function accepts the same arguments as | 
				
			||||||
 | 
					        :meth:`~flask.Flask.test_request_context` which are directly | 
				
			||||||
 | 
					        passed through. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if self.cookie_jar is None: | 
				
			||||||
 | 
					            raise RuntimeError('Session transactions only make sense ' | 
				
			||||||
 | 
					                               'with cookies enabled.') | 
				
			||||||
 | 
					        app = self.application | 
				
			||||||
 | 
					        environ_overrides = kwargs.setdefault('environ_overrides', {}) | 
				
			||||||
 | 
					        self.cookie_jar.inject_wsgi(environ_overrides) | 
				
			||||||
 | 
					        outer_reqctx = _request_ctx_stack.top | 
				
			||||||
 | 
					        with app.test_request_context(*args, **kwargs) as c: | 
				
			||||||
 | 
					            session_interface = app.session_interface | 
				
			||||||
 | 
					            sess = session_interface.open_session(app, c.request) | 
				
			||||||
 | 
					            if sess is None: | 
				
			||||||
 | 
					                raise RuntimeError('Session backend did not open a session. ' | 
				
			||||||
 | 
					                                   'Check the configuration') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Since we have to open a new request context for the session | 
				
			||||||
 | 
					            # handling we want to make sure that we hide out own context | 
				
			||||||
 | 
					            # from the caller.  By pushing the original request context | 
				
			||||||
 | 
					            # (or None) on top of this and popping it we get exactly that | 
				
			||||||
 | 
					            # behavior.  It's important to not use the push and pop | 
				
			||||||
 | 
					            # methods of the actual request context object since that would | 
				
			||||||
 | 
					            # mean that cleanup handlers are called | 
				
			||||||
 | 
					            _request_ctx_stack.push(outer_reqctx) | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                yield sess | 
				
			||||||
 | 
					            finally: | 
				
			||||||
 | 
					                _request_ctx_stack.pop() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            resp = app.response_class() | 
				
			||||||
 | 
					            if not session_interface.is_null_session(sess): | 
				
			||||||
 | 
					                session_interface.save_session(app, sess, resp) | 
				
			||||||
 | 
					            headers = resp.get_wsgi_headers(c.request.environ) | 
				
			||||||
 | 
					            self.cookie_jar.extract_wsgi(c.request.environ, headers) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def open(self, *args, **kwargs): | 
				
			||||||
 | 
					        as_tuple = kwargs.pop('as_tuple', False) | 
				
			||||||
 | 
					        buffered = kwargs.pop('buffered', False) | 
				
			||||||
 | 
					        follow_redirects = kwargs.pop('follow_redirects', False) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ( | 
				
			||||||
 | 
					            not kwargs and len(args) == 1 | 
				
			||||||
 | 
					            and isinstance(args[0], (EnvironBuilder, dict)) | 
				
			||||||
 | 
					        ): | 
				
			||||||
 | 
					            environ = self.environ_base.copy() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if isinstance(args[0], EnvironBuilder): | 
				
			||||||
 | 
					                environ.update(args[0].get_environ()) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                environ.update(args[0]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            environ['flask._preserve_context'] = self.preserve_context | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            kwargs.setdefault('environ_overrides', {}) \ | 
				
			||||||
 | 
					                ['flask._preserve_context'] = self.preserve_context | 
				
			||||||
 | 
					            kwargs.setdefault('environ_base', self.environ_base) | 
				
			||||||
 | 
					            builder = make_test_environ_builder( | 
				
			||||||
 | 
					                self.application, *args, **kwargs | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                environ = builder.get_environ() | 
				
			||||||
 | 
					            finally: | 
				
			||||||
 | 
					                builder.close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Client.open( | 
				
			||||||
 | 
					            self, environ, | 
				
			||||||
 | 
					            as_tuple=as_tuple, | 
				
			||||||
 | 
					            buffered=buffered, | 
				
			||||||
 | 
					            follow_redirects=follow_redirects | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __enter__(self): | 
				
			||||||
 | 
					        if self.preserve_context: | 
				
			||||||
 | 
					            raise RuntimeError('Cannot nest client invocations') | 
				
			||||||
 | 
					        self.preserve_context = True | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, exc_type, exc_value, tb): | 
				
			||||||
 | 
					        self.preserve_context = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # on exit we want to clean up earlier.  Normally the request context | 
				
			||||||
 | 
					        # stays preserved until the next request in the same thread comes | 
				
			||||||
 | 
					        # in.  See RequestGlobals.push() for the general behavior. | 
				
			||||||
 | 
					        top = _request_ctx_stack.top | 
				
			||||||
 | 
					        if top is not None and top.preserved: | 
				
			||||||
 | 
					            top.pop() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FlaskCliRunner(CliRunner): | 
				
			||||||
 | 
					    """A :class:`~click.testing.CliRunner` for testing a Flask app's | 
				
			||||||
 | 
					    CLI commands. Typically created using | 
				
			||||||
 | 
					    :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def __init__(self, app, **kwargs): | 
				
			||||||
 | 
					        self.app = app | 
				
			||||||
 | 
					        super(FlaskCliRunner, self).__init__(**kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def invoke(self, cli=None, args=None, **kwargs): | 
				
			||||||
 | 
					        """Invokes a CLI command in an isolated environment. See | 
				
			||||||
 | 
					        :meth:`CliRunner.invoke <click.testing.CliRunner.invoke>` for | 
				
			||||||
 | 
					        full method documentation. See :ref:`testing-cli` for examples. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If the ``obj`` argument is not given, passes an instance of | 
				
			||||||
 | 
					        :class:`~flask.cli.ScriptInfo` that knows how to load the Flask | 
				
			||||||
 | 
					        app being tested. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param cli: Command object to invoke. Default is the app's | 
				
			||||||
 | 
					            :attr:`~flask.app.Flask.cli` group. | 
				
			||||||
 | 
					        :param args: List of strings to invoke the command with. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :return: a :class:`~click.testing.Result` object. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if cli is None: | 
				
			||||||
 | 
					            cli = self.app.cli | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'obj' not in kwargs: | 
				
			||||||
 | 
					            kwargs['obj'] = ScriptInfo(create_app=lambda: self.app) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return super(FlaskCliRunner, self).invoke(cli, args, **kwargs) | 
				
			||||||
@ -0,0 +1,158 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.views | 
				
			||||||
 | 
					    ~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This module provides class-based views inspired by the ones in Django. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .globals import request | 
				
			||||||
 | 
					from ._compat import with_metaclass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					http_method_funcs = frozenset(['get', 'post', 'head', 'options', | 
				
			||||||
 | 
					                               'delete', 'put', 'trace', 'patch']) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class View(object): | 
				
			||||||
 | 
					    """Alternative way to use view functions.  A subclass has to implement | 
				
			||||||
 | 
					    :meth:`dispatch_request` which is called with the view arguments from | 
				
			||||||
 | 
					    the URL routing system.  If :attr:`methods` is provided the methods | 
				
			||||||
 | 
					    do not have to be passed to the :meth:`~flask.Flask.add_url_rule` | 
				
			||||||
 | 
					    method explicitly:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class MyView(View): | 
				
			||||||
 | 
					            methods = ['GET'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def dispatch_request(self, name): | 
				
			||||||
 | 
					                return 'Hello %s!' % name | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    When you want to decorate a pluggable view you will have to either do that | 
				
			||||||
 | 
					    when the view function is created (by wrapping the return value of | 
				
			||||||
 | 
					    :meth:`as_view`) or you can use the :attr:`decorators` attribute:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class SecretView(View): | 
				
			||||||
 | 
					            methods = ['GET'] | 
				
			||||||
 | 
					            decorators = [superuser_required] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def dispatch_request(self): | 
				
			||||||
 | 
					                ... | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The decorators stored in the decorators list are applied one after another | 
				
			||||||
 | 
					    when the view function is created.  Note that you can *not* use the class | 
				
			||||||
 | 
					    based decorators since those would decorate the view class and not the | 
				
			||||||
 | 
					    generated view function! | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: A list of methods this view can handle. | 
				
			||||||
 | 
					    methods = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: Setting this disables or force-enables the automatic options handling. | 
				
			||||||
 | 
					    provide_automatic_options = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: The canonical way to decorate class-based views is to decorate the | 
				
			||||||
 | 
					    #: return value of as_view().  However since this moves parts of the | 
				
			||||||
 | 
					    #: logic from the class declaration to the place where it's hooked | 
				
			||||||
 | 
					    #: into the routing system. | 
				
			||||||
 | 
					    #: | 
				
			||||||
 | 
					    #: You can place one or more decorators in this list and whenever the | 
				
			||||||
 | 
					    #: view function is created the result is automatically decorated. | 
				
			||||||
 | 
					    #: | 
				
			||||||
 | 
					    #: .. versionadded:: 0.8 | 
				
			||||||
 | 
					    decorators = () | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dispatch_request(self): | 
				
			||||||
 | 
					        """Subclasses have to override this method to implement the | 
				
			||||||
 | 
					        actual view function code.  This method is called with all | 
				
			||||||
 | 
					        the arguments from the URL rule. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod | 
				
			||||||
 | 
					    def as_view(cls, name, *class_args, **class_kwargs): | 
				
			||||||
 | 
					        """Converts the class into an actual view function that can be used | 
				
			||||||
 | 
					        with the routing system.  Internally this generates a function on the | 
				
			||||||
 | 
					        fly which will instantiate the :class:`View` on each request and call | 
				
			||||||
 | 
					        the :meth:`dispatch_request` method on it. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The arguments passed to :meth:`as_view` are forwarded to the | 
				
			||||||
 | 
					        constructor of the class. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        def view(*args, **kwargs): | 
				
			||||||
 | 
					            self = view.view_class(*class_args, **class_kwargs) | 
				
			||||||
 | 
					            return self.dispatch_request(*args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if cls.decorators: | 
				
			||||||
 | 
					            view.__name__ = name | 
				
			||||||
 | 
					            view.__module__ = cls.__module__ | 
				
			||||||
 | 
					            for decorator in cls.decorators: | 
				
			||||||
 | 
					                view = decorator(view) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # We attach the view class to the view function for two reasons: | 
				
			||||||
 | 
					        # first of all it allows us to easily figure out what class-based | 
				
			||||||
 | 
					        # view this thing came from, secondly it's also used for instantiating | 
				
			||||||
 | 
					        # the view class so you can actually replace it with something else | 
				
			||||||
 | 
					        # for testing purposes and debugging. | 
				
			||||||
 | 
					        view.view_class = cls | 
				
			||||||
 | 
					        view.__name__ = name | 
				
			||||||
 | 
					        view.__doc__ = cls.__doc__ | 
				
			||||||
 | 
					        view.__module__ = cls.__module__ | 
				
			||||||
 | 
					        view.methods = cls.methods | 
				
			||||||
 | 
					        view.provide_automatic_options = cls.provide_automatic_options | 
				
			||||||
 | 
					        return view | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MethodViewType(type): | 
				
			||||||
 | 
					    """Metaclass for :class:`MethodView` that determines what methods the view | 
				
			||||||
 | 
					    defines. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(cls, name, bases, d): | 
				
			||||||
 | 
					        super(MethodViewType, cls).__init__(name, bases, d) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'methods' not in d: | 
				
			||||||
 | 
					            methods = set() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for key in http_method_funcs: | 
				
			||||||
 | 
					                if hasattr(cls, key): | 
				
			||||||
 | 
					                    methods.add(key.upper()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # If we have no method at all in there we don't want to add a | 
				
			||||||
 | 
					            # method list. This is for instance the case for the base class | 
				
			||||||
 | 
					            # or another subclass of a base method view that does not introduce | 
				
			||||||
 | 
					            # new methods. | 
				
			||||||
 | 
					            if methods: | 
				
			||||||
 | 
					                cls.methods = methods | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MethodView(with_metaclass(MethodViewType, View)): | 
				
			||||||
 | 
					    """A class-based view that dispatches request methods to the corresponding | 
				
			||||||
 | 
					    class methods. For example, if you implement a ``get`` method, it will be | 
				
			||||||
 | 
					    used to handle ``GET`` requests. :: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class CounterAPI(MethodView): | 
				
			||||||
 | 
					            def get(self): | 
				
			||||||
 | 
					                return session.get('counter', 0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def post(self): | 
				
			||||||
 | 
					                session['counter'] = session.get('counter', 0) + 1 | 
				
			||||||
 | 
					                return 'OK' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter')) | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dispatch_request(self, *args, **kwargs): | 
				
			||||||
 | 
					        meth = getattr(self, request.method.lower(), None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # If the request method is HEAD and we don't have a handler for it | 
				
			||||||
 | 
					        # retry with GET. | 
				
			||||||
 | 
					        if meth is None and request.method == 'HEAD': | 
				
			||||||
 | 
					            meth = getattr(self, 'get', None) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert meth is not None, 'Unimplemented method %r' % request.method | 
				
			||||||
 | 
					        return meth(*args, **kwargs) | 
				
			||||||
@ -0,0 +1,216 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 -*- | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					    flask.wrappers | 
				
			||||||
 | 
					    ~~~~~~~~~~~~~~ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Implements the WSGI wrappers (request and response). | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :copyright: © 2010 by the Pallets team. | 
				
			||||||
 | 
					    :license: BSD, see LICENSE for more details. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from werkzeug.exceptions import BadRequest | 
				
			||||||
 | 
					from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from flask import json | 
				
			||||||
 | 
					from flask.globals import current_app | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class JSONMixin(object): | 
				
			||||||
 | 
					    """Common mixin for both request and response objects to provide JSON | 
				
			||||||
 | 
					    parsing capabilities. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 1.0 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _cached_json = (Ellipsis, Ellipsis) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def is_json(self): | 
				
			||||||
 | 
					        """Check if the mimetype indicates JSON data, either | 
				
			||||||
 | 
					        :mimetype:`application/json` or :mimetype:`application/*+json`. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 0.11 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        mt = self.mimetype | 
				
			||||||
 | 
					        return ( | 
				
			||||||
 | 
					            mt == 'application/json' | 
				
			||||||
 | 
					            or (mt.startswith('application/')) and mt.endswith('+json') | 
				
			||||||
 | 
					        ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def json(self): | 
				
			||||||
 | 
					        """This will contain the parsed JSON data if the mimetype indicates | 
				
			||||||
 | 
					        JSON (:mimetype:`application/json`, see :meth:`is_json`), otherwise it | 
				
			||||||
 | 
					        will be ``None``. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return self.get_json() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_data_for_json(self, cache): | 
				
			||||||
 | 
					        return self.get_data(cache=cache) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_json(self, force=False, silent=False, cache=True): | 
				
			||||||
 | 
					        """Parse and return the data as JSON. If the mimetype does not | 
				
			||||||
 | 
					        indicate JSON (:mimetype:`application/json`, see | 
				
			||||||
 | 
					        :meth:`is_json`), this returns ``None`` unless ``force`` is | 
				
			||||||
 | 
					        true. If parsing fails, :meth:`on_json_loading_failed` is called | 
				
			||||||
 | 
					        and its return value is used as the return value. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param force: Ignore the mimetype and always try to parse JSON. | 
				
			||||||
 | 
					        :param silent: Silence parsing errors and return ``None`` | 
				
			||||||
 | 
					            instead. | 
				
			||||||
 | 
					        :param cache: Store the parsed JSON to return for subsequent | 
				
			||||||
 | 
					            calls. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if cache and self._cached_json[silent] is not Ellipsis: | 
				
			||||||
 | 
					            return self._cached_json[silent] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not (force or self.is_json): | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = self._get_data_for_json(cache=cache) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            rv = json.loads(data) | 
				
			||||||
 | 
					        except ValueError as e: | 
				
			||||||
 | 
					            if silent: | 
				
			||||||
 | 
					                rv = None | 
				
			||||||
 | 
					                if cache: | 
				
			||||||
 | 
					                    normal_rv, _ = self._cached_json | 
				
			||||||
 | 
					                    self._cached_json = (normal_rv, rv) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                rv = self.on_json_loading_failed(e) | 
				
			||||||
 | 
					                if cache: | 
				
			||||||
 | 
					                    _, silent_rv = self._cached_json | 
				
			||||||
 | 
					                    self._cached_json = (rv, silent_rv) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            if cache: | 
				
			||||||
 | 
					                self._cached_json = (rv, rv) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return rv | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_json_loading_failed(self, e): | 
				
			||||||
 | 
					        """Called if :meth:`get_json` parsing fails and isn't silenced. If | 
				
			||||||
 | 
					        this method returns a value, it is used as the return value for | 
				
			||||||
 | 
					        :meth:`get_json`. The default implementation raises a | 
				
			||||||
 | 
					        :class:`BadRequest` exception. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionchanged:: 0.10 | 
				
			||||||
 | 
					           Raise a :exc:`BadRequest` error instead of returning an error | 
				
			||||||
 | 
					           message as JSON. If you want that behavior you can add it by | 
				
			||||||
 | 
					           subclassing. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 0.8 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if current_app is not None and current_app.debug: | 
				
			||||||
 | 
					            raise BadRequest('Failed to decode JSON object: {0}'.format(e)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        raise BadRequest() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Request(RequestBase, JSONMixin): | 
				
			||||||
 | 
					    """The request object used by default in Flask.  Remembers the | 
				
			||||||
 | 
					    matched endpoint and view arguments. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    It is what ends up as :class:`~flask.request`.  If you want to replace | 
				
			||||||
 | 
					    the request object used you can subclass this and set | 
				
			||||||
 | 
					    :attr:`~flask.Flask.request_class` to your subclass. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The request object is a :class:`~werkzeug.wrappers.Request` subclass and | 
				
			||||||
 | 
					    provides all of the attributes Werkzeug defines plus a few Flask | 
				
			||||||
 | 
					    specific ones. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: The internal URL rule that matched the request.  This can be | 
				
			||||||
 | 
					    #: useful to inspect which methods are allowed for the URL from | 
				
			||||||
 | 
					    #: a before/after handler (``request.url_rule.methods``) etc. | 
				
			||||||
 | 
					    #: Though if the request's method was invalid for the URL rule, | 
				
			||||||
 | 
					    #: the valid list is available in ``routing_exception.valid_methods`` | 
				
			||||||
 | 
					    #: instead (an attribute of the Werkzeug exception :exc:`~werkzeug.exceptions.MethodNotAllowed`) | 
				
			||||||
 | 
					    #: because the request was never internally bound. | 
				
			||||||
 | 
					    #: | 
				
			||||||
 | 
					    #: .. versionadded:: 0.6 | 
				
			||||||
 | 
					    url_rule = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: A dict of view arguments that matched the request.  If an exception | 
				
			||||||
 | 
					    #: happened when matching, this will be ``None``. | 
				
			||||||
 | 
					    view_args = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #: If matching the URL failed, this is the exception that will be | 
				
			||||||
 | 
					    #: raised / was raised as part of the request handling.  This is | 
				
			||||||
 | 
					    #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or | 
				
			||||||
 | 
					    #: something similar. | 
				
			||||||
 | 
					    routing_exception = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def max_content_length(self): | 
				
			||||||
 | 
					        """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" | 
				
			||||||
 | 
					        if current_app: | 
				
			||||||
 | 
					            return current_app.config['MAX_CONTENT_LENGTH'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def endpoint(self): | 
				
			||||||
 | 
					        """The endpoint that matched the request.  This in combination with | 
				
			||||||
 | 
					        :attr:`view_args` can be used to reconstruct the same or a | 
				
			||||||
 | 
					        modified URL.  If an exception happened when matching, this will | 
				
			||||||
 | 
					        be ``None``. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if self.url_rule is not None: | 
				
			||||||
 | 
					            return self.url_rule.endpoint | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def blueprint(self): | 
				
			||||||
 | 
					        """The name of the current blueprint""" | 
				
			||||||
 | 
					        if self.url_rule and '.' in self.url_rule.endpoint: | 
				
			||||||
 | 
					            return self.url_rule.endpoint.rsplit('.', 1)[0] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _load_form_data(self): | 
				
			||||||
 | 
					        RequestBase._load_form_data(self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # In debug mode we're replacing the files multidict with an ad-hoc | 
				
			||||||
 | 
					        # subclass that raises a different error for key errors. | 
				
			||||||
 | 
					        if ( | 
				
			||||||
 | 
					            current_app | 
				
			||||||
 | 
					            and current_app.debug | 
				
			||||||
 | 
					            and self.mimetype != 'multipart/form-data' | 
				
			||||||
 | 
					            and not self.files | 
				
			||||||
 | 
					        ): | 
				
			||||||
 | 
					            from .debughelpers import attach_enctype_error_multidict | 
				
			||||||
 | 
					            attach_enctype_error_multidict(self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Response(ResponseBase, JSONMixin): | 
				
			||||||
 | 
					    """The response object that is used by default in Flask.  Works like the | 
				
			||||||
 | 
					    response object from Werkzeug but is set to have an HTML mimetype by | 
				
			||||||
 | 
					    default.  Quite often you don't have to create this object yourself because | 
				
			||||||
 | 
					    :meth:`~flask.Flask.make_response` will take care of that for you. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If you want to replace the response object used you can subclass this and | 
				
			||||||
 | 
					    set :attr:`~flask.Flask.response_class` to your subclass. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 1.0 | 
				
			||||||
 | 
					        JSON support is added to the response, like the request. This is useful | 
				
			||||||
 | 
					        when testing to get the test client response data as JSON. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 1.0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Added :attr:`max_cookie_size`. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default_mimetype = 'text/html' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_data_for_json(self, cache): | 
				
			||||||
 | 
					        return self.get_data() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property | 
				
			||||||
 | 
					    def max_cookie_size(self): | 
				
			||||||
 | 
					        """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        See :attr:`~werkzeug.wrappers.BaseResponse.max_cookie_size` in | 
				
			||||||
 | 
					        Werkzeug's docs. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if current_app: | 
				
			||||||
 | 
					            return current_app.config['MAX_COOKIE_SIZE'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # return Werkzeug's default when not in an app context | 
				
			||||||
 | 
					        return super(Response, self).max_cookie_size | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					pip | 
				
			||||||
@ -0,0 +1,23 @@ | 
				
			|||||||
 | 
					2009-2018 (c) Benoît Chesneau <benoitc@e-engura.org> | 
				
			||||||
 | 
					2009-2015 (c) Paul J. Davis <paul.joseph.davis@gmail.com> | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person | 
				
			||||||
 | 
					obtaining a copy of this software and associated documentation | 
				
			||||||
 | 
					files (the "Software"), to deal in the Software without | 
				
			||||||
 | 
					restriction, including without limitation the rights to use, | 
				
			||||||
 | 
					copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			||||||
 | 
					copies of the Software, and to permit persons to whom the | 
				
			||||||
 | 
					Software is furnished to do so, subject to the following | 
				
			||||||
 | 
					conditions: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be | 
				
			||||||
 | 
					included in all copies or substantial portions of the Software. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
				
			||||||
 | 
					EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | 
				
			||||||
 | 
					OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
				
			||||||
 | 
					NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | 
				
			||||||
 | 
					HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | 
				
			||||||
 | 
					WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | 
				
			||||||
 | 
					FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | 
				
			||||||
 | 
					OTHER DEALINGS IN THE SOFTWARE. | 
				
			||||||
@ -0,0 +1,111 @@ | 
				
			|||||||
 | 
					Metadata-Version: 2.1 | 
				
			||||||
 | 
					Name: gunicorn | 
				
			||||||
 | 
					Version: 19.8.1 | 
				
			||||||
 | 
					Summary: WSGI HTTP Server for UNIX | 
				
			||||||
 | 
					Home-page: http://gunicorn.org | 
				
			||||||
 | 
					Author: Benoit Chesneau | 
				
			||||||
 | 
					Author-email: benoitc@e-engura.com | 
				
			||||||
 | 
					License: MIT | 
				
			||||||
 | 
					Platform: UNKNOWN | 
				
			||||||
 | 
					Classifier: Development Status :: 4 - Beta | 
				
			||||||
 | 
					Classifier: Environment :: Other Environment | 
				
			||||||
 | 
					Classifier: Intended Audience :: Developers | 
				
			||||||
 | 
					Classifier: License :: OSI Approved :: MIT License | 
				
			||||||
 | 
					Classifier: Operating System :: MacOS :: MacOS X | 
				
			||||||
 | 
					Classifier: Operating System :: POSIX | 
				
			||||||
 | 
					Classifier: Programming Language :: Python | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.6 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 2.7 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.2 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.3 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.4 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.5 | 
				
			||||||
 | 
					Classifier: Programming Language :: Python :: 3.6 | 
				
			||||||
 | 
					Classifier: Topic :: Internet | 
				
			||||||
 | 
					Classifier: Topic :: Utilities | 
				
			||||||
 | 
					Classifier: Topic :: Software Development :: Libraries :: Python Modules | 
				
			||||||
 | 
					Classifier: Topic :: Internet :: WWW/HTTP | 
				
			||||||
 | 
					Classifier: Topic :: Internet :: WWW/HTTP :: WSGI | 
				
			||||||
 | 
					Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server | 
				
			||||||
 | 
					Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content | 
				
			||||||
 | 
					Requires-Python: >=2.6, !=3.0.*, !=3.1.* | 
				
			||||||
 | 
					Provides-Extra: gevent | 
				
			||||||
 | 
					Provides-Extra: tornado | 
				
			||||||
 | 
					Provides-Extra: eventlet | 
				
			||||||
 | 
					Provides-Extra: gthread | 
				
			||||||
 | 
					Provides-Extra: eventlet | 
				
			||||||
 | 
					Requires-Dist: eventlet (>=0.9.7); extra == 'eventlet' | 
				
			||||||
 | 
					Provides-Extra: gevent | 
				
			||||||
 | 
					Requires-Dist: gevent (>=0.13); extra == 'gevent' | 
				
			||||||
 | 
					Provides-Extra: gthread | 
				
			||||||
 | 
					Provides-Extra: tornado | 
				
			||||||
 | 
					Requires-Dist: tornado (>=0.2); extra == 'tornado' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Gunicorn | 
				
			||||||
 | 
					-------- | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. image:: https://img.shields.io/pypi/v/gunicorn.svg?style=flat | 
				
			||||||
 | 
					    :alt: PyPI version | 
				
			||||||
 | 
					    :target: https://pypi.python.org/pypi/gunicorn | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. image:: https://img.shields.io/pypi/pyversions/gunicorn.svg | 
				
			||||||
 | 
					    :alt: Supported Python versions | 
				
			||||||
 | 
					    :target: https://pypi.python.org/pypi/gunicorn | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. image:: https://travis-ci.org/benoitc/gunicorn.svg?branch=master | 
				
			||||||
 | 
					    :alt: Build Status | 
				
			||||||
 | 
					    :target: https://travis-ci.org/benoitc/gunicorn | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Gunicorn 'Green Unicorn' is a Python WSGI HTTP Server for UNIX. It's a pre-fork | 
				
			||||||
 | 
					worker model ported from Ruby's Unicorn_ project. The Gunicorn server is broadly | 
				
			||||||
 | 
					compatible with various web frameworks, simply implemented, light on server | 
				
			||||||
 | 
					resource usage, and fairly speedy. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Feel free to join us in `#gunicorn`_ on Freenode_. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Documentation | 
				
			||||||
 | 
					------------- | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The documentation is hosted at http://docs.gunicorn.org. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Installation | 
				
			||||||
 | 
					------------ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Gunicorn requires **Python 2.x >= 2.6** or **Python 3.x >= 3.2**. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Install from PyPI:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $ pip install gunicorn | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Usage | 
				
			||||||
 | 
					----- | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Basic usage:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $ gunicorn [OPTIONS] APP_MODULE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Where ``APP_MODULE`` is of the pattern ``$(MODULE_NAME):$(VARIABLE_NAME)``. The | 
				
			||||||
 | 
					module name can be a full dotted path. The variable name refers to a WSGI | 
				
			||||||
 | 
					callable that should be found in the specified module. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example with test app:: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $ cd examples | 
				
			||||||
 | 
					    $ gunicorn --workers=2 test:app | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					License | 
				
			||||||
 | 
					------- | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Gunicorn is released under the MIT License. See the LICENSE_ file for more | 
				
			||||||
 | 
					details. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _Unicorn: https://bogomips.org/unicorn/ | 
				
			||||||
 | 
					.. _`#gunicorn`: https://webchat.freenode.net/?channels=gunicorn | 
				
			||||||
 | 
					.. _Freenode: https://freenode.net/ | 
				
			||||||
 | 
					.. _LICENSE: https://github.com/benoitc/gunicorn/blob/master/LICENSE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,88 @@ | 
				
			|||||||
 | 
					gunicorn/__init__.py,sha256=Y1BlVaRVNjIKXqSzcqTNHo9dq2eG6kgPx5jWJ3h5GRE,255 | 
				
			||||||
 | 
					gunicorn/_compat.py,sha256=5cXb6vMfVzInDq-AHNyZfsK-UG5NetDn62nPfqylHSU,9355 | 
				
			||||||
 | 
					gunicorn/arbiter.py,sha256=AbJNSFnTmx9Qd-vZAqEH3y5fz8ydPmyli_BERNIwdyE,20158 | 
				
			||||||
 | 
					gunicorn/argparse_compat.py,sha256=gsHDGwo4BSJWHdiaEXy0Emr96NKC0LDYmK5nB7PE8Qc,87791 | 
				
			||||||
 | 
					gunicorn/config.py,sha256=HDoWZ0JyoPzl2WW6hlDatDevvhfah4oS_j7tOq8Pa7E,53417 | 
				
			||||||
 | 
					gunicorn/debug.py,sha256=UUw-eteLEm_OQ98D6K3XtDjx4Dya2H35zdiu8z7F7uc,2289 | 
				
			||||||
 | 
					gunicorn/errors.py,sha256=JlDBjag90gMiRwLHG3xzEJzDOntSl1iM32R277-U6j0,919 | 
				
			||||||
 | 
					gunicorn/glogging.py,sha256=PMdoe6hdBQWKGlnP4lXdBph6b1ygD0kknxkDsmNIVSU,15554 | 
				
			||||||
 | 
					gunicorn/pidfile.py,sha256=_69tsfF1aHklrMrJe2sHERovMduRByVTv99my7yQ874,2357 | 
				
			||||||
 | 
					gunicorn/reloader.py,sha256=fh4J7w_DxWaFuFd3G4RyOgDFs1C1lrd0w7jOXItSu5g,3791 | 
				
			||||||
 | 
					gunicorn/selectors.py,sha256=14_UESrpE3AQKXWKeeAUG9vBTzJ0yTYDGtEo6xOtlDY,18997 | 
				
			||||||
 | 
					gunicorn/six.py,sha256=6N-6RCENPfBtMpN5UmgDfDKmJebbbuPu_Dk3Zf8ngww,27344 | 
				
			||||||
 | 
					gunicorn/sock.py,sha256=gX2FsdsOGMCtSHbDXn7lsiYYYRc3roQklIJLip1oZQo,6019 | 
				
			||||||
 | 
					gunicorn/systemd.py,sha256=ffhv17cdv-hDeFAJi1eAVtJskkVciV6cQU75Q2oplqg,1362 | 
				
			||||||
 | 
					gunicorn/util.py,sha256=Ns_a8Pf7MkaEi0KbV3GsP9aVQ2a_S45EjSE6Iyg2tYU,16229 | 
				
			||||||
 | 
					gunicorn/app/__init__.py,sha256=GuqstqdkizeV4HRbd8aGMBn0Q8IDOyRU1wMMNqNe5GY,127 | 
				
			||||||
 | 
					gunicorn/app/base.py,sha256=LKxyziLMPNlK3qm6dPMieELBqfLfmwBFnn9SB-KBogE,6652 | 
				
			||||||
 | 
					gunicorn/app/pasterapp.py,sha256=AGzZnUpcpw8O8KrizxTgdJBZ4lQdrHgsV0gdx7FVTs8,6046 | 
				
			||||||
 | 
					gunicorn/app/wsgiapp.py,sha256=ny71qjegQHl_bGMjNfq_aemPrmGEpH2bMRIdph6bj4Q,1870 | 
				
			||||||
 | 
					gunicorn/http/__init__.py,sha256=b4TF3x5F0VYOPTOeNYwRGR1EYHBaPMhZRMoNeuD5-n0,277 | 
				
			||||||
 | 
					gunicorn/http/_sendfile.py,sha256=Eqd-s3HlvLuyfGjqaH_Jk72cAtEV8hQv5tb1M1AqcBU,2217 | 
				
			||||||
 | 
					gunicorn/http/body.py,sha256=MmlZpj_6oRPj3oPVSMQZr0X3KH6ikntxDnVcLgfekZs,7345 | 
				
			||||||
 | 
					gunicorn/http/errors.py,sha256=sNjF2lm4m2qyZ9l95_U33FRxPXpxXzjnZyYqWS-hxd4,2850 | 
				
			||||||
 | 
					gunicorn/http/message.py,sha256=G5po0upwbrTyIggb_IEAItIjSi_aDoWYLPQ62o8pOI4,12257 | 
				
			||||||
 | 
					gunicorn/http/parser.py,sha256=IRMvp0veP4wL8Z4vgNV72CPydCNPdNNIy9u-DlDvvSo,1294 | 
				
			||||||
 | 
					gunicorn/http/unreader.py,sha256=s4kDW5euiJPsDuHzCqFXUtHCApqIxpShb9dtAyjJw9Y,2019 | 
				
			||||||
 | 
					gunicorn/http/wsgi.py,sha256=_5zYFl5HGKrPpEMrEnsLyPreOJcTgaYzsEBuJkEOmko,12852 | 
				
			||||||
 | 
					gunicorn/instrument/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 | 
				
			||||||
 | 
					gunicorn/instrument/statsd.py,sha256=5xueDuTZMFtmS8ayGT4sU_OyB9qkEv4Agk-eJwAmhJM,4434 | 
				
			||||||
 | 
					gunicorn/workers/__init__.py,sha256=Z57G1WjnZDCG52C8PgiXF4mKRKqlv81b2GHkhOJiO6A,774 | 
				
			||||||
 | 
					gunicorn/workers/_gaiohttp.py,sha256=CFKiyLNqWqemhDvDovb-JqMRTMNz50gZUwwOpIjrpHw,5071 | 
				
			||||||
 | 
					gunicorn/workers/async.py,sha256=54VkS3S_wrFD7v3jInhFfkeBhaPnV5UN-cu-i5MoXkc,5575 | 
				
			||||||
 | 
					gunicorn/workers/base.py,sha256=eYdcy2EPlydcjyi2CpgeU0tqlJpz3_kt-RbGhoamvQ8,9126 | 
				
			||||||
 | 
					gunicorn/workers/gaiohttp.py,sha256=3rhXky6APkhI0D9nwXlogLo_Jd9v98CiEuCy9inzCU4,823 | 
				
			||||||
 | 
					gunicorn/workers/geventlet.py,sha256=L7N2bizKQw8VXb02teu1_wYVG5hwt9SSaQn7J0kSKyI,4253 | 
				
			||||||
 | 
					gunicorn/workers/ggevent.py,sha256=hzx2kOZP13yVBz-EBthoTgjalOeRKfoUk5XchmRKzDM,7407 | 
				
			||||||
 | 
					gunicorn/workers/gthread.py,sha256=HIoWuylHZfH1wlSh4eZ8wxo1kQ5abvdUaFfKfIsgQvI,12009 | 
				
			||||||
 | 
					gunicorn/workers/gtornado.py,sha256=LtBWnEX7MNpeGX-YmlBoV1_OOhjkdytFmt1pzOlRPZk,5044 | 
				
			||||||
 | 
					gunicorn/workers/sync.py,sha256=_vd1JATNLG4MgJppNJG5KWBIzLGYqRzhEAQVz9H11LI,7153 | 
				
			||||||
 | 
					gunicorn/workers/workertmp.py,sha256=6QINPBrriLvezgkC_hclOOeXLi_owMt_SOA5KPEIN-A,1459 | 
				
			||||||
 | 
					gunicorn-19.8.1.dist-info/LICENSE.txt,sha256=eJ_hG5Lhyr-890S1_MOSyb1cZ5hgOk6J-SW2M3mE0d8,1136 | 
				
			||||||
 | 
					gunicorn-19.8.1.dist-info/METADATA,sha256=w9Od3TWYwIXsx_CAyLy5OZ1h5v9C_T0_mhK9psl1x5Q,3388 | 
				
			||||||
 | 
					gunicorn-19.8.1.dist-info/RECORD,, | 
				
			||||||
 | 
					gunicorn-19.8.1.dist-info/WHEEL,sha256=J3CsTk7Mf2JNUyhImI-mjX-fmI4oDjyiXgWT4qgZiCE,110 | 
				
			||||||
 | 
					gunicorn-19.8.1.dist-info/entry_points.txt,sha256=XeFINKRdSUKwJwaVSolO24PuV_YeO71IMF-rOra5JO8,184 | 
				
			||||||
 | 
					gunicorn-19.8.1.dist-info/top_level.txt,sha256=cdMaa2yhxb8do-WioY9qRHUCfwf55YztjwQCncaInoE,9 | 
				
			||||||
 | 
					../../../bin/gunicorn,sha256=3n02jiI_1KSuGRVnArdMBsMwXwNk9qYNxest7n5NCtA,226 | 
				
			||||||
 | 
					../../../bin/gunicorn_paster,sha256=VpfTHNYpEn2RVEjyLdmq1-_lArYY2joHAgAlGrbIjeg,228 | 
				
			||||||
 | 
					gunicorn-19.8.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 | 
				
			||||||
 | 
					gunicorn/debug.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/sync.pyc,, | 
				
			||||||
 | 
					gunicorn/http/wsgi.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/async.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/workertmp.pyc,, | 
				
			||||||
 | 
					gunicorn/http/message.pyc,, | 
				
			||||||
 | 
					gunicorn/instrument/__init__.pyc,, | 
				
			||||||
 | 
					gunicorn/http/unreader.pyc,, | 
				
			||||||
 | 
					gunicorn/app/__init__.pyc,, | 
				
			||||||
 | 
					gunicorn/systemd.pyc,, | 
				
			||||||
 | 
					gunicorn/config.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/base.pyc,, | 
				
			||||||
 | 
					gunicorn/app/wsgiapp.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/gaiohttp.pyc,, | 
				
			||||||
 | 
					gunicorn/app/base.pyc,, | 
				
			||||||
 | 
					gunicorn/arbiter.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/ggevent.pyc,, | 
				
			||||||
 | 
					gunicorn/http/errors.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/gtornado.pyc,, | 
				
			||||||
 | 
					gunicorn/six.pyc,, | 
				
			||||||
 | 
					gunicorn/http/__init__.pyc,, | 
				
			||||||
 | 
					gunicorn/pidfile.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/gthread.pyc,, | 
				
			||||||
 | 
					gunicorn/_compat.pyc,, | 
				
			||||||
 | 
					gunicorn/errors.pyc,, | 
				
			||||||
 | 
					gunicorn/http/body.pyc,, | 
				
			||||||
 | 
					gunicorn/instrument/statsd.pyc,, | 
				
			||||||
 | 
					gunicorn/__init__.pyc,, | 
				
			||||||
 | 
					gunicorn/http/parser.pyc,, | 
				
			||||||
 | 
					gunicorn/selectors.pyc,, | 
				
			||||||
 | 
					gunicorn/sock.pyc,, | 
				
			||||||
 | 
					gunicorn/reloader.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/__init__.pyc,, | 
				
			||||||
 | 
					gunicorn/argparse_compat.pyc,, | 
				
			||||||
 | 
					gunicorn/glogging.pyc,, | 
				
			||||||
 | 
					gunicorn/http/_sendfile.pyc,, | 
				
			||||||
 | 
					gunicorn/workers/geventlet.pyc,, | 
				
			||||||
 | 
					gunicorn/util.pyc,, | 
				
			||||||
 | 
					gunicorn/app/pasterapp.pyc,, | 
				
			||||||
@ -0,0 +1,6 @@ | 
				
			|||||||
 | 
					Wheel-Version: 1.0 | 
				
			||||||
 | 
					Generator: bdist_wheel (0.31.0) | 
				
			||||||
 | 
					Root-Is-Purelib: true | 
				
			||||||
 | 
					Tag: py2-none-any | 
				
			||||||
 | 
					Tag: py3-none-any | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,8 @@ | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					    [console_scripts] | 
				
			||||||
 | 
					    gunicorn=gunicorn.app.wsgiapp:run | 
				
			||||||
 | 
					    gunicorn_paster=gunicorn.app.pasterapp:run | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [paste.server_runner] | 
				
			||||||
 | 
					    main=gunicorn.app.pasterapp:paste_server | 
				
			||||||
 | 
					     | 
				
			||||||
@ -0,0 +1 @@ | 
				
			|||||||
 | 
					gunicorn | 
				
			||||||
@ -0,0 +1,8 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					version_info = (19, 8, 1) | 
				
			||||||
 | 
					__version__ = ".".join([str(v) for v in version_info]) | 
				
			||||||
 | 
					SERVER_SOFTWARE = "gunicorn/%s" % __version__ | 
				
			||||||
@ -0,0 +1,298 @@ | 
				
			|||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn import six | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PY26 = (sys.version_info[:2] == (2, 6)) | 
				
			||||||
 | 
					PY33 = (sys.version_info >= (3, 3)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _check_if_pyc(fname): | 
				
			||||||
 | 
					    """Return True if the extension is .pyc, False if .py | 
				
			||||||
 | 
					    and None if otherwise""" | 
				
			||||||
 | 
					    from imp import find_module | 
				
			||||||
 | 
					    from os.path import realpath, dirname, basename, splitext | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Normalize the file-path for the find_module() | 
				
			||||||
 | 
					    filepath = realpath(fname) | 
				
			||||||
 | 
					    dirpath = dirname(filepath) | 
				
			||||||
 | 
					    module_name = splitext(basename(filepath))[0] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Validate and fetch | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        fileobj, fullpath, (_, _, pytype) = find_module(module_name, [dirpath]) | 
				
			||||||
 | 
					    except ImportError: | 
				
			||||||
 | 
					        raise IOError("Cannot find config file. " | 
				
			||||||
 | 
					                      "Path maybe incorrect! : {0}".format(filepath)) | 
				
			||||||
 | 
					    return pytype, fileobj, fullpath | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _get_codeobj(pyfile): | 
				
			||||||
 | 
					    """ Returns the code object, given a python file """ | 
				
			||||||
 | 
					    from imp import PY_COMPILED, PY_SOURCE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result, fileobj, fullpath = _check_if_pyc(pyfile) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # WARNING: | 
				
			||||||
 | 
					    # fp.read() can blowup if the module is extremely large file. | 
				
			||||||
 | 
					    # Lookout for overflow errors. | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        data = fileobj.read() | 
				
			||||||
 | 
					    finally: | 
				
			||||||
 | 
					        fileobj.close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # This is a .pyc file. Treat accordingly. | 
				
			||||||
 | 
					    if result is PY_COMPILED: | 
				
			||||||
 | 
					        # .pyc format is as follows: | 
				
			||||||
 | 
					        # 0 - 4 bytes: Magic number, which changes with each create of .pyc file. | 
				
			||||||
 | 
					        #              First 2 bytes change with each marshal of .pyc file. Last 2 bytes is "\r\n". | 
				
			||||||
 | 
					        # 4 - 8 bytes: Datetime value, when the .py was last changed. | 
				
			||||||
 | 
					        # 8 - EOF: Marshalled code object data. | 
				
			||||||
 | 
					        # So to get code object, just read the 8th byte onwards till EOF, and | 
				
			||||||
 | 
					        # UN-marshal it. | 
				
			||||||
 | 
					        import marshal | 
				
			||||||
 | 
					        code_obj = marshal.loads(data[8:]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    elif result is PY_SOURCE: | 
				
			||||||
 | 
					        # This is a .py file. | 
				
			||||||
 | 
					        code_obj = compile(data, fullpath, 'exec') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        # Unsupported extension | 
				
			||||||
 | 
					        raise Exception("Input file is unknown format: {0}".format(fullpath)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Return code object | 
				
			||||||
 | 
					    return code_obj | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if six.PY3: | 
				
			||||||
 | 
					    def execfile_(fname, *args): | 
				
			||||||
 | 
					        if fname.endswith(".pyc"): | 
				
			||||||
 | 
					            code = _get_codeobj(fname) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            code = compile(open(fname, 'rb').read(), fname, 'exec') | 
				
			||||||
 | 
					        return six.exec_(code, *args) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def bytes_to_str(b): | 
				
			||||||
 | 
					        if isinstance(b, six.text_type): | 
				
			||||||
 | 
					            return b | 
				
			||||||
 | 
					        return str(b, 'latin1') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import urllib.parse | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def unquote_to_wsgi_str(string): | 
				
			||||||
 | 
					        return _unquote_to_bytes(string).decode('latin-1') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _unquote_to_bytes = urllib.parse.unquote_to_bytes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def execfile_(fname, *args): | 
				
			||||||
 | 
					        """ Overriding PY2 execfile() implementation to support .pyc files """ | 
				
			||||||
 | 
					        if fname.endswith(".pyc"): | 
				
			||||||
 | 
					            return six.exec_(_get_codeobj(fname), *args) | 
				
			||||||
 | 
					        return execfile(fname, *args) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def bytes_to_str(s): | 
				
			||||||
 | 
					        if isinstance(s, unicode): | 
				
			||||||
 | 
					            return s.encode('utf-8') | 
				
			||||||
 | 
					        return s | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import urllib | 
				
			||||||
 | 
					    unquote_to_wsgi_str = urllib.unquote | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The following code adapted from trollius.py33_exceptions | 
				
			||||||
 | 
					def _wrap_error(exc, mapping, key): | 
				
			||||||
 | 
					    if key not in mapping: | 
				
			||||||
 | 
					        return | 
				
			||||||
 | 
					    new_err_cls = mapping[key] | 
				
			||||||
 | 
					    new_err = new_err_cls(*exc.args) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # raise a new exception with the original traceback | 
				
			||||||
 | 
					    six.reraise(new_err_cls, new_err, | 
				
			||||||
 | 
					                exc.__traceback__ if hasattr(exc, '__traceback__') else sys.exc_info()[2]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY33: | 
				
			||||||
 | 
					    import builtins | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BlockingIOError = builtins.BlockingIOError | 
				
			||||||
 | 
					    BrokenPipeError = builtins.BrokenPipeError | 
				
			||||||
 | 
					    ChildProcessError = builtins.ChildProcessError | 
				
			||||||
 | 
					    ConnectionRefusedError = builtins.ConnectionRefusedError | 
				
			||||||
 | 
					    ConnectionResetError = builtins.ConnectionResetError | 
				
			||||||
 | 
					    InterruptedError = builtins.InterruptedError | 
				
			||||||
 | 
					    ConnectionAbortedError = builtins.ConnectionAbortedError | 
				
			||||||
 | 
					    PermissionError = builtins.PermissionError | 
				
			||||||
 | 
					    FileNotFoundError = builtins.FileNotFoundError | 
				
			||||||
 | 
					    ProcessLookupError = builtins.ProcessLookupError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wrap_error(func, *args, **kw): | 
				
			||||||
 | 
					        return func(*args, **kw) | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    import errno | 
				
			||||||
 | 
					    import select | 
				
			||||||
 | 
					    import socket | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class BlockingIOError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class BrokenPipeError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ChildProcessError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ConnectionRefusedError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class InterruptedError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ConnectionResetError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ConnectionAbortedError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class PermissionError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class FileNotFoundError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class ProcessLookupError(OSError): | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _MAP_ERRNO = { | 
				
			||||||
 | 
					        errno.EACCES: PermissionError, | 
				
			||||||
 | 
					        errno.EAGAIN: BlockingIOError, | 
				
			||||||
 | 
					        errno.EALREADY: BlockingIOError, | 
				
			||||||
 | 
					        errno.ECHILD: ChildProcessError, | 
				
			||||||
 | 
					        errno.ECONNABORTED: ConnectionAbortedError, | 
				
			||||||
 | 
					        errno.ECONNREFUSED: ConnectionRefusedError, | 
				
			||||||
 | 
					        errno.ECONNRESET: ConnectionResetError, | 
				
			||||||
 | 
					        errno.EINPROGRESS: BlockingIOError, | 
				
			||||||
 | 
					        errno.EINTR: InterruptedError, | 
				
			||||||
 | 
					        errno.ENOENT: FileNotFoundError, | 
				
			||||||
 | 
					        errno.EPERM: PermissionError, | 
				
			||||||
 | 
					        errno.EPIPE: BrokenPipeError, | 
				
			||||||
 | 
					        errno.ESHUTDOWN: BrokenPipeError, | 
				
			||||||
 | 
					        errno.EWOULDBLOCK: BlockingIOError, | 
				
			||||||
 | 
					        errno.ESRCH: ProcessLookupError, | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wrap_error(func, *args, **kw): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Wrap socket.error, IOError, OSError, select.error to raise new specialized | 
				
			||||||
 | 
					        exceptions of Python 3.3 like InterruptedError (PEP 3151). | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return func(*args, **kw) | 
				
			||||||
 | 
					        except (socket.error, IOError, OSError) as exc: | 
				
			||||||
 | 
					            if hasattr(exc, 'winerror'): | 
				
			||||||
 | 
					                _wrap_error(exc, _MAP_ERRNO, exc.winerror) | 
				
			||||||
 | 
					                # _MAP_ERRNO does not contain all Windows errors. | 
				
			||||||
 | 
					                # For some errors like "file not found", exc.errno should | 
				
			||||||
 | 
					                # be used (ex: ENOENT). | 
				
			||||||
 | 
					            _wrap_error(exc, _MAP_ERRNO, exc.errno) | 
				
			||||||
 | 
					            raise | 
				
			||||||
 | 
					        except select.error as exc: | 
				
			||||||
 | 
					            if exc.args: | 
				
			||||||
 | 
					                _wrap_error(exc, _MAP_ERRNO, exc.args[0]) | 
				
			||||||
 | 
					            raise | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY26: | 
				
			||||||
 | 
					    from urlparse import ( | 
				
			||||||
 | 
					        _parse_cache, MAX_CACHE_SIZE, clear_cache, _splitnetloc, SplitResult, | 
				
			||||||
 | 
					        scheme_chars, | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def urlsplit(url, scheme='', allow_fragments=True): | 
				
			||||||
 | 
					        """Parse a URL into 5 components: | 
				
			||||||
 | 
					        <scheme>://<netloc>/<path>?<query>#<fragment> | 
				
			||||||
 | 
					        Return a 5-tuple: (scheme, netloc, path, query, fragment). | 
				
			||||||
 | 
					        Note that we don't break the components up in smaller bits | 
				
			||||||
 | 
					        (e.g. netloc is a single string) and we don't expand % escapes.""" | 
				
			||||||
 | 
					        allow_fragments = bool(allow_fragments) | 
				
			||||||
 | 
					        key = url, scheme, allow_fragments, type(url), type(scheme) | 
				
			||||||
 | 
					        cached = _parse_cache.get(key, None) | 
				
			||||||
 | 
					        if cached: | 
				
			||||||
 | 
					            return cached | 
				
			||||||
 | 
					        if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth | 
				
			||||||
 | 
					            clear_cache() | 
				
			||||||
 | 
					        netloc = query = fragment = '' | 
				
			||||||
 | 
					        i = url.find(':') | 
				
			||||||
 | 
					        if i > 0: | 
				
			||||||
 | 
					            if url[:i] == 'http': # optimize the common case | 
				
			||||||
 | 
					                scheme = url[:i].lower() | 
				
			||||||
 | 
					                url = url[i+1:] | 
				
			||||||
 | 
					                if url[:2] == '//': | 
				
			||||||
 | 
					                    netloc, url = _splitnetloc(url, 2) | 
				
			||||||
 | 
					                    if (('[' in netloc and ']' not in netloc) or | 
				
			||||||
 | 
					                            (']' in netloc and '[' not in netloc)): | 
				
			||||||
 | 
					                        raise ValueError("Invalid IPv6 URL") | 
				
			||||||
 | 
					                if allow_fragments and '#' in url: | 
				
			||||||
 | 
					                    url, fragment = url.split('#', 1) | 
				
			||||||
 | 
					                if '?' in url: | 
				
			||||||
 | 
					                    url, query = url.split('?', 1) | 
				
			||||||
 | 
					                v = SplitResult(scheme, netloc, url, query, fragment) | 
				
			||||||
 | 
					                _parse_cache[key] = v | 
				
			||||||
 | 
					                return v | 
				
			||||||
 | 
					            for c in url[:i]: | 
				
			||||||
 | 
					                if c not in scheme_chars: | 
				
			||||||
 | 
					                    break | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                # make sure "url" is not actually a port number (in which case | 
				
			||||||
 | 
					                # "scheme" is really part of the path) | 
				
			||||||
 | 
					                rest = url[i+1:] | 
				
			||||||
 | 
					                if not rest or any(c not in '0123456789' for c in rest): | 
				
			||||||
 | 
					                    # not a port number | 
				
			||||||
 | 
					                    scheme, url = url[:i].lower(), rest | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if url[:2] == '//': | 
				
			||||||
 | 
					            netloc, url = _splitnetloc(url, 2) | 
				
			||||||
 | 
					            if (('[' in netloc and ']' not in netloc) or | 
				
			||||||
 | 
					                    (']' in netloc and '[' not in netloc)): | 
				
			||||||
 | 
					                raise ValueError("Invalid IPv6 URL") | 
				
			||||||
 | 
					        if allow_fragments and '#' in url: | 
				
			||||||
 | 
					            url, fragment = url.split('#', 1) | 
				
			||||||
 | 
					        if '?' in url: | 
				
			||||||
 | 
					            url, query = url.split('?', 1) | 
				
			||||||
 | 
					        v = SplitResult(scheme, netloc, url, query, fragment) | 
				
			||||||
 | 
					        _parse_cache[key] = v | 
				
			||||||
 | 
					        return v | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    from gunicorn.six.moves.urllib.parse import urlsplit | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import inspect | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if hasattr(inspect, 'signature'): | 
				
			||||||
 | 
					    positionals = ( | 
				
			||||||
 | 
					        inspect.Parameter.POSITIONAL_ONLY, | 
				
			||||||
 | 
					        inspect.Parameter.POSITIONAL_OR_KEYWORD, | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_arity(f): | 
				
			||||||
 | 
					        sig = inspect.signature(f) | 
				
			||||||
 | 
					        arity = 0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for param in sig.parameters.values(): | 
				
			||||||
 | 
					            if param.kind in positionals: | 
				
			||||||
 | 
					                arity += 1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return arity | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def get_arity(f): | 
				
			||||||
 | 
					        return len(inspect.getargspec(f)[0]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    import html | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def html_escape(s): | 
				
			||||||
 | 
					        return html.escape(s) | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    import cgi | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def html_escape(s): | 
				
			||||||
 | 
					        return cgi.escape(s, quote=True) | 
				
			||||||
@ -0,0 +1,4 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
@ -0,0 +1,223 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					from __future__ import print_function | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import traceback | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn._compat import execfile_ | 
				
			||||||
 | 
					from gunicorn import util | 
				
			||||||
 | 
					from gunicorn.arbiter import Arbiter | 
				
			||||||
 | 
					from gunicorn.config import Config, get_default_config_file | 
				
			||||||
 | 
					from gunicorn import debug | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseApplication(object): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    An application interface for configuring and loading | 
				
			||||||
 | 
					    the various necessities for any given web framework. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def __init__(self, usage=None, prog=None): | 
				
			||||||
 | 
					        self.usage = usage | 
				
			||||||
 | 
					        self.cfg = None | 
				
			||||||
 | 
					        self.callable = None | 
				
			||||||
 | 
					        self.prog = prog | 
				
			||||||
 | 
					        self.logger = None | 
				
			||||||
 | 
					        self.do_load_config() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def do_load_config(self): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Loads the configuration | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.load_default_config() | 
				
			||||||
 | 
					            self.load_config() | 
				
			||||||
 | 
					        except Exception as e: | 
				
			||||||
 | 
					            print("\nError: %s" % str(e), file=sys.stderr) | 
				
			||||||
 | 
					            sys.stderr.flush() | 
				
			||||||
 | 
					            sys.exit(1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_default_config(self): | 
				
			||||||
 | 
					        # init configuration | 
				
			||||||
 | 
					        self.cfg = Config(self.usage, prog=self.prog) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def init(self, parser, opts, args): | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load(self): | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_config(self): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        This method is used to load the configuration from one or several input(s). | 
				
			||||||
 | 
					        Custom Command line, configuration file. | 
				
			||||||
 | 
					        You have to override this method in your class. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reload(self): | 
				
			||||||
 | 
					        self.do_load_config() | 
				
			||||||
 | 
					        if self.cfg.spew: | 
				
			||||||
 | 
					            debug.spew() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wsgi(self): | 
				
			||||||
 | 
					        if self.callable is None: | 
				
			||||||
 | 
					            self.callable = self.load() | 
				
			||||||
 | 
					        return self.callable | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            Arbiter(self).run() | 
				
			||||||
 | 
					        except RuntimeError as e: | 
				
			||||||
 | 
					            print("\nError: %s\n" % e, file=sys.stderr) | 
				
			||||||
 | 
					            sys.stderr.flush() | 
				
			||||||
 | 
					            sys.exit(1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Application(BaseApplication): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 'init' and 'load' methods are implemented by WSGIApplication. | 
				
			||||||
 | 
					    # pylint: disable=abstract-method | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def chdir(self): | 
				
			||||||
 | 
					        # chdir to the configured path before loading, | 
				
			||||||
 | 
					        # default is the current dir | 
				
			||||||
 | 
					        os.chdir(self.cfg.chdir) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # add the path to sys.path | 
				
			||||||
 | 
					        if self.cfg.chdir not in sys.path: | 
				
			||||||
 | 
					            sys.path.insert(0, self.cfg.chdir) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_config_from_filename(self, filename): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not os.path.exists(filename): | 
				
			||||||
 | 
					            raise RuntimeError("%r doesn't exist" % filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cfg = { | 
				
			||||||
 | 
					            "__builtins__": __builtins__, | 
				
			||||||
 | 
					            "__name__": "__config__", | 
				
			||||||
 | 
					            "__file__": filename, | 
				
			||||||
 | 
					            "__doc__": None, | 
				
			||||||
 | 
					            "__package__": None | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            execfile_(filename, cfg, cfg) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            print("Failed to read config file: %s" % filename, file=sys.stderr) | 
				
			||||||
 | 
					            traceback.print_exc() | 
				
			||||||
 | 
					            sys.stderr.flush() | 
				
			||||||
 | 
					            sys.exit(1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return cfg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_config_from_module_name(self, module_name): | 
				
			||||||
 | 
					        return vars(util.import_module(module_name)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_config_from_module_name_or_filename(self, location): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Loads the configuration file: the file is a python file, otherwise raise an RuntimeError | 
				
			||||||
 | 
					        Exception or stop the process if the configuration file contains a syntax error. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if location.startswith("python:"): | 
				
			||||||
 | 
					            module_name = location[len("python:"):] | 
				
			||||||
 | 
					            cfg = self.get_config_from_module_name(module_name) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            if location.startswith("file:"): | 
				
			||||||
 | 
					                filename = location[len("file:"):] | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                filename = location | 
				
			||||||
 | 
					            cfg = self.get_config_from_filename(filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for k, v in cfg.items(): | 
				
			||||||
 | 
					            # Ignore unknown names | 
				
			||||||
 | 
					            if k not in self.cfg.settings: | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                self.cfg.set(k.lower(), v) | 
				
			||||||
 | 
					            except: | 
				
			||||||
 | 
					                print("Invalid value for %s: %s\n" % (k, v), file=sys.stderr) | 
				
			||||||
 | 
					                sys.stderr.flush() | 
				
			||||||
 | 
					                raise | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return cfg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_config_from_file(self, filename): | 
				
			||||||
 | 
					        return self.load_config_from_module_name_or_filename(location=filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_config(self): | 
				
			||||||
 | 
					        # parse console args | 
				
			||||||
 | 
					        parser = self.cfg.parser() | 
				
			||||||
 | 
					        args = parser.parse_args() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # optional settings from apps | 
				
			||||||
 | 
					        cfg = self.init(parser, args, args.args) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set up import paths and follow symlinks | 
				
			||||||
 | 
					        self.chdir() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Load up the any app specific configuration | 
				
			||||||
 | 
					        if cfg: | 
				
			||||||
 | 
					            for k, v in cfg.items(): | 
				
			||||||
 | 
					                self.cfg.set(k.lower(), v) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        env_args = parser.parse_args(self.cfg.get_cmd_args_from_env()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if args.config: | 
				
			||||||
 | 
					            self.load_config_from_file(args.config) | 
				
			||||||
 | 
					        elif env_args.config: | 
				
			||||||
 | 
					            self.load_config_from_file(env_args.config) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            default_config = get_default_config_file() | 
				
			||||||
 | 
					            if default_config is not None: | 
				
			||||||
 | 
					                self.load_config_from_file(default_config) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Load up environment configuration | 
				
			||||||
 | 
					        for k, v in vars(env_args).items(): | 
				
			||||||
 | 
					            if v is None: | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					            if k == "args": | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					            self.cfg.set(k.lower(), v) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Lastly, update the configuration with any command line settings. | 
				
			||||||
 | 
					        for k, v in vars(args).items(): | 
				
			||||||
 | 
					            if v is None: | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					            if k == "args": | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					            self.cfg.set(k.lower(), v) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # current directory might be changed by the config now | 
				
			||||||
 | 
					        # set up import paths and follow symlinks | 
				
			||||||
 | 
					        self.chdir() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self): | 
				
			||||||
 | 
					        if self.cfg.check_config: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                self.load() | 
				
			||||||
 | 
					            except: | 
				
			||||||
 | 
					                msg = "\nError while loading the application:\n" | 
				
			||||||
 | 
					                print(msg, file=sys.stderr) | 
				
			||||||
 | 
					                traceback.print_exc() | 
				
			||||||
 | 
					                sys.stderr.flush() | 
				
			||||||
 | 
					                sys.exit(1) | 
				
			||||||
 | 
					            sys.exit(0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.cfg.spew: | 
				
			||||||
 | 
					            debug.spew() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.cfg.daemon: | 
				
			||||||
 | 
					            util.daemonize(self.cfg.enable_stdio_inheritance) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set python paths | 
				
			||||||
 | 
					        if self.cfg.pythonpath: | 
				
			||||||
 | 
					            paths = self.cfg.pythonpath.split(",") | 
				
			||||||
 | 
					            for path in paths: | 
				
			||||||
 | 
					                pythonpath = os.path.abspath(path) | 
				
			||||||
 | 
					                if pythonpath not in sys.path: | 
				
			||||||
 | 
					                    sys.path.insert(0, pythonpath) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(Application, self).run() | 
				
			||||||
@ -0,0 +1,209 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					from __future__ import print_function | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pylint: skip-file | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import pkg_resources | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    import configparser as ConfigParser | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    import ConfigParser | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from paste.deploy import loadapp, loadwsgi | 
				
			||||||
 | 
					SERVER = loadwsgi.SERVER | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn.app.base import Application | 
				
			||||||
 | 
					from gunicorn.config import Config, get_default_config_file | 
				
			||||||
 | 
					from gunicorn import util | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _has_logging_config(paste_file): | 
				
			||||||
 | 
					    cfg_parser = ConfigParser.ConfigParser() | 
				
			||||||
 | 
					    cfg_parser.read([paste_file]) | 
				
			||||||
 | 
					    return cfg_parser.has_section('loggers') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def paste_config(gconfig, config_url, relative_to, global_conf=None): | 
				
			||||||
 | 
					    # add entry to pkg_resources | 
				
			||||||
 | 
					    sys.path.insert(0, relative_to) | 
				
			||||||
 | 
					    pkg_resources.working_set.add_entry(relative_to) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    config_url = config_url.split('#')[0] | 
				
			||||||
 | 
					    cx = loadwsgi.loadcontext(SERVER, config_url, relative_to=relative_to, | 
				
			||||||
 | 
					                              global_conf=global_conf) | 
				
			||||||
 | 
					    gc, lc = cx.global_conf.copy(), cx.local_conf.copy() | 
				
			||||||
 | 
					    cfg = {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    host, port = lc.pop('host', ''), lc.pop('port', '') | 
				
			||||||
 | 
					    if host and port: | 
				
			||||||
 | 
					        cfg['bind'] = '%s:%s' % (host, port) | 
				
			||||||
 | 
					    elif host: | 
				
			||||||
 | 
					        cfg['bind'] = host.split(',') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cfg['default_proc_name'] = gc.get('__file__') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # init logging configuration | 
				
			||||||
 | 
					    config_file = config_url.split(':')[1] | 
				
			||||||
 | 
					    if _has_logging_config(config_file): | 
				
			||||||
 | 
					        cfg.setdefault('logconfig', config_file) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for k, v in gc.items(): | 
				
			||||||
 | 
					        if k not in gconfig.settings: | 
				
			||||||
 | 
					            continue | 
				
			||||||
 | 
					        cfg[k] = v | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for k, v in lc.items(): | 
				
			||||||
 | 
					        if k not in gconfig.settings: | 
				
			||||||
 | 
					            continue | 
				
			||||||
 | 
					        cfg[k] = v | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return cfg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def load_pasteapp(config_url, relative_to, global_conf=None): | 
				
			||||||
 | 
					    return loadapp(config_url, relative_to=relative_to, | 
				
			||||||
 | 
					            global_conf=global_conf) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PasterBaseApplication(Application): | 
				
			||||||
 | 
					    gcfg = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def app_config(self): | 
				
			||||||
 | 
					        return paste_config(self.cfg, self.cfgurl, self.relpath, | 
				
			||||||
 | 
					                global_conf=self.gcfg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_config(self): | 
				
			||||||
 | 
					        super(PasterBaseApplication, self).load_config() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # reload logging conf | 
				
			||||||
 | 
					        if hasattr(self, "cfgfname"): | 
				
			||||||
 | 
					            parser = ConfigParser.ConfigParser() | 
				
			||||||
 | 
					            parser.read([self.cfgfname]) | 
				
			||||||
 | 
					            if parser.has_section('loggers'): | 
				
			||||||
 | 
					                from logging.config import fileConfig | 
				
			||||||
 | 
					                config_file = os.path.abspath(self.cfgfname) | 
				
			||||||
 | 
					                fileConfig(config_file, dict(__file__=config_file, | 
				
			||||||
 | 
					                                             here=os.path.dirname(config_file))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PasterApplication(PasterBaseApplication): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def init(self, parser, opts, args): | 
				
			||||||
 | 
					        if len(args) != 1: | 
				
			||||||
 | 
					            parser.error("No application name specified.") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cwd = util.getcwd() | 
				
			||||||
 | 
					        cfgfname = os.path.normpath(os.path.join(cwd, args[0])) | 
				
			||||||
 | 
					        cfgfname = os.path.abspath(cfgfname) | 
				
			||||||
 | 
					        if not os.path.exists(cfgfname): | 
				
			||||||
 | 
					            parser.error("Config file not found: %s" % cfgfname) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.cfgurl = 'config:%s' % cfgfname | 
				
			||||||
 | 
					        self.relpath = os.path.dirname(cfgfname) | 
				
			||||||
 | 
					        self.cfgfname = cfgfname | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sys.path.insert(0, self.relpath) | 
				
			||||||
 | 
					        pkg_resources.working_set.add_entry(self.relpath) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.app_config() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load(self): | 
				
			||||||
 | 
					        # chdir to the configured path before loading, | 
				
			||||||
 | 
					        # default is the current dir | 
				
			||||||
 | 
					        os.chdir(self.cfg.chdir) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return load_pasteapp(self.cfgurl, self.relpath, global_conf=self.gcfg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PasterServerApplication(PasterBaseApplication): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, app, gcfg=None, host="127.0.0.1", port=None, **kwargs): | 
				
			||||||
 | 
					        # pylint: disable=super-init-not-called | 
				
			||||||
 | 
					        self.cfg = Config() | 
				
			||||||
 | 
					        self.gcfg = gcfg  # need to hold this for app_config | 
				
			||||||
 | 
					        self.app = app | 
				
			||||||
 | 
					        self.callable = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gcfg = gcfg or {} | 
				
			||||||
 | 
					        cfgfname = gcfg.get("__file__") | 
				
			||||||
 | 
					        if cfgfname is not None: | 
				
			||||||
 | 
					            self.cfgurl = 'config:%s' % cfgfname | 
				
			||||||
 | 
					            self.relpath = os.path.dirname(cfgfname) | 
				
			||||||
 | 
					            self.cfgfname = cfgfname | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cfg = kwargs.copy() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if port and not host.startswith("unix:"): | 
				
			||||||
 | 
					            bind = "%s:%s" % (host, port) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            bind = host | 
				
			||||||
 | 
					        cfg["bind"] = bind.split(',') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if gcfg: | 
				
			||||||
 | 
					            for k, v in gcfg.items(): | 
				
			||||||
 | 
					                cfg[k] = v | 
				
			||||||
 | 
					            cfg["default_proc_name"] = cfg['__file__'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            for k, v in cfg.items(): | 
				
			||||||
 | 
					                if k.lower() in self.cfg.settings and v is not None: | 
				
			||||||
 | 
					                    self.cfg.set(k.lower(), v) | 
				
			||||||
 | 
					        except Exception as e: | 
				
			||||||
 | 
					            print("\nConfig error: %s" % str(e), file=sys.stderr) | 
				
			||||||
 | 
					            sys.stderr.flush() | 
				
			||||||
 | 
					            sys.exit(1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if cfg.get("config"): | 
				
			||||||
 | 
					            self.load_config_from_file(cfg["config"]) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            default_config = get_default_config_file() | 
				
			||||||
 | 
					            if default_config is not None: | 
				
			||||||
 | 
					                self.load_config_from_file(default_config) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load(self): | 
				
			||||||
 | 
					        return self.app | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def run(): | 
				
			||||||
 | 
					    """\ | 
				
			||||||
 | 
					    The ``gunicorn_paster`` command for launching Paster compatible | 
				
			||||||
 | 
					    applications like Pylons or Turbogears2 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    util.warn("""This command is deprecated. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    You should now use the `--paste` option. Ex.: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gunicorn --paste development.ini | 
				
			||||||
 | 
					    """) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from gunicorn.app.pasterapp import PasterApplication | 
				
			||||||
 | 
					    PasterApplication("%(prog)s [OPTIONS] pasteconfig.ini").run() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def paste_server(app, gcfg=None, host="127.0.0.1", port=None, **kwargs): | 
				
			||||||
 | 
					    """\ | 
				
			||||||
 | 
					    A paster server. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Then entry point in your paster ini file should looks like this: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [server:main] | 
				
			||||||
 | 
					    use = egg:gunicorn#main | 
				
			||||||
 | 
					    host = 127.0.0.1 | 
				
			||||||
 | 
					    port = 5000 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    util.warn("""This command is deprecated. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    You should now use the `--paste` option. Ex.: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gunicorn --paste development.ini | 
				
			||||||
 | 
					    """) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    from gunicorn.app.pasterapp import PasterServerApplication | 
				
			||||||
 | 
					    PasterServerApplication(app, gcfg=gcfg, host=host, port=port, **kwargs).run() | 
				
			||||||
@ -0,0 +1,65 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn.errors import ConfigError | 
				
			||||||
 | 
					from gunicorn.app.base import Application | 
				
			||||||
 | 
					from gunicorn import util | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WSGIApplication(Application): | 
				
			||||||
 | 
					    def init(self, parser, opts, args): | 
				
			||||||
 | 
					        if opts.paste: | 
				
			||||||
 | 
					            app_name = 'main' | 
				
			||||||
 | 
					            path = opts.paste | 
				
			||||||
 | 
					            if '#' in path: | 
				
			||||||
 | 
					                path, app_name = path.split('#') | 
				
			||||||
 | 
					            path = os.path.abspath(os.path.normpath( | 
				
			||||||
 | 
					                os.path.join(util.getcwd(), path))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if not os.path.exists(path): | 
				
			||||||
 | 
					                raise ConfigError("%r not found" % path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # paste application, load the config | 
				
			||||||
 | 
					            self.cfgurl = 'config:%s#%s' % (path, app_name) | 
				
			||||||
 | 
					            self.relpath = os.path.dirname(path) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            from .pasterapp import paste_config | 
				
			||||||
 | 
					            return paste_config(self.cfg, self.cfgurl, self.relpath) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if len(args) < 1: | 
				
			||||||
 | 
					            parser.error("No application module specified.") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.cfg.set("default_proc_name", args[0]) | 
				
			||||||
 | 
					        self.app_uri = args[0] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_wsgiapp(self): | 
				
			||||||
 | 
					        # load the app | 
				
			||||||
 | 
					        return util.import_app(self.app_uri) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_pasteapp(self): | 
				
			||||||
 | 
					        # load the paste app | 
				
			||||||
 | 
					        from .pasterapp import load_pasteapp | 
				
			||||||
 | 
					        return load_pasteapp(self.cfgurl, self.relpath, global_conf=self.cfg.paste_global_conf) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load(self): | 
				
			||||||
 | 
					        if self.cfg.paste is not None: | 
				
			||||||
 | 
					            return self.load_pasteapp() | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            return self.load_wsgiapp() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def run(): | 
				
			||||||
 | 
					    """\ | 
				
			||||||
 | 
					    The ``gunicorn`` command line runner for launching Gunicorn with | 
				
			||||||
 | 
					    generic WSGI applications. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    from gunicorn.app.wsgiapp import WSGIApplication | 
				
			||||||
 | 
					    WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__': | 
				
			||||||
 | 
					    run() | 
				
			||||||
@ -0,0 +1,646 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					from __future__ import print_function | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import errno | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import random | 
				
			||||||
 | 
					import select | 
				
			||||||
 | 
					import signal | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					import traceback | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn.errors import HaltServer, AppImportError | 
				
			||||||
 | 
					from gunicorn.pidfile import Pidfile | 
				
			||||||
 | 
					from gunicorn import sock, systemd, util | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn import __version__, SERVER_SOFTWARE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Arbiter(object): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Arbiter maintain the workers processes alive. It launches or | 
				
			||||||
 | 
					    kills them if needed. It also manages application reloading | 
				
			||||||
 | 
					    via SIGHUP/USR2. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # A flag indicating if a worker failed to | 
				
			||||||
 | 
					    # to boot. If a worker process exist with | 
				
			||||||
 | 
					    # this error code, the arbiter will terminate. | 
				
			||||||
 | 
					    WORKER_BOOT_ERROR = 3 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # A flag indicating if an application failed to be loaded | 
				
			||||||
 | 
					    APP_LOAD_ERROR = 4 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    START_CTX = {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LISTENERS = [] | 
				
			||||||
 | 
					    WORKERS = {} | 
				
			||||||
 | 
					    PIPE = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # I love dynamic languages | 
				
			||||||
 | 
					    SIG_QUEUE = [] | 
				
			||||||
 | 
					    SIGNALS = [getattr(signal, "SIG%s" % x) | 
				
			||||||
 | 
					               for x in "HUP QUIT INT TERM TTIN TTOU USR1 USR2 WINCH".split()] | 
				
			||||||
 | 
					    SIG_NAMES = dict( | 
				
			||||||
 | 
					        (getattr(signal, name), name[3:].lower()) for name in dir(signal) | 
				
			||||||
 | 
					        if name[:3] == "SIG" and name[3] != "_" | 
				
			||||||
 | 
					    ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, app): | 
				
			||||||
 | 
					        os.environ["SERVER_SOFTWARE"] = SERVER_SOFTWARE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._num_workers = None | 
				
			||||||
 | 
					        self._last_logged_active_worker_count = None | 
				
			||||||
 | 
					        self.log = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.setup(app) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.pidfile = None | 
				
			||||||
 | 
					        self.systemd = False | 
				
			||||||
 | 
					        self.worker_age = 0 | 
				
			||||||
 | 
					        self.reexec_pid = 0 | 
				
			||||||
 | 
					        self.master_pid = 0 | 
				
			||||||
 | 
					        self.master_name = "Master" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cwd = util.getcwd() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        args = sys.argv[:] | 
				
			||||||
 | 
					        args.insert(0, sys.executable) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # init start context | 
				
			||||||
 | 
					        self.START_CTX = { | 
				
			||||||
 | 
					            "args": args, | 
				
			||||||
 | 
					            "cwd": cwd, | 
				
			||||||
 | 
					            0: sys.executable | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_num_workers(self): | 
				
			||||||
 | 
					        return self._num_workers | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _set_num_workers(self, value): | 
				
			||||||
 | 
					        old_value = self._num_workers | 
				
			||||||
 | 
					        self._num_workers = value | 
				
			||||||
 | 
					        self.cfg.nworkers_changed(self, value, old_value) | 
				
			||||||
 | 
					    num_workers = property(_get_num_workers, _set_num_workers) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setup(self, app): | 
				
			||||||
 | 
					        self.app = app | 
				
			||||||
 | 
					        self.cfg = app.cfg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.log is None: | 
				
			||||||
 | 
					            self.log = self.cfg.logger_class(app.cfg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # reopen files | 
				
			||||||
 | 
					        if 'GUNICORN_FD' in os.environ: | 
				
			||||||
 | 
					            self.log.reopen_files() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.worker_class = self.cfg.worker_class | 
				
			||||||
 | 
					        self.address = self.cfg.address | 
				
			||||||
 | 
					        self.num_workers = self.cfg.workers | 
				
			||||||
 | 
					        self.timeout = self.cfg.timeout | 
				
			||||||
 | 
					        self.proc_name = self.cfg.proc_name | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.log.debug('Current configuration:\n{0}'.format( | 
				
			||||||
 | 
					            '\n'.join( | 
				
			||||||
 | 
					                '  {0}: {1}'.format(config, value.value) | 
				
			||||||
 | 
					                for config, value | 
				
			||||||
 | 
					                in sorted(self.cfg.settings.items(), | 
				
			||||||
 | 
					                          key=lambda setting: setting[1])))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set enviroment' variables | 
				
			||||||
 | 
					        if self.cfg.env: | 
				
			||||||
 | 
					            for k, v in self.cfg.env.items(): | 
				
			||||||
 | 
					                os.environ[k] = v | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.cfg.preload_app: | 
				
			||||||
 | 
					            self.app.wsgi() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def start(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Initialize the arbiter. Start listening and set pidfile if needed. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.log.info("Starting gunicorn %s", __version__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'GUNICORN_PID' in os.environ: | 
				
			||||||
 | 
					            self.master_pid = int(os.environ.get('GUNICORN_PID')) | 
				
			||||||
 | 
					            self.proc_name = self.proc_name + ".2" | 
				
			||||||
 | 
					            self.master_name = "Master.2" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.pid = os.getpid() | 
				
			||||||
 | 
					        if self.cfg.pidfile is not None: | 
				
			||||||
 | 
					            pidname = self.cfg.pidfile | 
				
			||||||
 | 
					            if self.master_pid != 0: | 
				
			||||||
 | 
					                pidname += ".2" | 
				
			||||||
 | 
					            self.pidfile = Pidfile(pidname) | 
				
			||||||
 | 
					            self.pidfile.create(self.pid) | 
				
			||||||
 | 
					        self.cfg.on_starting(self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.init_signals() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.LISTENERS: | 
				
			||||||
 | 
					            fds = None | 
				
			||||||
 | 
					            listen_fds = systemd.listen_fds() | 
				
			||||||
 | 
					            if listen_fds: | 
				
			||||||
 | 
					                self.systemd = True | 
				
			||||||
 | 
					                fds = range(systemd.SD_LISTEN_FDS_START, | 
				
			||||||
 | 
					                            systemd.SD_LISTEN_FDS_START + listen_fds) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            elif self.master_pid: | 
				
			||||||
 | 
					                fds = [] | 
				
			||||||
 | 
					                for fd in os.environ.pop('GUNICORN_FD').split(','): | 
				
			||||||
 | 
					                    fds.append(int(fd)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.LISTENERS = sock.create_sockets(self.cfg, self.log, fds) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        listeners_str = ",".join([str(l) for l in self.LISTENERS]) | 
				
			||||||
 | 
					        self.log.debug("Arbiter booted") | 
				
			||||||
 | 
					        self.log.info("Listening at: %s (%s)", listeners_str, self.pid) | 
				
			||||||
 | 
					        self.log.info("Using worker: %s", self.cfg.worker_class_str) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check worker class requirements | 
				
			||||||
 | 
					        if hasattr(self.worker_class, "check_config"): | 
				
			||||||
 | 
					            self.worker_class.check_config(self.cfg, self.log) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.cfg.when_ready(self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def init_signals(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Initialize master signal handling. Most of the signals | 
				
			||||||
 | 
					        are queued. Child signals only wake up the master. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        # close old PIPE | 
				
			||||||
 | 
					        for p in self.PIPE: | 
				
			||||||
 | 
					            os.close(p) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # initialize the pipe | 
				
			||||||
 | 
					        self.PIPE = pair = os.pipe() | 
				
			||||||
 | 
					        for p in pair: | 
				
			||||||
 | 
					            util.set_non_blocking(p) | 
				
			||||||
 | 
					            util.close_on_exec(p) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.log.close_on_exec() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # initialize all signals | 
				
			||||||
 | 
					        for s in self.SIGNALS: | 
				
			||||||
 | 
					            signal.signal(s, self.signal) | 
				
			||||||
 | 
					        signal.signal(signal.SIGCHLD, self.handle_chld) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def signal(self, sig, frame): | 
				
			||||||
 | 
					        if len(self.SIG_QUEUE) < 5: | 
				
			||||||
 | 
					            self.SIG_QUEUE.append(sig) | 
				
			||||||
 | 
					            self.wakeup() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self): | 
				
			||||||
 | 
					        "Main master loop." | 
				
			||||||
 | 
					        self.start() | 
				
			||||||
 | 
					        util._setproctitle("master [%s]" % self.proc_name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.manage_workers() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            while True: | 
				
			||||||
 | 
					                self.maybe_promote_master() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                sig = self.SIG_QUEUE.pop(0) if self.SIG_QUEUE else None | 
				
			||||||
 | 
					                if sig is None: | 
				
			||||||
 | 
					                    self.sleep() | 
				
			||||||
 | 
					                    self.murder_workers() | 
				
			||||||
 | 
					                    self.manage_workers() | 
				
			||||||
 | 
					                    continue | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if sig not in self.SIG_NAMES: | 
				
			||||||
 | 
					                    self.log.info("Ignoring unknown signal: %s", sig) | 
				
			||||||
 | 
					                    continue | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                signame = self.SIG_NAMES.get(sig) | 
				
			||||||
 | 
					                handler = getattr(self, "handle_%s" % signame, None) | 
				
			||||||
 | 
					                if not handler: | 
				
			||||||
 | 
					                    self.log.error("Unhandled signal: %s", signame) | 
				
			||||||
 | 
					                    continue | 
				
			||||||
 | 
					                self.log.info("Handling signal: %s", signame) | 
				
			||||||
 | 
					                handler() | 
				
			||||||
 | 
					                self.wakeup() | 
				
			||||||
 | 
					        except StopIteration: | 
				
			||||||
 | 
					            self.halt() | 
				
			||||||
 | 
					        except KeyboardInterrupt: | 
				
			||||||
 | 
					            self.halt() | 
				
			||||||
 | 
					        except HaltServer as inst: | 
				
			||||||
 | 
					            self.halt(reason=inst.reason, exit_status=inst.exit_status) | 
				
			||||||
 | 
					        except SystemExit: | 
				
			||||||
 | 
					            raise | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            self.log.info("Unhandled exception in main loop", | 
				
			||||||
 | 
					                          exc_info=True) | 
				
			||||||
 | 
					            self.stop(False) | 
				
			||||||
 | 
					            if self.pidfile is not None: | 
				
			||||||
 | 
					                self.pidfile.unlink() | 
				
			||||||
 | 
					            sys.exit(-1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_chld(self, sig, frame): | 
				
			||||||
 | 
					        "SIGCHLD handling" | 
				
			||||||
 | 
					        self.reap_workers() | 
				
			||||||
 | 
					        self.wakeup() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_hup(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        HUP handling. | 
				
			||||||
 | 
					        - Reload configuration | 
				
			||||||
 | 
					        - Start the new worker processes with a new configuration | 
				
			||||||
 | 
					        - Gracefully shutdown the old worker processes | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.log.info("Hang up: %s", self.master_name) | 
				
			||||||
 | 
					        self.reload() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_term(self): | 
				
			||||||
 | 
					        "SIGTERM handling" | 
				
			||||||
 | 
					        raise StopIteration | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_int(self): | 
				
			||||||
 | 
					        "SIGINT handling" | 
				
			||||||
 | 
					        self.stop(False) | 
				
			||||||
 | 
					        raise StopIteration | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_quit(self): | 
				
			||||||
 | 
					        "SIGQUIT handling" | 
				
			||||||
 | 
					        self.stop(False) | 
				
			||||||
 | 
					        raise StopIteration | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_ttin(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        SIGTTIN handling. | 
				
			||||||
 | 
					        Increases the number of workers by one. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.num_workers += 1 | 
				
			||||||
 | 
					        self.manage_workers() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_ttou(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        SIGTTOU handling. | 
				
			||||||
 | 
					        Decreases the number of workers by one. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if self.num_workers <= 1: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        self.num_workers -= 1 | 
				
			||||||
 | 
					        self.manage_workers() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_usr1(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        SIGUSR1 handling. | 
				
			||||||
 | 
					        Kill all workers by sending them a SIGUSR1 | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.log.reopen_files() | 
				
			||||||
 | 
					        self.kill_workers(signal.SIGUSR1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_usr2(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        SIGUSR2 handling. | 
				
			||||||
 | 
					        Creates a new master/worker set as a slave of the current | 
				
			||||||
 | 
					        master without affecting old workers. Use this to do live | 
				
			||||||
 | 
					        deployment with the ability to backout a change. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.reexec() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_winch(self): | 
				
			||||||
 | 
					        """SIGWINCH handling""" | 
				
			||||||
 | 
					        if self.cfg.daemon: | 
				
			||||||
 | 
					            self.log.info("graceful stop of workers") | 
				
			||||||
 | 
					            self.num_workers = 0 | 
				
			||||||
 | 
					            self.kill_workers(signal.SIGTERM) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.log.debug("SIGWINCH ignored. Not daemonized") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def maybe_promote_master(self): | 
				
			||||||
 | 
					        if self.master_pid == 0: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.master_pid != os.getppid(): | 
				
			||||||
 | 
					            self.log.info("Master has been promoted.") | 
				
			||||||
 | 
					            # reset master infos | 
				
			||||||
 | 
					            self.master_name = "Master" | 
				
			||||||
 | 
					            self.master_pid = 0 | 
				
			||||||
 | 
					            self.proc_name = self.cfg.proc_name | 
				
			||||||
 | 
					            del os.environ['GUNICORN_PID'] | 
				
			||||||
 | 
					            # rename the pidfile | 
				
			||||||
 | 
					            if self.pidfile is not None: | 
				
			||||||
 | 
					                self.pidfile.rename(self.cfg.pidfile) | 
				
			||||||
 | 
					            # reset proctitle | 
				
			||||||
 | 
					            util._setproctitle("master [%s]" % self.proc_name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def wakeup(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Wake up the arbiter by writing to the PIPE | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            os.write(self.PIPE[1], b'.') | 
				
			||||||
 | 
					        except IOError as e: | 
				
			||||||
 | 
					            if e.errno not in [errno.EAGAIN, errno.EINTR]: | 
				
			||||||
 | 
					                raise | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def halt(self, reason=None, exit_status=0): | 
				
			||||||
 | 
					        """ halt arbiter """ | 
				
			||||||
 | 
					        self.stop() | 
				
			||||||
 | 
					        self.log.info("Shutting down: %s", self.master_name) | 
				
			||||||
 | 
					        if reason is not None: | 
				
			||||||
 | 
					            self.log.info("Reason: %s", reason) | 
				
			||||||
 | 
					        if self.pidfile is not None: | 
				
			||||||
 | 
					            self.pidfile.unlink() | 
				
			||||||
 | 
					        self.cfg.on_exit(self) | 
				
			||||||
 | 
					        sys.exit(exit_status) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sleep(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Sleep until PIPE is readable or we timeout. | 
				
			||||||
 | 
					        A readable PIPE means a signal occurred. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            ready = select.select([self.PIPE[0]], [], [], 1.0) | 
				
			||||||
 | 
					            if not ready[0]: | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					            while os.read(self.PIPE[0], 1): | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					        except (select.error, OSError) as e: | 
				
			||||||
 | 
					            # TODO: select.error is a subclass of OSError since Python 3.3. | 
				
			||||||
 | 
					            error_number = getattr(e, 'errno', e.args[0]) | 
				
			||||||
 | 
					            if error_number not in [errno.EAGAIN, errno.EINTR]: | 
				
			||||||
 | 
					                raise | 
				
			||||||
 | 
					        except KeyboardInterrupt: | 
				
			||||||
 | 
					            sys.exit() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def stop(self, graceful=True): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Stop workers | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :attr graceful: boolean, If True (the default) workers will be | 
				
			||||||
 | 
					        killed gracefully  (ie. trying to wait for the current connection) | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unlink = self.reexec_pid == self.master_pid == 0 and not self.systemd | 
				
			||||||
 | 
					        sock.close_sockets(self.LISTENERS, unlink) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.LISTENERS = [] | 
				
			||||||
 | 
					        sig = signal.SIGTERM | 
				
			||||||
 | 
					        if not graceful: | 
				
			||||||
 | 
					            sig = signal.SIGQUIT | 
				
			||||||
 | 
					        limit = time.time() + self.cfg.graceful_timeout | 
				
			||||||
 | 
					        # instruct the workers to exit | 
				
			||||||
 | 
					        self.kill_workers(sig) | 
				
			||||||
 | 
					        # wait until the graceful timeout | 
				
			||||||
 | 
					        while self.WORKERS and time.time() < limit: | 
				
			||||||
 | 
					            time.sleep(0.1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.kill_workers(signal.SIGKILL) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reexec(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Relaunch the master and workers. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if self.reexec_pid != 0: | 
				
			||||||
 | 
					            self.log.warning("USR2 signal ignored. Child exists.") | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.master_pid != 0: | 
				
			||||||
 | 
					            self.log.warning("USR2 signal ignored. Parent exists.") | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        master_pid = os.getpid() | 
				
			||||||
 | 
					        self.reexec_pid = os.fork() | 
				
			||||||
 | 
					        if self.reexec_pid != 0: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.cfg.pre_exec(self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        environ = self.cfg.env_orig.copy() | 
				
			||||||
 | 
					        environ['GUNICORN_PID'] = str(master_pid) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.systemd: | 
				
			||||||
 | 
					            environ['LISTEN_PID'] = str(os.getpid()) | 
				
			||||||
 | 
					            environ['LISTEN_FDS'] = str(len(self.LISTENERS)) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            environ['GUNICORN_FD'] = ','.join( | 
				
			||||||
 | 
					                str(l.fileno()) for l in self.LISTENERS) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        os.chdir(self.START_CTX['cwd']) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # exec the process using the original environment | 
				
			||||||
 | 
					        os.execvpe(self.START_CTX[0], self.START_CTX['args'], environ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reload(self): | 
				
			||||||
 | 
					        old_address = self.cfg.address | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # reset old environment | 
				
			||||||
 | 
					        for k in self.cfg.env: | 
				
			||||||
 | 
					            if k in self.cfg.env_orig: | 
				
			||||||
 | 
					                # reset the key to the value it had before | 
				
			||||||
 | 
					                # we launched gunicorn | 
				
			||||||
 | 
					                os.environ[k] = self.cfg.env_orig[k] | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                # delete the value set by gunicorn | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    del os.environ[k] | 
				
			||||||
 | 
					                except KeyError: | 
				
			||||||
 | 
					                    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # reload conf | 
				
			||||||
 | 
					        self.app.reload() | 
				
			||||||
 | 
					        self.setup(self.app) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # reopen log files | 
				
			||||||
 | 
					        self.log.reopen_files() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # do we need to change listener ? | 
				
			||||||
 | 
					        if old_address != self.cfg.address: | 
				
			||||||
 | 
					            # close all listeners | 
				
			||||||
 | 
					            for l in self.LISTENERS: | 
				
			||||||
 | 
					                l.close() | 
				
			||||||
 | 
					            # init new listeners | 
				
			||||||
 | 
					            self.LISTENERS = sock.create_sockets(self.cfg, self.log) | 
				
			||||||
 | 
					            listeners_str = ",".join([str(l) for l in self.LISTENERS]) | 
				
			||||||
 | 
					            self.log.info("Listening at: %s", listeners_str) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # do some actions on reload | 
				
			||||||
 | 
					        self.cfg.on_reload(self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # unlink pidfile | 
				
			||||||
 | 
					        if self.pidfile is not None: | 
				
			||||||
 | 
					            self.pidfile.unlink() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # create new pidfile | 
				
			||||||
 | 
					        if self.cfg.pidfile is not None: | 
				
			||||||
 | 
					            self.pidfile = Pidfile(self.cfg.pidfile) | 
				
			||||||
 | 
					            self.pidfile.create(self.pid) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set new proc_name | 
				
			||||||
 | 
					        util._setproctitle("master [%s]" % self.proc_name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # spawn new workers | 
				
			||||||
 | 
					        for _ in range(self.cfg.workers): | 
				
			||||||
 | 
					            self.spawn_worker() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # manage workers | 
				
			||||||
 | 
					        self.manage_workers() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def murder_workers(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Kill unused/idle workers | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if not self.timeout: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        workers = list(self.WORKERS.items()) | 
				
			||||||
 | 
					        for (pid, worker) in workers: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                if time.time() - worker.tmp.last_update() <= self.timeout: | 
				
			||||||
 | 
					                    continue | 
				
			||||||
 | 
					            except (OSError, ValueError): | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if not worker.aborted: | 
				
			||||||
 | 
					                self.log.critical("WORKER TIMEOUT (pid:%s)", pid) | 
				
			||||||
 | 
					                worker.aborted = True | 
				
			||||||
 | 
					                self.kill_worker(pid, signal.SIGABRT) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                self.kill_worker(pid, signal.SIGKILL) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reap_workers(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Reap workers to avoid zombie processes | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            while True: | 
				
			||||||
 | 
					                wpid, status = os.waitpid(-1, os.WNOHANG) | 
				
			||||||
 | 
					                if not wpid: | 
				
			||||||
 | 
					                    break | 
				
			||||||
 | 
					                if self.reexec_pid == wpid: | 
				
			||||||
 | 
					                    self.reexec_pid = 0 | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    # A worker was terminated. If the termination reason was | 
				
			||||||
 | 
					                    # that it could not boot, we'll shut it down to avoid | 
				
			||||||
 | 
					                    # infinite start/stop cycles. | 
				
			||||||
 | 
					                    exitcode = status >> 8 | 
				
			||||||
 | 
					                    if exitcode == self.WORKER_BOOT_ERROR: | 
				
			||||||
 | 
					                        reason = "Worker failed to boot." | 
				
			||||||
 | 
					                        raise HaltServer(reason, self.WORKER_BOOT_ERROR) | 
				
			||||||
 | 
					                    if exitcode == self.APP_LOAD_ERROR: | 
				
			||||||
 | 
					                        reason = "App failed to load." | 
				
			||||||
 | 
					                        raise HaltServer(reason, self.APP_LOAD_ERROR) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    worker = self.WORKERS.pop(wpid, None) | 
				
			||||||
 | 
					                    if not worker: | 
				
			||||||
 | 
					                        continue | 
				
			||||||
 | 
					                    worker.tmp.close() | 
				
			||||||
 | 
					                    self.cfg.child_exit(self, worker) | 
				
			||||||
 | 
					        except OSError as e: | 
				
			||||||
 | 
					            if e.errno != errno.ECHILD: | 
				
			||||||
 | 
					                raise | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def manage_workers(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Maintain the number of workers by spawning or killing | 
				
			||||||
 | 
					        as required. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if len(self.WORKERS.keys()) < self.num_workers: | 
				
			||||||
 | 
					            self.spawn_workers() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        workers = self.WORKERS.items() | 
				
			||||||
 | 
					        workers = sorted(workers, key=lambda w: w[1].age) | 
				
			||||||
 | 
					        while len(workers) > self.num_workers: | 
				
			||||||
 | 
					            (pid, _) = workers.pop(0) | 
				
			||||||
 | 
					            self.kill_worker(pid, signal.SIGTERM) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        active_worker_count = len(workers) | 
				
			||||||
 | 
					        if self._last_logged_active_worker_count != active_worker_count: | 
				
			||||||
 | 
					            self._last_logged_active_worker_count = active_worker_count | 
				
			||||||
 | 
					            self.log.debug("{0} workers".format(active_worker_count), | 
				
			||||||
 | 
					                           extra={"metric": "gunicorn.workers", | 
				
			||||||
 | 
					                                  "value": active_worker_count, | 
				
			||||||
 | 
					                                  "mtype": "gauge"}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def spawn_worker(self): | 
				
			||||||
 | 
					        self.worker_age += 1 | 
				
			||||||
 | 
					        worker = self.worker_class(self.worker_age, self.pid, self.LISTENERS, | 
				
			||||||
 | 
					                                   self.app, self.timeout / 2.0, | 
				
			||||||
 | 
					                                   self.cfg, self.log) | 
				
			||||||
 | 
					        self.cfg.pre_fork(self, worker) | 
				
			||||||
 | 
					        pid = os.fork() | 
				
			||||||
 | 
					        if pid != 0: | 
				
			||||||
 | 
					            worker.pid = pid | 
				
			||||||
 | 
					            self.WORKERS[pid] = worker | 
				
			||||||
 | 
					            return pid | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Do not inherit the temporary files of other workers | 
				
			||||||
 | 
					        for sibling in self.WORKERS.values(): | 
				
			||||||
 | 
					            sibling.tmp.close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Process Child | 
				
			||||||
 | 
					        worker.pid = os.getpid() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            util._setproctitle("worker [%s]" % self.proc_name) | 
				
			||||||
 | 
					            self.log.info("Booting worker with pid: %s", worker.pid) | 
				
			||||||
 | 
					            self.cfg.post_fork(self, worker) | 
				
			||||||
 | 
					            worker.init_process() | 
				
			||||||
 | 
					            sys.exit(0) | 
				
			||||||
 | 
					        except SystemExit: | 
				
			||||||
 | 
					            raise | 
				
			||||||
 | 
					        except AppImportError as e: | 
				
			||||||
 | 
					            self.log.debug("Exception while loading the application", | 
				
			||||||
 | 
					                           exc_info=True) | 
				
			||||||
 | 
					            print("%s" % e, file=sys.stderr) | 
				
			||||||
 | 
					            sys.stderr.flush() | 
				
			||||||
 | 
					            sys.exit(self.APP_LOAD_ERROR) | 
				
			||||||
 | 
					        except: | 
				
			||||||
 | 
					            self.log.exception("Exception in worker process") | 
				
			||||||
 | 
					            if not worker.booted: | 
				
			||||||
 | 
					                sys.exit(self.WORKER_BOOT_ERROR) | 
				
			||||||
 | 
					            sys.exit(-1) | 
				
			||||||
 | 
					        finally: | 
				
			||||||
 | 
					            self.log.info("Worker exiting (pid: %s)", worker.pid) | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                worker.tmp.close() | 
				
			||||||
 | 
					                self.cfg.worker_exit(self, worker) | 
				
			||||||
 | 
					            except: | 
				
			||||||
 | 
					                self.log.warning("Exception during worker exit:\n%s", | 
				
			||||||
 | 
					                                  traceback.format_exc()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def spawn_workers(self): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Spawn new workers as needed. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This is where a worker process leaves the main loop | 
				
			||||||
 | 
					        of the master process. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for _ in range(self.num_workers - len(self.WORKERS.keys())): | 
				
			||||||
 | 
					            self.spawn_worker() | 
				
			||||||
 | 
					            time.sleep(0.1 * random.random()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def kill_workers(self, sig): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Kill all workers with the signal `sig` | 
				
			||||||
 | 
					        :attr sig: `signal.SIG*` value | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        worker_pids = list(self.WORKERS.keys()) | 
				
			||||||
 | 
					        for pid in worker_pids: | 
				
			||||||
 | 
					            self.kill_worker(pid, sig) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def kill_worker(self, pid, sig): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Kill a worker | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :attr pid: int, worker pid | 
				
			||||||
 | 
					        :attr sig: `signal.SIG*` value | 
				
			||||||
 | 
					         """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            os.kill(pid, sig) | 
				
			||||||
 | 
					        except OSError as e: | 
				
			||||||
 | 
					            if e.errno == errno.ESRCH: | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    worker = self.WORKERS.pop(pid) | 
				
			||||||
 | 
					                    worker.tmp.close() | 
				
			||||||
 | 
					                    self.cfg.worker_exit(self, worker) | 
				
			||||||
 | 
					                    return | 
				
			||||||
 | 
					                except (KeyError, OSError): | 
				
			||||||
 | 
					                    return | 
				
			||||||
 | 
					            raise | 
				
			||||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						@ -0,0 +1,69 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""The debug module contains utilities and functions for better | 
				
			||||||
 | 
					debugging Gunicorn.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import linecache | 
				
			||||||
 | 
					import re | 
				
			||||||
 | 
					import inspect | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__all__ = ['spew', 'unspew'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_token_spliter = re.compile(r'\W+') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Spew(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, trace_names=None, show_values=True): | 
				
			||||||
 | 
					        self.trace_names = trace_names | 
				
			||||||
 | 
					        self.show_values = show_values | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __call__(self, frame, event, arg): | 
				
			||||||
 | 
					        if event == 'line': | 
				
			||||||
 | 
					            lineno = frame.f_lineno | 
				
			||||||
 | 
					            if '__file__' in frame.f_globals: | 
				
			||||||
 | 
					                filename = frame.f_globals['__file__'] | 
				
			||||||
 | 
					                if (filename.endswith('.pyc') or | 
				
			||||||
 | 
					                    filename.endswith('.pyo')): | 
				
			||||||
 | 
					                    filename = filename[:-1] | 
				
			||||||
 | 
					                name = frame.f_globals['__name__'] | 
				
			||||||
 | 
					                line = linecache.getline(filename, lineno) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                name = '[unknown]' | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    src = inspect.getsourcelines(frame) | 
				
			||||||
 | 
					                    line = src[lineno] | 
				
			||||||
 | 
					                except IOError: | 
				
			||||||
 | 
					                    line = 'Unknown code named [%s].  VM instruction #%d' % ( | 
				
			||||||
 | 
					                        frame.f_code.co_name, frame.f_lasti) | 
				
			||||||
 | 
					            if self.trace_names is None or name in self.trace_names: | 
				
			||||||
 | 
					                print('%s:%s: %s' % (name, lineno, line.rstrip())) | 
				
			||||||
 | 
					                if not self.show_values: | 
				
			||||||
 | 
					                    return self | 
				
			||||||
 | 
					                details = [] | 
				
			||||||
 | 
					                tokens = _token_spliter.split(line) | 
				
			||||||
 | 
					                for tok in tokens: | 
				
			||||||
 | 
					                    if tok in frame.f_globals: | 
				
			||||||
 | 
					                        details.append('%s=%r' % (tok, frame.f_globals[tok])) | 
				
			||||||
 | 
					                    if tok in frame.f_locals: | 
				
			||||||
 | 
					                        details.append('%s=%r' % (tok, frame.f_locals[tok])) | 
				
			||||||
 | 
					                if details: | 
				
			||||||
 | 
					                    print("\t%s" % ' '.join(details)) | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def spew(trace_names=None, show_values=False): | 
				
			||||||
 | 
					    """Install a trace hook which writes incredibly detailed logs | 
				
			||||||
 | 
					    about what code is being executed to stdout. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    sys.settrace(Spew(trace_names, show_values)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def unspew(): | 
				
			||||||
 | 
					    """Remove the trace hook installed by spew. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    sys.settrace(None) | 
				
			||||||
@ -0,0 +1,29 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# We don't need to call super() in __init__ methods of our | 
				
			||||||
 | 
					# BaseException and Exception classes because we also define | 
				
			||||||
 | 
					# our own __str__ methods so there is no need to pass 'message' | 
				
			||||||
 | 
					# to the base class to get a meaningful output from 'str(exc)'. | 
				
			||||||
 | 
					# pylint: disable=super-init-not-called | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# we inherit from BaseException here to make sure to not be caught | 
				
			||||||
 | 
					# at application level | 
				
			||||||
 | 
					class HaltServer(BaseException): | 
				
			||||||
 | 
					    def __init__(self, reason, exit_status=1): | 
				
			||||||
 | 
					        self.reason = reason | 
				
			||||||
 | 
					        self.exit_status = exit_status | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "<HaltServer %r %d>" % (self.reason, self.exit_status) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConfigError(Exception): | 
				
			||||||
 | 
					    """ Exception raised on config error """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AppImportError(Exception): | 
				
			||||||
 | 
					    """ Exception raised when loading an application """ | 
				
			||||||
@ -0,0 +1,478 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import base64 | 
				
			||||||
 | 
					import binascii | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					import logging | 
				
			||||||
 | 
					logging.Logger.manager.emittedNoHandlerWarning = 1 | 
				
			||||||
 | 
					from logging.config import fileConfig | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    from logging.config import dictConfig | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    # python 2.6 | 
				
			||||||
 | 
					    dictConfig = None | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import socket | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import threading | 
				
			||||||
 | 
					import traceback | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn import util | 
				
			||||||
 | 
					from gunicorn.six import PY3, string_types | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# syslog facility codes | 
				
			||||||
 | 
					SYSLOG_FACILITIES = { | 
				
			||||||
 | 
					        "auth":     4, | 
				
			||||||
 | 
					        "authpriv": 10, | 
				
			||||||
 | 
					        "cron":     9, | 
				
			||||||
 | 
					        "daemon":   3, | 
				
			||||||
 | 
					        "ftp":      11, | 
				
			||||||
 | 
					        "kern":     0, | 
				
			||||||
 | 
					        "lpr":      6, | 
				
			||||||
 | 
					        "mail":     2, | 
				
			||||||
 | 
					        "news":     7, | 
				
			||||||
 | 
					        "security": 4,  #  DEPRECATED | 
				
			||||||
 | 
					        "syslog":   5, | 
				
			||||||
 | 
					        "user":     1, | 
				
			||||||
 | 
					        "uucp":     8, | 
				
			||||||
 | 
					        "local0":   16, | 
				
			||||||
 | 
					        "local1":   17, | 
				
			||||||
 | 
					        "local2":   18, | 
				
			||||||
 | 
					        "local3":   19, | 
				
			||||||
 | 
					        "local4":   20, | 
				
			||||||
 | 
					        "local5":   21, | 
				
			||||||
 | 
					        "local6":   22, | 
				
			||||||
 | 
					        "local7":   23 | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIG_DEFAULTS = dict( | 
				
			||||||
 | 
					        version=1, | 
				
			||||||
 | 
					        disable_existing_loggers=False, | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loggers={ | 
				
			||||||
 | 
					            "root": {"level": "INFO", "handlers": ["console"]}, | 
				
			||||||
 | 
					            "gunicorn.error": { | 
				
			||||||
 | 
					                "level": "INFO", | 
				
			||||||
 | 
					                "handlers": ["error_console"], | 
				
			||||||
 | 
					                "propagate": True, | 
				
			||||||
 | 
					                "qualname": "gunicorn.error" | 
				
			||||||
 | 
					            }, | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            "gunicorn.access": { | 
				
			||||||
 | 
					                "level": "INFO", | 
				
			||||||
 | 
					                "handlers": ["console"], | 
				
			||||||
 | 
					                "propagate": True, | 
				
			||||||
 | 
					                "qualname": "gunicorn.access" | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        }, | 
				
			||||||
 | 
					        handlers={ | 
				
			||||||
 | 
					            "console": { | 
				
			||||||
 | 
					                "class": "logging.StreamHandler", | 
				
			||||||
 | 
					                "formatter": "generic", | 
				
			||||||
 | 
					                "stream": "sys.stdout" | 
				
			||||||
 | 
					            }, | 
				
			||||||
 | 
					            "error_console": { | 
				
			||||||
 | 
					                "class": "logging.StreamHandler", | 
				
			||||||
 | 
					                "formatter": "generic", | 
				
			||||||
 | 
					                "stream": "sys.stderr" | 
				
			||||||
 | 
					            }, | 
				
			||||||
 | 
					        }, | 
				
			||||||
 | 
					        formatters={ | 
				
			||||||
 | 
					            "generic": { | 
				
			||||||
 | 
					                "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s", | 
				
			||||||
 | 
					                "datefmt": "[%Y-%m-%d %H:%M:%S %z]", | 
				
			||||||
 | 
					                "class": "logging.Formatter" | 
				
			||||||
 | 
					            } | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def loggers(): | 
				
			||||||
 | 
					    """ get list of all loggers """ | 
				
			||||||
 | 
					    root = logging.root | 
				
			||||||
 | 
					    existing = root.manager.loggerDict.keys() | 
				
			||||||
 | 
					    return [logging.getLogger(name) for name in existing] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SafeAtoms(dict): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, atoms): | 
				
			||||||
 | 
					        dict.__init__(self) | 
				
			||||||
 | 
					        for key, value in atoms.items(): | 
				
			||||||
 | 
					            if isinstance(value, string_types): | 
				
			||||||
 | 
					                self[key] = value.replace('"', '\\"') | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                self[key] = value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getitem__(self, k): | 
				
			||||||
 | 
					        if k.startswith("{"): | 
				
			||||||
 | 
					            kl = k.lower() | 
				
			||||||
 | 
					            if kl in self: | 
				
			||||||
 | 
					                return super(SafeAtoms, self).__getitem__(kl) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                return "-" | 
				
			||||||
 | 
					        if k in self: | 
				
			||||||
 | 
					            return super(SafeAtoms, self).__getitem__(k) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            return '-' | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def parse_syslog_address(addr): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # unix domain socket type depends on backend | 
				
			||||||
 | 
					    # SysLogHandler will try both when given None | 
				
			||||||
 | 
					    if addr.startswith("unix://"): | 
				
			||||||
 | 
					        sock_type = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set socket type only if explicitly requested | 
				
			||||||
 | 
					        parts = addr.split("#", 1) | 
				
			||||||
 | 
					        if len(parts) == 2: | 
				
			||||||
 | 
					            addr = parts[0] | 
				
			||||||
 | 
					            if parts[1] == "dgram": | 
				
			||||||
 | 
					                sock_type = socket.SOCK_DGRAM | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (sock_type, addr.split("unix://")[1]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if addr.startswith("udp://"): | 
				
			||||||
 | 
					        addr = addr.split("udp://")[1] | 
				
			||||||
 | 
					        socktype = socket.SOCK_DGRAM | 
				
			||||||
 | 
					    elif addr.startswith("tcp://"): | 
				
			||||||
 | 
					        addr = addr.split("tcp://")[1] | 
				
			||||||
 | 
					        socktype = socket.SOCK_STREAM | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        raise RuntimeError("invalid syslog address") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if '[' in addr and ']' in addr: | 
				
			||||||
 | 
					        host = addr.split(']')[0][1:].lower() | 
				
			||||||
 | 
					    elif ':' in addr: | 
				
			||||||
 | 
					        host = addr.split(':')[0].lower() | 
				
			||||||
 | 
					    elif addr == "": | 
				
			||||||
 | 
					        host = "localhost" | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        host = addr.lower() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addr = addr.split(']')[-1] | 
				
			||||||
 | 
					    if ":" in addr: | 
				
			||||||
 | 
					        port = addr.split(':', 1)[1] | 
				
			||||||
 | 
					        if not port.isdigit(): | 
				
			||||||
 | 
					            raise RuntimeError("%r is not a valid port number." % port) | 
				
			||||||
 | 
					        port = int(port) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        port = 514 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (socktype, (host, port)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Logger(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LOG_LEVELS = { | 
				
			||||||
 | 
					        "critical": logging.CRITICAL, | 
				
			||||||
 | 
					        "error": logging.ERROR, | 
				
			||||||
 | 
					        "warning": logging.WARNING, | 
				
			||||||
 | 
					        "info": logging.INFO, | 
				
			||||||
 | 
					        "debug": logging.DEBUG | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					    loglevel = logging.INFO | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    error_fmt = r"%(asctime)s [%(process)d] [%(levelname)s] %(message)s" | 
				
			||||||
 | 
					    datefmt = r"[%Y-%m-%d %H:%M:%S %z]" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    access_fmt = "%(message)s" | 
				
			||||||
 | 
					    syslog_fmt = "[%(process)d] %(message)s" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    atoms_wrapper_class = SafeAtoms | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, cfg): | 
				
			||||||
 | 
					        self.error_log = logging.getLogger("gunicorn.error") | 
				
			||||||
 | 
					        self.error_log.propagate = False | 
				
			||||||
 | 
					        self.access_log = logging.getLogger("gunicorn.access") | 
				
			||||||
 | 
					        self.access_log.propagate = False | 
				
			||||||
 | 
					        self.error_handlers = [] | 
				
			||||||
 | 
					        self.access_handlers = [] | 
				
			||||||
 | 
					        self.logfile = None | 
				
			||||||
 | 
					        self.lock = threading.Lock() | 
				
			||||||
 | 
					        self.cfg = cfg | 
				
			||||||
 | 
					        self.setup(cfg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setup(self, cfg): | 
				
			||||||
 | 
					        self.loglevel = self.LOG_LEVELS.get(cfg.loglevel.lower(), logging.INFO) | 
				
			||||||
 | 
					        self.error_log.setLevel(self.loglevel) | 
				
			||||||
 | 
					        self.access_log.setLevel(logging.INFO) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set gunicorn.error handler | 
				
			||||||
 | 
					        if self.cfg.capture_output and cfg.errorlog != "-": | 
				
			||||||
 | 
					            for stream in sys.stdout, sys.stderr: | 
				
			||||||
 | 
					                stream.flush() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.logfile = open(cfg.errorlog, 'a+') | 
				
			||||||
 | 
					            os.dup2(self.logfile.fileno(), sys.stdout.fileno()) | 
				
			||||||
 | 
					            os.dup2(self.logfile.fileno(), sys.stderr.fileno()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._set_handler(self.error_log, cfg.errorlog, | 
				
			||||||
 | 
					                          logging.Formatter(self.error_fmt, self.datefmt)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set gunicorn.access handler | 
				
			||||||
 | 
					        if cfg.accesslog is not None: | 
				
			||||||
 | 
					            self._set_handler(self.access_log, cfg.accesslog, | 
				
			||||||
 | 
					                fmt=logging.Formatter(self.access_fmt), stream=sys.stdout) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set syslog handler | 
				
			||||||
 | 
					        if cfg.syslog: | 
				
			||||||
 | 
					            self._set_syslog_handler( | 
				
			||||||
 | 
					                self.error_log, cfg, self.syslog_fmt, "error" | 
				
			||||||
 | 
					            ) | 
				
			||||||
 | 
					            if not cfg.disable_redirect_access_to_syslog: | 
				
			||||||
 | 
					                self._set_syslog_handler( | 
				
			||||||
 | 
					                    self.access_log, cfg, self.syslog_fmt, "access" | 
				
			||||||
 | 
					                ) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if dictConfig is None and cfg.logconfig_dict: | 
				
			||||||
 | 
					            util.warn("Dictionary-based log configuration requires " | 
				
			||||||
 | 
					                      "Python 2.7 or above.") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if dictConfig and cfg.logconfig_dict: | 
				
			||||||
 | 
					            config = CONFIG_DEFAULTS.copy() | 
				
			||||||
 | 
					            config.update(cfg.logconfig_dict) | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                dictConfig(config) | 
				
			||||||
 | 
					            except ( | 
				
			||||||
 | 
					                    AttributeError, | 
				
			||||||
 | 
					                    ImportError, | 
				
			||||||
 | 
					                    ValueError, | 
				
			||||||
 | 
					                    TypeError | 
				
			||||||
 | 
					            ) as exc: | 
				
			||||||
 | 
					                raise RuntimeError(str(exc)) | 
				
			||||||
 | 
					        elif cfg.logconfig: | 
				
			||||||
 | 
					            if os.path.exists(cfg.logconfig): | 
				
			||||||
 | 
					                defaults = CONFIG_DEFAULTS.copy() | 
				
			||||||
 | 
					                defaults['__file__'] = cfg.logconfig | 
				
			||||||
 | 
					                defaults['here'] = os.path.dirname(cfg.logconfig) | 
				
			||||||
 | 
					                fileConfig(cfg.logconfig, defaults=defaults, | 
				
			||||||
 | 
					                           disable_existing_loggers=False) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                msg = "Error: log config '%s' not found" | 
				
			||||||
 | 
					                raise RuntimeError(msg % cfg.logconfig) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def critical(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        self.error_log.critical(msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def error(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        self.error_log.error(msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def warning(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        self.error_log.warning(msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def info(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        self.error_log.info(msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def debug(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        self.error_log.debug(msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def exception(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        self.error_log.exception(msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def log(self, lvl, msg, *args, **kwargs): | 
				
			||||||
 | 
					        if isinstance(lvl, string_types): | 
				
			||||||
 | 
					            lvl = self.LOG_LEVELS.get(lvl.lower(), logging.INFO) | 
				
			||||||
 | 
					        self.error_log.log(lvl, msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def atoms(self, resp, req, environ, request_time): | 
				
			||||||
 | 
					        """ Gets atoms for log formating. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        status = resp.status | 
				
			||||||
 | 
					        if isinstance(status, str): | 
				
			||||||
 | 
					            status = status.split(None, 1)[0] | 
				
			||||||
 | 
					        atoms = { | 
				
			||||||
 | 
					            'h': environ.get('REMOTE_ADDR', '-'), | 
				
			||||||
 | 
					            'l': '-', | 
				
			||||||
 | 
					            'u': self._get_user(environ) or '-', | 
				
			||||||
 | 
					            't': self.now(), | 
				
			||||||
 | 
					            'r': "%s %s %s" % (environ['REQUEST_METHOD'], | 
				
			||||||
 | 
					                environ['RAW_URI'], environ["SERVER_PROTOCOL"]), | 
				
			||||||
 | 
					            's': status, | 
				
			||||||
 | 
					            'm': environ.get('REQUEST_METHOD'), | 
				
			||||||
 | 
					            'U': environ.get('PATH_INFO'), | 
				
			||||||
 | 
					            'q': environ.get('QUERY_STRING'), | 
				
			||||||
 | 
					            'H': environ.get('SERVER_PROTOCOL'), | 
				
			||||||
 | 
					            'b': getattr(resp, 'sent', None) and str(resp.sent) or '-', | 
				
			||||||
 | 
					            'B': getattr(resp, 'sent', None), | 
				
			||||||
 | 
					            'f': environ.get('HTTP_REFERER', '-'), | 
				
			||||||
 | 
					            'a': environ.get('HTTP_USER_AGENT', '-'), | 
				
			||||||
 | 
					            'T': request_time.seconds, | 
				
			||||||
 | 
					            'D': (request_time.seconds*1000000) + request_time.microseconds, | 
				
			||||||
 | 
					            'L': "%d.%06d" % (request_time.seconds, request_time.microseconds), | 
				
			||||||
 | 
					            'p': "<%s>" % os.getpid() | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # add request headers | 
				
			||||||
 | 
					        if hasattr(req, 'headers'): | 
				
			||||||
 | 
					            req_headers = req.headers | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            req_headers = req | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if hasattr(req_headers, "items"): | 
				
			||||||
 | 
					            req_headers = req_headers.items() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        atoms.update(dict([("{%s}i" % k.lower(), v) for k, v in req_headers])) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        resp_headers = resp.headers | 
				
			||||||
 | 
					        if hasattr(resp_headers, "items"): | 
				
			||||||
 | 
					            resp_headers = resp_headers.items() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # add response headers | 
				
			||||||
 | 
					        atoms.update(dict([("{%s}o" % k.lower(), v) for k, v in resp_headers])) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # add environ variables | 
				
			||||||
 | 
					        environ_variables = environ.items() | 
				
			||||||
 | 
					        atoms.update(dict([("{%s}e" % k.lower(), v) for k, v in environ_variables])) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return atoms | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def access(self, resp, req, environ, request_time): | 
				
			||||||
 | 
					        """ See http://httpd.apache.org/docs/2.0/logs.html#combined | 
				
			||||||
 | 
					        for format details | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not (self.cfg.accesslog or self.cfg.logconfig or | 
				
			||||||
 | 
					           self.cfg.logconfig_dict or | 
				
			||||||
 | 
					           (self.cfg.syslog and not self.cfg.disable_access_log_redirection)): | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # wrap atoms: | 
				
			||||||
 | 
					        # - make sure atoms will be test case insensitively | 
				
			||||||
 | 
					        # - if atom doesn't exist replace it by '-' | 
				
			||||||
 | 
					        safe_atoms = self.atoms_wrapper_class(self.atoms(resp, req, environ, | 
				
			||||||
 | 
					            request_time)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.access_log.info(self.cfg.access_log_format, safe_atoms) | 
				
			||||||
 | 
					        except: | 
				
			||||||
 | 
					            self.error(traceback.format_exc()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def now(self): | 
				
			||||||
 | 
					        """ return date in Apache Common Log Format """ | 
				
			||||||
 | 
					        return time.strftime('[%d/%b/%Y:%H:%M:%S %z]') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reopen_files(self): | 
				
			||||||
 | 
					        if self.cfg.capture_output and self.cfg.errorlog != "-": | 
				
			||||||
 | 
					            for stream in sys.stdout, sys.stderr: | 
				
			||||||
 | 
					                stream.flush() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            with self.lock: | 
				
			||||||
 | 
					                if self.logfile is not None: | 
				
			||||||
 | 
					                    self.logfile.close() | 
				
			||||||
 | 
					                self.logfile = open(self.cfg.errorlog, 'a+') | 
				
			||||||
 | 
					                os.dup2(self.logfile.fileno(), sys.stdout.fileno()) | 
				
			||||||
 | 
					                os.dup2(self.logfile.fileno(), sys.stderr.fileno()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for log in loggers(): | 
				
			||||||
 | 
					            for handler in log.handlers: | 
				
			||||||
 | 
					                if isinstance(handler, logging.FileHandler): | 
				
			||||||
 | 
					                    handler.acquire() | 
				
			||||||
 | 
					                    try: | 
				
			||||||
 | 
					                        if handler.stream: | 
				
			||||||
 | 
					                            handler.close() | 
				
			||||||
 | 
					                            handler.stream = handler._open() | 
				
			||||||
 | 
					                    finally: | 
				
			||||||
 | 
					                        handler.release() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def close_on_exec(self): | 
				
			||||||
 | 
					        for log in loggers(): | 
				
			||||||
 | 
					            for handler in log.handlers: | 
				
			||||||
 | 
					                if isinstance(handler, logging.FileHandler): | 
				
			||||||
 | 
					                    handler.acquire() | 
				
			||||||
 | 
					                    try: | 
				
			||||||
 | 
					                        if handler.stream: | 
				
			||||||
 | 
					                            util.close_on_exec(handler.stream.fileno()) | 
				
			||||||
 | 
					                    finally: | 
				
			||||||
 | 
					                        handler.release() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_gunicorn_handler(self, log): | 
				
			||||||
 | 
					        for h in log.handlers: | 
				
			||||||
 | 
					            if getattr(h, "_gunicorn", False): | 
				
			||||||
 | 
					                return h | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _set_handler(self, log, output, fmt, stream=None): | 
				
			||||||
 | 
					        # remove previous gunicorn log handler | 
				
			||||||
 | 
					        h = self._get_gunicorn_handler(log) | 
				
			||||||
 | 
					        if h: | 
				
			||||||
 | 
					            log.handlers.remove(h) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if output is not None: | 
				
			||||||
 | 
					            if output == "-": | 
				
			||||||
 | 
					                h = logging.StreamHandler(stream) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                util.check_is_writeable(output) | 
				
			||||||
 | 
					                h = logging.FileHandler(output) | 
				
			||||||
 | 
					                # make sure the user can reopen the file | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    os.chown(h.baseFilename, self.cfg.user, self.cfg.group) | 
				
			||||||
 | 
					                except OSError: | 
				
			||||||
 | 
					                    # it's probably OK there, we assume the user has given | 
				
			||||||
 | 
					                    # /dev/null as a parameter. | 
				
			||||||
 | 
					                    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            h.setFormatter(fmt) | 
				
			||||||
 | 
					            h._gunicorn = True | 
				
			||||||
 | 
					            log.addHandler(h) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _set_syslog_handler(self, log, cfg, fmt, name): | 
				
			||||||
 | 
					        # setup format | 
				
			||||||
 | 
					        if not cfg.syslog_prefix: | 
				
			||||||
 | 
					            prefix = cfg.proc_name.replace(":", ".") | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            prefix = cfg.syslog_prefix | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        prefix = "gunicorn.%s.%s" % (prefix, name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set format | 
				
			||||||
 | 
					        fmt = logging.Formatter(r"%s: %s" % (prefix, fmt)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # syslog facility | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            facility = SYSLOG_FACILITIES[cfg.syslog_facility.lower()] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise RuntimeError("unknown facility name") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # parse syslog address | 
				
			||||||
 | 
					        socktype, addr = parse_syslog_address(cfg.syslog_addr) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # finally setup the syslog handler | 
				
			||||||
 | 
					        if sys.version_info >= (2, 7): | 
				
			||||||
 | 
					            h = logging.handlers.SysLogHandler(address=addr, | 
				
			||||||
 | 
					                    facility=facility, socktype=socktype) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            # socktype is only supported in 2.7 and sup | 
				
			||||||
 | 
					            # fix issue #541 | 
				
			||||||
 | 
					            h = logging.handlers.SysLogHandler(address=addr, | 
				
			||||||
 | 
					                    facility=facility) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        h.setFormatter(fmt) | 
				
			||||||
 | 
					        h._gunicorn = True | 
				
			||||||
 | 
					        log.addHandler(h) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_user(self, environ): | 
				
			||||||
 | 
					        user = None | 
				
			||||||
 | 
					        http_auth = environ.get("HTTP_AUTHORIZATION") | 
				
			||||||
 | 
					        if http_auth and http_auth.startswith('Basic'): | 
				
			||||||
 | 
					            auth = http_auth.split(" ", 1) | 
				
			||||||
 | 
					            if len(auth) == 2: | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    # b64decode doesn't accept unicode in Python < 3.3 | 
				
			||||||
 | 
					                    # so we need to convert it to a byte string | 
				
			||||||
 | 
					                    auth = base64.b64decode(auth[1].strip().encode('utf-8')) | 
				
			||||||
 | 
					                    if PY3:  # b64decode returns a byte string in Python 3 | 
				
			||||||
 | 
					                        auth = auth.decode('utf-8') | 
				
			||||||
 | 
					                    auth = auth.split(":", 1) | 
				
			||||||
 | 
					                except (TypeError, binascii.Error, UnicodeDecodeError) as exc: | 
				
			||||||
 | 
					                    self.debug("Couldn't get username: %s", exc) | 
				
			||||||
 | 
					                    return user | 
				
			||||||
 | 
					                if len(auth) == 2: | 
				
			||||||
 | 
					                    user = auth[0] | 
				
			||||||
 | 
					        return user | 
				
			||||||
@ -0,0 +1,9 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn.http.message import Message, Request | 
				
			||||||
 | 
					from gunicorn.http.parser import RequestParser | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__all__ = ['Message', 'Request', 'RequestParser'] | 
				
			||||||
@ -0,0 +1,67 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import errno | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    import ctypes | 
				
			||||||
 | 
					    import ctypes.util | 
				
			||||||
 | 
					except MemoryError: | 
				
			||||||
 | 
					    # selinux execmem denial | 
				
			||||||
 | 
					    # https://bugzilla.redhat.com/show_bug.cgi?id=488396 | 
				
			||||||
 | 
					    raise ImportError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SUPPORTED_PLATFORMS = ( | 
				
			||||||
 | 
					    'darwin', | 
				
			||||||
 | 
					    'freebsd', | 
				
			||||||
 | 
					    'dragonfly', | 
				
			||||||
 | 
					    'linux2') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.platform not in SUPPORTED_PLATFORMS: | 
				
			||||||
 | 
					    raise ImportError("sendfile isn't supported on this platform") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) | 
				
			||||||
 | 
					_sendfile = _libc.sendfile | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def sendfile(fdout, fdin, offset, nbytes): | 
				
			||||||
 | 
					    if sys.platform == 'darwin': | 
				
			||||||
 | 
					        _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64, | 
				
			||||||
 | 
					                              ctypes.POINTER(ctypes.c_uint64), ctypes.c_voidp, | 
				
			||||||
 | 
					                              ctypes.c_int] | 
				
			||||||
 | 
					        _nbytes = ctypes.c_uint64(nbytes) | 
				
			||||||
 | 
					        result = _sendfile(fdin, fdout, offset, _nbytes, None, 0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if result == -1: | 
				
			||||||
 | 
					            e = ctypes.get_errno() | 
				
			||||||
 | 
					            if e == errno.EAGAIN and _nbytes.value is not None: | 
				
			||||||
 | 
					                return _nbytes.value | 
				
			||||||
 | 
					            raise OSError(e, os.strerror(e)) | 
				
			||||||
 | 
					        return _nbytes.value | 
				
			||||||
 | 
					    elif sys.platform in ('freebsd', 'dragonfly',): | 
				
			||||||
 | 
					        _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint64, | 
				
			||||||
 | 
					                              ctypes.c_uint64, ctypes.c_voidp, | 
				
			||||||
 | 
					                              ctypes.POINTER(ctypes.c_uint64), ctypes.c_int] | 
				
			||||||
 | 
					        _sbytes = ctypes.c_uint64() | 
				
			||||||
 | 
					        result = _sendfile(fdin, fdout, offset, nbytes, None, _sbytes, 0) | 
				
			||||||
 | 
					        if result == -1: | 
				
			||||||
 | 
					            e = ctypes.get_errno() | 
				
			||||||
 | 
					            if e == errno.EAGAIN and _sbytes.value is not None: | 
				
			||||||
 | 
					                return _sbytes.value | 
				
			||||||
 | 
					            raise OSError(e, os.strerror(e)) | 
				
			||||||
 | 
					        return _sbytes.value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        _sendfile.argtypes = [ctypes.c_int, ctypes.c_int, | 
				
			||||||
 | 
					                ctypes.POINTER(ctypes.c_uint64), ctypes.c_size_t] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _offset = ctypes.c_uint64(offset) | 
				
			||||||
 | 
					        sent = _sendfile(fdout, fdin, _offset, nbytes) | 
				
			||||||
 | 
					        if sent == -1: | 
				
			||||||
 | 
					            e = ctypes.get_errno() | 
				
			||||||
 | 
					            raise OSError(e, os.strerror(e)) | 
				
			||||||
 | 
					        return sent | 
				
			||||||
@ -0,0 +1,259 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn.http.errors import (NoMoreData, ChunkMissingTerminator, | 
				
			||||||
 | 
					        InvalidChunkSize) | 
				
			||||||
 | 
					from gunicorn import six | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChunkedReader(object): | 
				
			||||||
 | 
					    def __init__(self, req, unreader): | 
				
			||||||
 | 
					        self.req = req | 
				
			||||||
 | 
					        self.parser = self.parse_chunked(unreader) | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read(self, size): | 
				
			||||||
 | 
					        if not isinstance(size, six.integer_types): | 
				
			||||||
 | 
					            raise TypeError("size must be an integral type") | 
				
			||||||
 | 
					        if size < 0: | 
				
			||||||
 | 
					            raise ValueError("Size must be positive.") | 
				
			||||||
 | 
					        if size == 0: | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.parser: | 
				
			||||||
 | 
					            while self.buf.tell() < size: | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    self.buf.write(six.next(self.parser)) | 
				
			||||||
 | 
					                except StopIteration: | 
				
			||||||
 | 
					                    self.parser = None | 
				
			||||||
 | 
					                    break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = self.buf.getvalue() | 
				
			||||||
 | 
					        ret, rest = data[:size], data[size:] | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					        self.buf.write(rest) | 
				
			||||||
 | 
					        return ret | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_trailers(self, unreader, data): | 
				
			||||||
 | 
					        buf = six.BytesIO() | 
				
			||||||
 | 
					        buf.write(data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        idx = buf.getvalue().find(b"\r\n\r\n") | 
				
			||||||
 | 
					        done = buf.getvalue()[:2] == b"\r\n" | 
				
			||||||
 | 
					        while idx < 0 and not done: | 
				
			||||||
 | 
					            self.get_data(unreader, buf) | 
				
			||||||
 | 
					            idx = buf.getvalue().find(b"\r\n\r\n") | 
				
			||||||
 | 
					            done = buf.getvalue()[:2] == b"\r\n" | 
				
			||||||
 | 
					        if done: | 
				
			||||||
 | 
					            unreader.unread(buf.getvalue()[2:]) | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
 | 
					        self.req.trailers = self.req.parse_headers(buf.getvalue()[:idx]) | 
				
			||||||
 | 
					        unreader.unread(buf.getvalue()[idx + 4:]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_chunked(self, unreader): | 
				
			||||||
 | 
					        (size, rest) = self.parse_chunk_size(unreader) | 
				
			||||||
 | 
					        while size > 0: | 
				
			||||||
 | 
					            while size > len(rest): | 
				
			||||||
 | 
					                size -= len(rest) | 
				
			||||||
 | 
					                yield rest | 
				
			||||||
 | 
					                rest = unreader.read() | 
				
			||||||
 | 
					                if not rest: | 
				
			||||||
 | 
					                    raise NoMoreData() | 
				
			||||||
 | 
					            yield rest[:size] | 
				
			||||||
 | 
					            # Remove \r\n after chunk | 
				
			||||||
 | 
					            rest = rest[size:] | 
				
			||||||
 | 
					            while len(rest) < 2: | 
				
			||||||
 | 
					                rest += unreader.read() | 
				
			||||||
 | 
					            if rest[:2] != b'\r\n': | 
				
			||||||
 | 
					                raise ChunkMissingTerminator(rest[:2]) | 
				
			||||||
 | 
					            (size, rest) = self.parse_chunk_size(unreader, data=rest[2:]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_chunk_size(self, unreader, data=None): | 
				
			||||||
 | 
					        buf = six.BytesIO() | 
				
			||||||
 | 
					        if data is not None: | 
				
			||||||
 | 
					            buf.write(data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        idx = buf.getvalue().find(b"\r\n") | 
				
			||||||
 | 
					        while idx < 0: | 
				
			||||||
 | 
					            self.get_data(unreader, buf) | 
				
			||||||
 | 
					            idx = buf.getvalue().find(b"\r\n") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = buf.getvalue() | 
				
			||||||
 | 
					        line, rest_chunk = data[:idx], data[idx + 2:] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        chunk_size = line.split(b";", 1)[0].strip() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            chunk_size = int(chunk_size, 16) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise InvalidChunkSize(chunk_size) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if chunk_size == 0: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                self.parse_trailers(unreader, rest_chunk) | 
				
			||||||
 | 
					            except NoMoreData: | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					            return (0, None) | 
				
			||||||
 | 
					        return (chunk_size, rest_chunk) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_data(self, unreader, buf): | 
				
			||||||
 | 
					        data = unreader.read() | 
				
			||||||
 | 
					        if not data: | 
				
			||||||
 | 
					            raise NoMoreData() | 
				
			||||||
 | 
					        buf.write(data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LengthReader(object): | 
				
			||||||
 | 
					    def __init__(self, unreader, length): | 
				
			||||||
 | 
					        self.unreader = unreader | 
				
			||||||
 | 
					        self.length = length | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read(self, size): | 
				
			||||||
 | 
					        if not isinstance(size, six.integer_types): | 
				
			||||||
 | 
					            raise TypeError("size must be an integral type") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        size = min(self.length, size) | 
				
			||||||
 | 
					        if size < 0: | 
				
			||||||
 | 
					            raise ValueError("Size must be positive.") | 
				
			||||||
 | 
					        if size == 0: | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        buf = six.BytesIO() | 
				
			||||||
 | 
					        data = self.unreader.read() | 
				
			||||||
 | 
					        while data: | 
				
			||||||
 | 
					            buf.write(data) | 
				
			||||||
 | 
					            if buf.tell() >= size: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					            data = self.unreader.read() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        buf = buf.getvalue() | 
				
			||||||
 | 
					        ret, rest = buf[:size], buf[size:] | 
				
			||||||
 | 
					        self.unreader.unread(rest) | 
				
			||||||
 | 
					        self.length -= size | 
				
			||||||
 | 
					        return ret | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EOFReader(object): | 
				
			||||||
 | 
					    def __init__(self, unreader): | 
				
			||||||
 | 
					        self.unreader = unreader | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					        self.finished = False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read(self, size): | 
				
			||||||
 | 
					        if not isinstance(size, six.integer_types): | 
				
			||||||
 | 
					            raise TypeError("size must be an integral type") | 
				
			||||||
 | 
					        if size < 0: | 
				
			||||||
 | 
					            raise ValueError("Size must be positive.") | 
				
			||||||
 | 
					        if size == 0: | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.finished: | 
				
			||||||
 | 
					            data = self.buf.getvalue() | 
				
			||||||
 | 
					            ret, rest = data[:size], data[size:] | 
				
			||||||
 | 
					            self.buf = six.BytesIO() | 
				
			||||||
 | 
					            self.buf.write(rest) | 
				
			||||||
 | 
					            return ret | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = self.unreader.read() | 
				
			||||||
 | 
					        while data: | 
				
			||||||
 | 
					            self.buf.write(data) | 
				
			||||||
 | 
					            if self.buf.tell() > size: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					            data = self.unreader.read() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not data: | 
				
			||||||
 | 
					            self.finished = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = self.buf.getvalue() | 
				
			||||||
 | 
					        ret, rest = data[:size], data[size:] | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					        self.buf.write(rest) | 
				
			||||||
 | 
					        return ret | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Body(object): | 
				
			||||||
 | 
					    def __init__(self, reader): | 
				
			||||||
 | 
					        self.reader = reader | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self): | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __next__(self): | 
				
			||||||
 | 
					        ret = self.readline() | 
				
			||||||
 | 
					        if not ret: | 
				
			||||||
 | 
					            raise StopIteration() | 
				
			||||||
 | 
					        return ret | 
				
			||||||
 | 
					    next = __next__ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getsize(self, size): | 
				
			||||||
 | 
					        if size is None: | 
				
			||||||
 | 
					            return six.MAXSIZE | 
				
			||||||
 | 
					        elif not isinstance(size, six.integer_types): | 
				
			||||||
 | 
					            raise TypeError("size must be an integral type") | 
				
			||||||
 | 
					        elif size < 0: | 
				
			||||||
 | 
					            return six.MAXSIZE | 
				
			||||||
 | 
					        return size | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read(self, size=None): | 
				
			||||||
 | 
					        size = self.getsize(size) | 
				
			||||||
 | 
					        if size == 0: | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if size < self.buf.tell(): | 
				
			||||||
 | 
					            data = self.buf.getvalue() | 
				
			||||||
 | 
					            ret, rest = data[:size], data[size:] | 
				
			||||||
 | 
					            self.buf = six.BytesIO() | 
				
			||||||
 | 
					            self.buf.write(rest) | 
				
			||||||
 | 
					            return ret | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while size > self.buf.tell(): | 
				
			||||||
 | 
					            data = self.reader.read(1024) | 
				
			||||||
 | 
					            if not data: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					            self.buf.write(data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = self.buf.getvalue() | 
				
			||||||
 | 
					        ret, rest = data[:size], data[size:] | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					        self.buf.write(rest) | 
				
			||||||
 | 
					        return ret | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def readline(self, size=None): | 
				
			||||||
 | 
					        size = self.getsize(size) | 
				
			||||||
 | 
					        if size == 0: | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = self.buf.getvalue() | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ret = [] | 
				
			||||||
 | 
					        while 1: | 
				
			||||||
 | 
					            idx = data.find(b"\n", 0, size) | 
				
			||||||
 | 
					            idx = idx + 1 if idx >= 0 else size if len(data) >= size else 0 | 
				
			||||||
 | 
					            if idx: | 
				
			||||||
 | 
					                ret.append(data[:idx]) | 
				
			||||||
 | 
					                self.buf.write(data[idx:]) | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ret.append(data) | 
				
			||||||
 | 
					            size -= len(data) | 
				
			||||||
 | 
					            data = self.reader.read(min(1024, size)) | 
				
			||||||
 | 
					            if not data: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return b"".join(ret) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def readlines(self, size=None): | 
				
			||||||
 | 
					        ret = [] | 
				
			||||||
 | 
					        data = self.read() | 
				
			||||||
 | 
					        while data: | 
				
			||||||
 | 
					            pos = data.find(b"\n") | 
				
			||||||
 | 
					            if pos < 0: | 
				
			||||||
 | 
					                ret.append(data) | 
				
			||||||
 | 
					                data = b"" | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                line, data = data[:pos + 1], data[pos + 1:] | 
				
			||||||
 | 
					                ret.append(line) | 
				
			||||||
 | 
					        return ret | 
				
			||||||
@ -0,0 +1,120 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# We don't need to call super() in __init__ methods of our | 
				
			||||||
 | 
					# BaseException and Exception classes because we also define | 
				
			||||||
 | 
					# our own __str__ methods so there is no need to pass 'message' | 
				
			||||||
 | 
					# to the base class to get a meaningful output from 'str(exc)'. | 
				
			||||||
 | 
					# pylint: disable=super-init-not-called | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ParseException(Exception): | 
				
			||||||
 | 
					    pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NoMoreData(IOError): | 
				
			||||||
 | 
					    def __init__(self, buf=None): | 
				
			||||||
 | 
					        self.buf = buf | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "No more data after: %r" % self.buf | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidRequestLine(ParseException): | 
				
			||||||
 | 
					    def __init__(self, req): | 
				
			||||||
 | 
					        self.req = req | 
				
			||||||
 | 
					        self.code = 400 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Invalid HTTP request line: %r" % self.req | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidRequestMethod(ParseException): | 
				
			||||||
 | 
					    def __init__(self, method): | 
				
			||||||
 | 
					        self.method = method | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Invalid HTTP method: %r" % self.method | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidHTTPVersion(ParseException): | 
				
			||||||
 | 
					    def __init__(self, version): | 
				
			||||||
 | 
					        self.version = version | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Invalid HTTP Version: %r" % self.version | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidHeader(ParseException): | 
				
			||||||
 | 
					    def __init__(self, hdr, req=None): | 
				
			||||||
 | 
					        self.hdr = hdr | 
				
			||||||
 | 
					        self.req = req | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Invalid HTTP Header: %r" % self.hdr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidHeaderName(ParseException): | 
				
			||||||
 | 
					    def __init__(self, hdr): | 
				
			||||||
 | 
					        self.hdr = hdr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Invalid HTTP header name: %r" % self.hdr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidChunkSize(IOError): | 
				
			||||||
 | 
					    def __init__(self, data): | 
				
			||||||
 | 
					        self.data = data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Invalid chunk size: %r" % self.data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChunkMissingTerminator(IOError): | 
				
			||||||
 | 
					    def __init__(self, term): | 
				
			||||||
 | 
					        self.term = term | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Invalid chunk terminator is not '\\r\\n': %r" % self.term | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LimitRequestLine(ParseException): | 
				
			||||||
 | 
					    def __init__(self, size, max_size): | 
				
			||||||
 | 
					        self.size = size | 
				
			||||||
 | 
					        self.max_size = max_size | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Request Line is too large (%s > %s)" % (self.size, self.max_size) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LimitRequestHeaders(ParseException): | 
				
			||||||
 | 
					    def __init__(self, msg): | 
				
			||||||
 | 
					        self.msg = msg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return self.msg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidProxyLine(ParseException): | 
				
			||||||
 | 
					    def __init__(self, line): | 
				
			||||||
 | 
					        self.line = line | 
				
			||||||
 | 
					        self.code = 400 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Invalid PROXY line: %r" % self.line | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ForbiddenProxyRequest(ParseException): | 
				
			||||||
 | 
					    def __init__(self, host): | 
				
			||||||
 | 
					        self.host = host | 
				
			||||||
 | 
					        self.code = 403 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Proxy request from %r not allowed" % self.host | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InvalidSchemeHeaders(ParseException): | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "Contradictory scheme headers" | 
				
			||||||
@ -0,0 +1,363 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import re | 
				
			||||||
 | 
					import socket | 
				
			||||||
 | 
					from errno import ENOTCONN | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn._compat import bytes_to_str | 
				
			||||||
 | 
					from gunicorn.http.unreader import SocketUnreader | 
				
			||||||
 | 
					from gunicorn.http.body import ChunkedReader, LengthReader, EOFReader, Body | 
				
			||||||
 | 
					from gunicorn.http.errors import (InvalidHeader, InvalidHeaderName, NoMoreData, | 
				
			||||||
 | 
					    InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, | 
				
			||||||
 | 
					    LimitRequestLine, LimitRequestHeaders) | 
				
			||||||
 | 
					from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest | 
				
			||||||
 | 
					from gunicorn.http.errors import InvalidSchemeHeaders | 
				
			||||||
 | 
					from gunicorn.six import BytesIO, string_types | 
				
			||||||
 | 
					from gunicorn.util import split_request_uri | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAX_REQUEST_LINE = 8190 | 
				
			||||||
 | 
					MAX_HEADERS = 32768 | 
				
			||||||
 | 
					DEFAULT_MAX_HEADERFIELD_SIZE = 8190 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HEADER_RE = re.compile(r"[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\"]") | 
				
			||||||
 | 
					METH_RE = re.compile(r"[A-Z0-9$-_.]{3,20}") | 
				
			||||||
 | 
					VERSION_RE = re.compile(r"HTTP/(\d+)\.(\d+)") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Message(object): | 
				
			||||||
 | 
					    def __init__(self, cfg, unreader): | 
				
			||||||
 | 
					        self.cfg = cfg | 
				
			||||||
 | 
					        self.unreader = unreader | 
				
			||||||
 | 
					        self.version = None | 
				
			||||||
 | 
					        self.headers = [] | 
				
			||||||
 | 
					        self.trailers = [] | 
				
			||||||
 | 
					        self.body = None | 
				
			||||||
 | 
					        self.scheme = "https" if cfg.is_ssl else "http" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set headers limits | 
				
			||||||
 | 
					        self.limit_request_fields = cfg.limit_request_fields | 
				
			||||||
 | 
					        if (self.limit_request_fields <= 0 | 
				
			||||||
 | 
					            or self.limit_request_fields > MAX_HEADERS): | 
				
			||||||
 | 
					            self.limit_request_fields = MAX_HEADERS | 
				
			||||||
 | 
					        self.limit_request_field_size = cfg.limit_request_field_size | 
				
			||||||
 | 
					        if self.limit_request_field_size < 0: | 
				
			||||||
 | 
					            self.limit_request_field_size = DEFAULT_MAX_HEADERFIELD_SIZE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set max header buffer size | 
				
			||||||
 | 
					        max_header_field_size = self.limit_request_field_size or DEFAULT_MAX_HEADERFIELD_SIZE | 
				
			||||||
 | 
					        self.max_buffer_headers = self.limit_request_fields * \ | 
				
			||||||
 | 
					            (max_header_field_size + 2) + 4 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unused = self.parse(self.unreader) | 
				
			||||||
 | 
					        self.unreader.unread(unused) | 
				
			||||||
 | 
					        self.set_body_reader() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse(self, unreader): | 
				
			||||||
 | 
					        raise NotImplementedError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_headers(self, data): | 
				
			||||||
 | 
					        cfg = self.cfg | 
				
			||||||
 | 
					        headers = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Split lines on \r\n keeping the \r\n on each line | 
				
			||||||
 | 
					        lines = [bytes_to_str(line) + "\r\n" for line in data.split(b"\r\n")] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # handle scheme headers | 
				
			||||||
 | 
					        scheme_header = False | 
				
			||||||
 | 
					        secure_scheme_headers = {} | 
				
			||||||
 | 
					        if '*' in cfg.forwarded_allow_ips: | 
				
			||||||
 | 
					            secure_scheme_headers = cfg.secure_scheme_headers | 
				
			||||||
 | 
					        elif isinstance(self.unreader, SocketUnreader): | 
				
			||||||
 | 
					            remote_addr = self.unreader.sock.getpeername() | 
				
			||||||
 | 
					            if isinstance(remote_addr, tuple): | 
				
			||||||
 | 
					                remote_host = remote_addr[0] | 
				
			||||||
 | 
					                if remote_host in cfg.forwarded_allow_ips: | 
				
			||||||
 | 
					                    secure_scheme_headers = cfg.secure_scheme_headers | 
				
			||||||
 | 
					            elif isinstance(remote_addr, string_types): | 
				
			||||||
 | 
					                secure_scheme_headers = cfg.secure_scheme_headers | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Parse headers into key/value pairs paying attention | 
				
			||||||
 | 
					        # to continuation lines. | 
				
			||||||
 | 
					        while lines: | 
				
			||||||
 | 
					            if len(headers) >= self.limit_request_fields: | 
				
			||||||
 | 
					                raise LimitRequestHeaders("limit request headers fields") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Parse initial header name : value pair. | 
				
			||||||
 | 
					            curr = lines.pop(0) | 
				
			||||||
 | 
					            header_length = len(curr) | 
				
			||||||
 | 
					            if curr.find(":") < 0: | 
				
			||||||
 | 
					                raise InvalidHeader(curr.strip()) | 
				
			||||||
 | 
					            name, value = curr.split(":", 1) | 
				
			||||||
 | 
					            name = name.rstrip(" \t").upper() | 
				
			||||||
 | 
					            if HEADER_RE.search(name): | 
				
			||||||
 | 
					                raise InvalidHeaderName(name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            name, value = name.strip(), [value.lstrip()] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Consume value continuation lines | 
				
			||||||
 | 
					            while lines and lines[0].startswith((" ", "\t")): | 
				
			||||||
 | 
					                curr = lines.pop(0) | 
				
			||||||
 | 
					                header_length += len(curr) | 
				
			||||||
 | 
					                if header_length > self.limit_request_field_size > 0: | 
				
			||||||
 | 
					                    raise LimitRequestHeaders("limit request headers " | 
				
			||||||
 | 
					                            + "fields size") | 
				
			||||||
 | 
					                value.append(curr) | 
				
			||||||
 | 
					            value = ''.join(value).rstrip() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if header_length > self.limit_request_field_size > 0: | 
				
			||||||
 | 
					                raise LimitRequestHeaders("limit request headers fields size") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if name in secure_scheme_headers: | 
				
			||||||
 | 
					                secure = value == secure_scheme_headers[name] | 
				
			||||||
 | 
					                scheme = "https" if secure else "http" | 
				
			||||||
 | 
					                if scheme_header: | 
				
			||||||
 | 
					                    if scheme != self.scheme: | 
				
			||||||
 | 
					                        raise InvalidSchemeHeaders() | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    scheme_header = True | 
				
			||||||
 | 
					                    self.scheme = scheme | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            headers.append((name, value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return headers | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_body_reader(self): | 
				
			||||||
 | 
					        chunked = False | 
				
			||||||
 | 
					        content_length = None | 
				
			||||||
 | 
					        for (name, value) in self.headers: | 
				
			||||||
 | 
					            if name == "CONTENT-LENGTH": | 
				
			||||||
 | 
					                content_length = value | 
				
			||||||
 | 
					            elif name == "TRANSFER-ENCODING": | 
				
			||||||
 | 
					                chunked = value.lower() == "chunked" | 
				
			||||||
 | 
					            elif name == "SEC-WEBSOCKET-KEY1": | 
				
			||||||
 | 
					                content_length = 8 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if chunked: | 
				
			||||||
 | 
					            self.body = Body(ChunkedReader(self, self.unreader)) | 
				
			||||||
 | 
					        elif content_length is not None: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                content_length = int(content_length) | 
				
			||||||
 | 
					            except ValueError: | 
				
			||||||
 | 
					                raise InvalidHeader("CONTENT-LENGTH", req=self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if content_length < 0: | 
				
			||||||
 | 
					                raise InvalidHeader("CONTENT-LENGTH", req=self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.body = Body(LengthReader(self.unreader, content_length)) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.body = Body(EOFReader(self.unreader)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def should_close(self): | 
				
			||||||
 | 
					        for (h, v) in self.headers: | 
				
			||||||
 | 
					            if h == "CONNECTION": | 
				
			||||||
 | 
					                v = v.lower().strip() | 
				
			||||||
 | 
					                if v == "close": | 
				
			||||||
 | 
					                    return True | 
				
			||||||
 | 
					                elif v == "keep-alive": | 
				
			||||||
 | 
					                    return False | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					        return self.version <= (1, 0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Request(Message): | 
				
			||||||
 | 
					    def __init__(self, cfg, unreader, req_number=1): | 
				
			||||||
 | 
					        self.method = None | 
				
			||||||
 | 
					        self.uri = None | 
				
			||||||
 | 
					        self.path = None | 
				
			||||||
 | 
					        self.query = None | 
				
			||||||
 | 
					        self.fragment = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # get max request line size | 
				
			||||||
 | 
					        self.limit_request_line = cfg.limit_request_line | 
				
			||||||
 | 
					        if (self.limit_request_line < 0 | 
				
			||||||
 | 
					            or self.limit_request_line >= MAX_REQUEST_LINE): | 
				
			||||||
 | 
					            self.limit_request_line = MAX_REQUEST_LINE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.req_number = req_number | 
				
			||||||
 | 
					        self.proxy_protocol_info = None | 
				
			||||||
 | 
					        super(Request, self).__init__(cfg, unreader) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_data(self, unreader, buf, stop=False): | 
				
			||||||
 | 
					        data = unreader.read() | 
				
			||||||
 | 
					        if not data: | 
				
			||||||
 | 
					            if stop: | 
				
			||||||
 | 
					                raise StopIteration() | 
				
			||||||
 | 
					            raise NoMoreData(buf.getvalue()) | 
				
			||||||
 | 
					        buf.write(data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse(self, unreader): | 
				
			||||||
 | 
					        buf = BytesIO() | 
				
			||||||
 | 
					        self.get_data(unreader, buf, stop=True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # get request line | 
				
			||||||
 | 
					        line, rbuf = self.read_line(unreader, buf, self.limit_request_line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # proxy protocol | 
				
			||||||
 | 
					        if self.proxy_protocol(bytes_to_str(line)): | 
				
			||||||
 | 
					            # get next request line | 
				
			||||||
 | 
					            buf = BytesIO() | 
				
			||||||
 | 
					            buf.write(rbuf) | 
				
			||||||
 | 
					            line, rbuf = self.read_line(unreader, buf, self.limit_request_line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.parse_request_line(line) | 
				
			||||||
 | 
					        buf = BytesIO() | 
				
			||||||
 | 
					        buf.write(rbuf) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Headers | 
				
			||||||
 | 
					        data = buf.getvalue() | 
				
			||||||
 | 
					        idx = data.find(b"\r\n\r\n") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        done = data[:2] == b"\r\n" | 
				
			||||||
 | 
					        while True: | 
				
			||||||
 | 
					            idx = data.find(b"\r\n\r\n") | 
				
			||||||
 | 
					            done = data[:2] == b"\r\n" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if idx < 0 and not done: | 
				
			||||||
 | 
					                self.get_data(unreader, buf) | 
				
			||||||
 | 
					                data = buf.getvalue() | 
				
			||||||
 | 
					                if len(data) > self.max_buffer_headers: | 
				
			||||||
 | 
					                    raise LimitRequestHeaders("max buffer headers") | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if done: | 
				
			||||||
 | 
					            self.unreader.unread(data[2:]) | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.headers = self.parse_headers(data[:idx]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ret = data[idx + 4:] | 
				
			||||||
 | 
					        buf = None | 
				
			||||||
 | 
					        return ret | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read_line(self, unreader, buf, limit=0): | 
				
			||||||
 | 
					        data = buf.getvalue() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while True: | 
				
			||||||
 | 
					            idx = data.find(b"\r\n") | 
				
			||||||
 | 
					            if idx >= 0: | 
				
			||||||
 | 
					                # check if the request line is too large | 
				
			||||||
 | 
					                if idx > limit > 0: | 
				
			||||||
 | 
					                    raise LimitRequestLine(idx, limit) | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					            elif len(data) - 2 > limit > 0: | 
				
			||||||
 | 
					                raise LimitRequestLine(len(data), limit) | 
				
			||||||
 | 
					            self.get_data(unreader, buf) | 
				
			||||||
 | 
					            data = buf.getvalue() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (data[:idx],  # request line, | 
				
			||||||
 | 
					                data[idx + 2:])  # residue in the buffer, skip \r\n | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def proxy_protocol(self, line): | 
				
			||||||
 | 
					        """\ | 
				
			||||||
 | 
					        Detect, check and parse proxy protocol. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :raises: ForbiddenProxyRequest, InvalidProxyLine. | 
				
			||||||
 | 
					        :return: True for proxy protocol line else False | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        if not self.cfg.proxy_protocol: | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.req_number != 1: | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not line.startswith("PROXY"): | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.proxy_protocol_access_check() | 
				
			||||||
 | 
					        self.parse_proxy_protocol(line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def proxy_protocol_access_check(self): | 
				
			||||||
 | 
					        # check in allow list | 
				
			||||||
 | 
					        if isinstance(self.unreader, SocketUnreader): | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                remote_host = self.unreader.sock.getpeername()[0] | 
				
			||||||
 | 
					            except socket.error as e: | 
				
			||||||
 | 
					                if e.args[0] == ENOTCONN: | 
				
			||||||
 | 
					                    raise ForbiddenProxyRequest("UNKNOW") | 
				
			||||||
 | 
					                raise | 
				
			||||||
 | 
					            if ("*" not in self.cfg.proxy_allow_ips and | 
				
			||||||
 | 
					                    remote_host not in self.cfg.proxy_allow_ips): | 
				
			||||||
 | 
					                raise ForbiddenProxyRequest(remote_host) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_proxy_protocol(self, line): | 
				
			||||||
 | 
					        bits = line.split() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if len(bits) != 6: | 
				
			||||||
 | 
					            raise InvalidProxyLine(line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Extract data | 
				
			||||||
 | 
					        proto = bits[1] | 
				
			||||||
 | 
					        s_addr = bits[2] | 
				
			||||||
 | 
					        d_addr = bits[3] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Validation | 
				
			||||||
 | 
					        if proto not in ["TCP4", "TCP6"]: | 
				
			||||||
 | 
					            raise InvalidProxyLine("protocol '%s' not supported" % proto) | 
				
			||||||
 | 
					        if proto == "TCP4": | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                socket.inet_pton(socket.AF_INET, s_addr) | 
				
			||||||
 | 
					                socket.inet_pton(socket.AF_INET, d_addr) | 
				
			||||||
 | 
					            except socket.error: | 
				
			||||||
 | 
					                raise InvalidProxyLine(line) | 
				
			||||||
 | 
					        elif proto == "TCP6": | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                socket.inet_pton(socket.AF_INET6, s_addr) | 
				
			||||||
 | 
					                socket.inet_pton(socket.AF_INET6, d_addr) | 
				
			||||||
 | 
					            except socket.error: | 
				
			||||||
 | 
					                raise InvalidProxyLine(line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            s_port = int(bits[4]) | 
				
			||||||
 | 
					            d_port = int(bits[5]) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise InvalidProxyLine("invalid port %s" % line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not ((0 <= s_port <= 65535) and (0 <= d_port <= 65535)): | 
				
			||||||
 | 
					            raise InvalidProxyLine("invalid port %s" % line) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Set data | 
				
			||||||
 | 
					        self.proxy_protocol_info = { | 
				
			||||||
 | 
					            "proxy_protocol": proto, | 
				
			||||||
 | 
					            "client_addr": s_addr, | 
				
			||||||
 | 
					            "client_port": s_port, | 
				
			||||||
 | 
					            "proxy_addr": d_addr, | 
				
			||||||
 | 
					            "proxy_port": d_port | 
				
			||||||
 | 
					        } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_request_line(self, line_bytes): | 
				
			||||||
 | 
					        bits = [bytes_to_str(bit) for bit in line_bytes.split(None, 2)] | 
				
			||||||
 | 
					        if len(bits) != 3: | 
				
			||||||
 | 
					            raise InvalidRequestLine(bytes_to_str(line_bytes)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Method | 
				
			||||||
 | 
					        if not METH_RE.match(bits[0]): | 
				
			||||||
 | 
					            raise InvalidRequestMethod(bits[0]) | 
				
			||||||
 | 
					        self.method = bits[0].upper() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # URI | 
				
			||||||
 | 
					        self.uri = bits[1] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            parts = split_request_uri(self.uri) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            raise InvalidRequestLine(bytes_to_str(line_bytes)) | 
				
			||||||
 | 
					        self.path = parts.path or "" | 
				
			||||||
 | 
					        self.query = parts.query or "" | 
				
			||||||
 | 
					        self.fragment = parts.fragment or "" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Version | 
				
			||||||
 | 
					        match = VERSION_RE.match(bits[2]) | 
				
			||||||
 | 
					        if match is None: | 
				
			||||||
 | 
					            raise InvalidHTTPVersion(bits[2]) | 
				
			||||||
 | 
					        self.version = (int(match.group(1)), int(match.group(2))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_body_reader(self): | 
				
			||||||
 | 
					        super(Request, self).set_body_reader() | 
				
			||||||
 | 
					        if isinstance(self.body.reader, EOFReader): | 
				
			||||||
 | 
					            self.body = Body(LengthReader(self.unreader, 0)) | 
				
			||||||
@ -0,0 +1,51 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn.http.message import Request | 
				
			||||||
 | 
					from gunicorn.http.unreader import SocketUnreader, IterUnreader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Parser(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mesg_class = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, cfg, source): | 
				
			||||||
 | 
					        self.cfg = cfg | 
				
			||||||
 | 
					        if hasattr(source, "recv"): | 
				
			||||||
 | 
					            self.unreader = SocketUnreader(source) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.unreader = IterUnreader(source) | 
				
			||||||
 | 
					        self.mesg = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # request counter (for keepalive connetions) | 
				
			||||||
 | 
					        self.req_count = 0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self): | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __next__(self): | 
				
			||||||
 | 
					        # Stop if HTTP dictates a stop. | 
				
			||||||
 | 
					        if self.mesg and self.mesg.should_close(): | 
				
			||||||
 | 
					            raise StopIteration() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Discard any unread body of the previous message | 
				
			||||||
 | 
					        if self.mesg: | 
				
			||||||
 | 
					            data = self.mesg.body.read(8192) | 
				
			||||||
 | 
					            while data: | 
				
			||||||
 | 
					                data = self.mesg.body.read(8192) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Parse the next request | 
				
			||||||
 | 
					        self.req_count += 1 | 
				
			||||||
 | 
					        self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count) | 
				
			||||||
 | 
					        if not self.mesg: | 
				
			||||||
 | 
					            raise StopIteration() | 
				
			||||||
 | 
					        return self.mesg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    next = __next__ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RequestParser(Parser): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mesg_class = Request | 
				
			||||||
@ -0,0 +1,80 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn import six | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Classes that can undo reading data from | 
				
			||||||
 | 
					# a given type of data source. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Unreader(object): | 
				
			||||||
 | 
					    def __init__(self): | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def chunk(self): | 
				
			||||||
 | 
					        raise NotImplementedError() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read(self, size=None): | 
				
			||||||
 | 
					        if size is not None and not isinstance(size, six.integer_types): | 
				
			||||||
 | 
					            raise TypeError("size parameter must be an int or long.") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if size is not None: | 
				
			||||||
 | 
					            if size == 0: | 
				
			||||||
 | 
					                return b"" | 
				
			||||||
 | 
					            if size < 0: | 
				
			||||||
 | 
					                size = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.buf.seek(0, os.SEEK_END) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if size is None and self.buf.tell(): | 
				
			||||||
 | 
					            ret = self.buf.getvalue() | 
				
			||||||
 | 
					            self.buf = six.BytesIO() | 
				
			||||||
 | 
					            return ret | 
				
			||||||
 | 
					        if size is None: | 
				
			||||||
 | 
					            d = self.chunk() | 
				
			||||||
 | 
					            return d | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while self.buf.tell() < size: | 
				
			||||||
 | 
					            chunk = self.chunk() | 
				
			||||||
 | 
					            if not chunk: | 
				
			||||||
 | 
					                ret = self.buf.getvalue() | 
				
			||||||
 | 
					                self.buf = six.BytesIO() | 
				
			||||||
 | 
					                return ret | 
				
			||||||
 | 
					            self.buf.write(chunk) | 
				
			||||||
 | 
					        data = self.buf.getvalue() | 
				
			||||||
 | 
					        self.buf = six.BytesIO() | 
				
			||||||
 | 
					        self.buf.write(data[size:]) | 
				
			||||||
 | 
					        return data[:size] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def unread(self, data): | 
				
			||||||
 | 
					        self.buf.seek(0, os.SEEK_END) | 
				
			||||||
 | 
					        self.buf.write(data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SocketUnreader(Unreader): | 
				
			||||||
 | 
					    def __init__(self, sock, max_chunk=8192): | 
				
			||||||
 | 
					        super(SocketUnreader, self).__init__() | 
				
			||||||
 | 
					        self.sock = sock | 
				
			||||||
 | 
					        self.mxchunk = max_chunk | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def chunk(self): | 
				
			||||||
 | 
					        return self.sock.recv(self.mxchunk) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class IterUnreader(Unreader): | 
				
			||||||
 | 
					    def __init__(self, iterable): | 
				
			||||||
 | 
					        super(IterUnreader, self).__init__() | 
				
			||||||
 | 
					        self.iter = iter(iterable) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def chunk(self): | 
				
			||||||
 | 
					        if not self.iter: | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return six.next(self.iter) | 
				
			||||||
 | 
					        except StopIteration: | 
				
			||||||
 | 
					            self.iter = None | 
				
			||||||
 | 
					            return b"" | 
				
			||||||
@ -0,0 +1,411 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io | 
				
			||||||
 | 
					import logging | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import re | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn._compat import unquote_to_wsgi_str | 
				
			||||||
 | 
					from gunicorn.http.message import HEADER_RE | 
				
			||||||
 | 
					from gunicorn.http.errors import InvalidHeader, InvalidHeaderName | 
				
			||||||
 | 
					from gunicorn.six import string_types, binary_type, reraise | 
				
			||||||
 | 
					from gunicorn import SERVER_SOFTWARE | 
				
			||||||
 | 
					import gunicorn.util as util | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    # Python 3.3 has os.sendfile(). | 
				
			||||||
 | 
					    from os import sendfile | 
				
			||||||
 | 
					except ImportError: | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        from ._sendfile import sendfile | 
				
			||||||
 | 
					    except ImportError: | 
				
			||||||
 | 
					        sendfile = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Send files in at most 1GB blocks as some operating systems can have problems | 
				
			||||||
 | 
					# with sending files in blocks over 2GB. | 
				
			||||||
 | 
					BLKSIZE = 0x3FFFFFFF | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HEADER_VALUE_RE = re.compile(r'[\x00-\x1F\x7F]') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					log = logging.getLogger(__name__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FileWrapper(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class WSGIErrorsWrapper(io.RawIOBase): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, cfg): | 
				
			||||||
 | 
					        # There is no public __init__ method for RawIOBase so | 
				
			||||||
 | 
					        # we don't need to call super() in the __init__ method. | 
				
			||||||
 | 
					        # pylint: disable=super-init-not-called | 
				
			||||||
 | 
					        errorlog = logging.getLogger("gunicorn.error") | 
				
			||||||
 | 
					        handlers = errorlog.handlers | 
				
			||||||
 | 
					        self.streams = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if cfg.errorlog == "-": | 
				
			||||||
 | 
					            self.streams.append(sys.stderr) | 
				
			||||||
 | 
					            handlers = handlers[1:] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for h in handlers: | 
				
			||||||
 | 
					            if hasattr(h, "stream"): | 
				
			||||||
 | 
					                self.streams.append(h.stream) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write(self, data): | 
				
			||||||
 | 
					        for stream in self.streams: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                stream.write(data) | 
				
			||||||
 | 
					            except UnicodeError: | 
				
			||||||
 | 
					                stream.write(data.encode("UTF-8")) | 
				
			||||||
 | 
					            stream.flush() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def base_environ(cfg): | 
				
			||||||
 | 
					    return { | 
				
			||||||
 | 
					        "wsgi.errors": WSGIErrorsWrapper(cfg), | 
				
			||||||
 | 
					        "wsgi.version": (1, 0), | 
				
			||||||
 | 
					        "wsgi.multithread": False, | 
				
			||||||
 | 
					        "wsgi.multiprocess": (cfg.workers > 1), | 
				
			||||||
 | 
					        "wsgi.run_once": False, | 
				
			||||||
 | 
					        "wsgi.file_wrapper": FileWrapper, | 
				
			||||||
 | 
					        "SERVER_SOFTWARE": SERVER_SOFTWARE, | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def default_environ(req, sock, cfg): | 
				
			||||||
 | 
					    env = base_environ(cfg) | 
				
			||||||
 | 
					    env.update({ | 
				
			||||||
 | 
					        "wsgi.input": req.body, | 
				
			||||||
 | 
					        "gunicorn.socket": sock, | 
				
			||||||
 | 
					        "REQUEST_METHOD": req.method, | 
				
			||||||
 | 
					        "QUERY_STRING": req.query, | 
				
			||||||
 | 
					        "RAW_URI": req.uri, | 
				
			||||||
 | 
					        "SERVER_PROTOCOL": "HTTP/%s" % ".".join([str(v) for v in req.version]) | 
				
			||||||
 | 
					    }) | 
				
			||||||
 | 
					    return env | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def proxy_environ(req): | 
				
			||||||
 | 
					    info = req.proxy_protocol_info | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not info: | 
				
			||||||
 | 
					        return {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return { | 
				
			||||||
 | 
					        "PROXY_PROTOCOL": info["proxy_protocol"], | 
				
			||||||
 | 
					        "REMOTE_ADDR": info["client_addr"], | 
				
			||||||
 | 
					        "REMOTE_PORT": str(info["client_port"]), | 
				
			||||||
 | 
					        "PROXY_ADDR": info["proxy_addr"], | 
				
			||||||
 | 
					        "PROXY_PORT": str(info["proxy_port"]), | 
				
			||||||
 | 
					    } | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create(req, sock, client, server, cfg): | 
				
			||||||
 | 
					    resp = Response(req, sock, cfg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # set initial environ | 
				
			||||||
 | 
					    environ = default_environ(req, sock, cfg) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # default variables | 
				
			||||||
 | 
					    host = None | 
				
			||||||
 | 
					    script_name = os.environ.get("SCRIPT_NAME", "") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # add the headers to the environ | 
				
			||||||
 | 
					    for hdr_name, hdr_value in req.headers: | 
				
			||||||
 | 
					        if hdr_name == "EXPECT": | 
				
			||||||
 | 
					            # handle expect | 
				
			||||||
 | 
					            if hdr_value.lower() == "100-continue": | 
				
			||||||
 | 
					                sock.send(b"HTTP/1.1 100 Continue\r\n\r\n") | 
				
			||||||
 | 
					        elif hdr_name == 'HOST': | 
				
			||||||
 | 
					            host = hdr_value | 
				
			||||||
 | 
					        elif hdr_name == "SCRIPT_NAME": | 
				
			||||||
 | 
					            script_name = hdr_value | 
				
			||||||
 | 
					        elif hdr_name == "CONTENT-TYPE": | 
				
			||||||
 | 
					            environ['CONTENT_TYPE'] = hdr_value | 
				
			||||||
 | 
					            continue | 
				
			||||||
 | 
					        elif hdr_name == "CONTENT-LENGTH": | 
				
			||||||
 | 
					            environ['CONTENT_LENGTH'] = hdr_value | 
				
			||||||
 | 
					            continue | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        key = 'HTTP_' + hdr_name.replace('-', '_') | 
				
			||||||
 | 
					        if key in environ: | 
				
			||||||
 | 
					            hdr_value = "%s,%s" % (environ[key], hdr_value) | 
				
			||||||
 | 
					        environ[key] = hdr_value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # set the url scheme | 
				
			||||||
 | 
					    environ['wsgi.url_scheme'] = req.scheme | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # set the REMOTE_* keys in environ | 
				
			||||||
 | 
					    # authors should be aware that REMOTE_HOST and REMOTE_ADDR | 
				
			||||||
 | 
					    # may not qualify the remote addr: | 
				
			||||||
 | 
					    # http://www.ietf.org/rfc/rfc3875 | 
				
			||||||
 | 
					    if isinstance(client, string_types): | 
				
			||||||
 | 
					        environ['REMOTE_ADDR'] = client | 
				
			||||||
 | 
					    elif isinstance(client, binary_type): | 
				
			||||||
 | 
					        environ['REMOTE_ADDR'] = str(client) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        environ['REMOTE_ADDR'] = client[0] | 
				
			||||||
 | 
					        environ['REMOTE_PORT'] = str(client[1]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # handle the SERVER_* | 
				
			||||||
 | 
					    # Normally only the application should use the Host header but since the | 
				
			||||||
 | 
					    # WSGI spec doesn't support unix sockets, we are using it to create | 
				
			||||||
 | 
					    # viable SERVER_* if possible. | 
				
			||||||
 | 
					    if isinstance(server, string_types): | 
				
			||||||
 | 
					        server = server.split(":") | 
				
			||||||
 | 
					        if len(server) == 1: | 
				
			||||||
 | 
					            # unix socket | 
				
			||||||
 | 
					            if host: | 
				
			||||||
 | 
					                server = host.split(':') | 
				
			||||||
 | 
					                if len(server) == 1: | 
				
			||||||
 | 
					                    if req.scheme == "http": | 
				
			||||||
 | 
					                        server.append(80) | 
				
			||||||
 | 
					                    elif req.scheme == "https": | 
				
			||||||
 | 
					                        server.append(443) | 
				
			||||||
 | 
					                    else: | 
				
			||||||
 | 
					                        server.append('') | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                # no host header given which means that we are not behind a | 
				
			||||||
 | 
					                # proxy, so append an empty port. | 
				
			||||||
 | 
					                server.append('') | 
				
			||||||
 | 
					    environ['SERVER_NAME'] = server[0] | 
				
			||||||
 | 
					    environ['SERVER_PORT'] = str(server[1]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # set the path and script name | 
				
			||||||
 | 
					    path_info = req.path | 
				
			||||||
 | 
					    if script_name: | 
				
			||||||
 | 
					        path_info = path_info.split(script_name, 1)[1] | 
				
			||||||
 | 
					    environ['PATH_INFO'] = unquote_to_wsgi_str(path_info) | 
				
			||||||
 | 
					    environ['SCRIPT_NAME'] = script_name | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # override the environ with the correct remote and server address if | 
				
			||||||
 | 
					    # we are behind a proxy using the proxy protocol. | 
				
			||||||
 | 
					    environ.update(proxy_environ(req)) | 
				
			||||||
 | 
					    return resp, environ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Response(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, req, sock, cfg): | 
				
			||||||
 | 
					        self.req = req | 
				
			||||||
 | 
					        self.sock = sock | 
				
			||||||
 | 
					        self.version = SERVER_SOFTWARE | 
				
			||||||
 | 
					        self.status = None | 
				
			||||||
 | 
					        self.chunked = False | 
				
			||||||
 | 
					        self.must_close = False | 
				
			||||||
 | 
					        self.headers = [] | 
				
			||||||
 | 
					        self.headers_sent = False | 
				
			||||||
 | 
					        self.response_length = None | 
				
			||||||
 | 
					        self.sent = 0 | 
				
			||||||
 | 
					        self.upgrade = False | 
				
			||||||
 | 
					        self.cfg = cfg | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def force_close(self): | 
				
			||||||
 | 
					        self.must_close = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def should_close(self): | 
				
			||||||
 | 
					        if self.must_close or self.req.should_close(): | 
				
			||||||
 | 
					            return True | 
				
			||||||
 | 
					        if self.response_length is not None or self.chunked: | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        if self.req.method == 'HEAD': | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        if self.status_code < 200 or self.status_code in (204, 304): | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def start_response(self, status, headers, exc_info=None): | 
				
			||||||
 | 
					        if exc_info: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                if self.status and self.headers_sent: | 
				
			||||||
 | 
					                    reraise(exc_info[0], exc_info[1], exc_info[2]) | 
				
			||||||
 | 
					            finally: | 
				
			||||||
 | 
					                exc_info = None | 
				
			||||||
 | 
					        elif self.status is not None: | 
				
			||||||
 | 
					            raise AssertionError("Response headers already set!") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.status = status | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # get the status code from the response here so we can use it to check | 
				
			||||||
 | 
					        # the need for the connection header later without parsing the string | 
				
			||||||
 | 
					        # each time. | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.status_code = int(self.status.split()[0]) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            self.status_code = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.process_headers(headers) | 
				
			||||||
 | 
					        self.chunked = self.is_chunked() | 
				
			||||||
 | 
					        return self.write | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def process_headers(self, headers): | 
				
			||||||
 | 
					        for name, value in headers: | 
				
			||||||
 | 
					            if not isinstance(name, string_types): | 
				
			||||||
 | 
					                raise TypeError('%r is not a string' % name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if HEADER_RE.search(name): | 
				
			||||||
 | 
					                raise InvalidHeaderName('%r' % name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if HEADER_VALUE_RE.search(value): | 
				
			||||||
 | 
					                raise InvalidHeader('%r' % value) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            value = str(value).strip() | 
				
			||||||
 | 
					            lname = name.lower().strip() | 
				
			||||||
 | 
					            if lname == "content-length": | 
				
			||||||
 | 
					                self.response_length = int(value) | 
				
			||||||
 | 
					            elif util.is_hoppish(name): | 
				
			||||||
 | 
					                if lname == "connection": | 
				
			||||||
 | 
					                    # handle websocket | 
				
			||||||
 | 
					                    if value.lower().strip() == "upgrade": | 
				
			||||||
 | 
					                        self.upgrade = True | 
				
			||||||
 | 
					                elif lname == "upgrade": | 
				
			||||||
 | 
					                    if value.lower().strip() == "websocket": | 
				
			||||||
 | 
					                        self.headers.append((name.strip(), value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # ignore hopbyhop headers | 
				
			||||||
 | 
					                continue | 
				
			||||||
 | 
					            self.headers.append((name.strip(), value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_chunked(self): | 
				
			||||||
 | 
					        # Only use chunked responses when the client is | 
				
			||||||
 | 
					        # speaking HTTP/1.1 or newer and there was | 
				
			||||||
 | 
					        # no Content-Length header set. | 
				
			||||||
 | 
					        if self.response_length is not None: | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        elif self.req.version <= (1, 0): | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        elif self.req.method == 'HEAD': | 
				
			||||||
 | 
					            # Responses to a HEAD request MUST NOT contain a response body. | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        elif self.status_code in (204, 304): | 
				
			||||||
 | 
					            # Do not use chunked responses when the response is guaranteed to | 
				
			||||||
 | 
					            # not have a response body. | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def default_headers(self): | 
				
			||||||
 | 
					        # set the connection header | 
				
			||||||
 | 
					        if self.upgrade: | 
				
			||||||
 | 
					            connection = "upgrade" | 
				
			||||||
 | 
					        elif self.should_close(): | 
				
			||||||
 | 
					            connection = "close" | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            connection = "keep-alive" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        headers = [ | 
				
			||||||
 | 
					            "HTTP/%s.%s %s\r\n" % (self.req.version[0], | 
				
			||||||
 | 
					                self.req.version[1], self.status), | 
				
			||||||
 | 
					            "Server: %s\r\n" % self.version, | 
				
			||||||
 | 
					            "Date: %s\r\n" % util.http_date(), | 
				
			||||||
 | 
					            "Connection: %s\r\n" % connection | 
				
			||||||
 | 
					        ] | 
				
			||||||
 | 
					        if self.chunked: | 
				
			||||||
 | 
					            headers.append("Transfer-Encoding: chunked\r\n") | 
				
			||||||
 | 
					        return headers | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def send_headers(self): | 
				
			||||||
 | 
					        if self.headers_sent: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        tosend = self.default_headers() | 
				
			||||||
 | 
					        tosend.extend(["%s: %s\r\n" % (k, v) for k, v in self.headers]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        header_str = "%s\r\n" % "".join(tosend) | 
				
			||||||
 | 
					        util.write(self.sock, util.to_bytestring(header_str, "ascii")) | 
				
			||||||
 | 
					        self.headers_sent = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write(self, arg): | 
				
			||||||
 | 
					        self.send_headers() | 
				
			||||||
 | 
					        if not isinstance(arg, binary_type): | 
				
			||||||
 | 
					            raise TypeError('%r is not a byte' % arg) | 
				
			||||||
 | 
					        arglen = len(arg) | 
				
			||||||
 | 
					        tosend = arglen | 
				
			||||||
 | 
					        if self.response_length is not None: | 
				
			||||||
 | 
					            if self.sent >= self.response_length: | 
				
			||||||
 | 
					                # Never write more than self.response_length bytes | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tosend = min(self.response_length - self.sent, tosend) | 
				
			||||||
 | 
					            if tosend < arglen: | 
				
			||||||
 | 
					                arg = arg[:tosend] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Sending an empty chunk signals the end of the | 
				
			||||||
 | 
					        # response and prematurely closes the response | 
				
			||||||
 | 
					        if self.chunked and tosend == 0: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.sent += tosend | 
				
			||||||
 | 
					        util.write(self.sock, arg, self.chunked) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def can_sendfile(self): | 
				
			||||||
 | 
					        return self.cfg.sendfile is not False and sendfile is not None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sendfile(self, respiter): | 
				
			||||||
 | 
					        if self.cfg.is_ssl or not self.can_sendfile(): | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not util.has_fileno(respiter.filelike): | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fileno = respiter.filelike.fileno() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            offset = os.lseek(fileno, 0, os.SEEK_CUR) | 
				
			||||||
 | 
					            if self.response_length is None: | 
				
			||||||
 | 
					                filesize = os.fstat(fileno).st_size | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # The file may be special and sendfile will fail. | 
				
			||||||
 | 
					                # It may also be zero-length, but that is okay. | 
				
			||||||
 | 
					                if filesize == 0: | 
				
			||||||
 | 
					                    return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                nbytes = filesize - offset | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                nbytes = self.response_length | 
				
			||||||
 | 
					        except (OSError, io.UnsupportedOperation): | 
				
			||||||
 | 
					            return False | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.send_headers() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.is_chunked(): | 
				
			||||||
 | 
					            chunk_size = "%X\r\n" % nbytes | 
				
			||||||
 | 
					            self.sock.sendall(chunk_size.encode('utf-8')) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sockno = self.sock.fileno() | 
				
			||||||
 | 
					        sent = 0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while sent != nbytes: | 
				
			||||||
 | 
					            count = min(nbytes - sent, BLKSIZE) | 
				
			||||||
 | 
					            sent += sendfile(sockno, fileno, offset + sent, count) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.is_chunked(): | 
				
			||||||
 | 
					            self.sock.sendall(b"\r\n") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        os.lseek(fileno, offset, os.SEEK_SET) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def write_file(self, respiter): | 
				
			||||||
 | 
					        if not self.sendfile(respiter): | 
				
			||||||
 | 
					            for item in respiter: | 
				
			||||||
 | 
					                self.write(item) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def close(self): | 
				
			||||||
 | 
					        if not self.headers_sent: | 
				
			||||||
 | 
					            self.send_headers() | 
				
			||||||
 | 
					        if self.chunked: | 
				
			||||||
 | 
					            util.write_chunk(self.sock, b"") | 
				
			||||||
@ -0,0 +1,123 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"Bare-bones implementation of statsD's protocol, client-side" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import socket | 
				
			||||||
 | 
					import logging | 
				
			||||||
 | 
					from re import sub | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn.glogging import Logger | 
				
			||||||
 | 
					from gunicorn import six | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Instrumentation constants | 
				
			||||||
 | 
					METRIC_VAR = "metric" | 
				
			||||||
 | 
					VALUE_VAR = "value" | 
				
			||||||
 | 
					MTYPE_VAR = "mtype" | 
				
			||||||
 | 
					GAUGE_TYPE = "gauge" | 
				
			||||||
 | 
					COUNTER_TYPE = "counter" | 
				
			||||||
 | 
					HISTOGRAM_TYPE = "histogram" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Statsd(Logger): | 
				
			||||||
 | 
					    """statsD-based instrumentation, that passes as a logger | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def __init__(self, cfg): | 
				
			||||||
 | 
					        """host, port: statsD server | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Logger.__init__(self, cfg) | 
				
			||||||
 | 
					        self.prefix = sub(r"^(.+[^.]+)\.*$", "\\g<1>.", cfg.statsd_prefix) | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            host, port = cfg.statsd_host | 
				
			||||||
 | 
					            self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | 
				
			||||||
 | 
					            self.sock.connect((host, int(port))) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            self.sock = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Log errors and warnings | 
				
			||||||
 | 
					    def critical(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        Logger.critical(self, msg, *args, **kwargs) | 
				
			||||||
 | 
					        self.increment("gunicorn.log.critical", 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def error(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        Logger.error(self, msg, *args, **kwargs) | 
				
			||||||
 | 
					        self.increment("gunicorn.log.error", 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def warning(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        Logger.warning(self, msg, *args, **kwargs) | 
				
			||||||
 | 
					        self.increment("gunicorn.log.warning", 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def exception(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        Logger.exception(self, msg, *args, **kwargs) | 
				
			||||||
 | 
					        self.increment("gunicorn.log.exception", 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Special treatement for info, the most common log level | 
				
			||||||
 | 
					    def info(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        self.log(logging.INFO, msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # skip the run-of-the-mill logs | 
				
			||||||
 | 
					    def debug(self, msg, *args, **kwargs): | 
				
			||||||
 | 
					        self.log(logging.DEBUG, msg, *args, **kwargs) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def log(self, lvl, msg, *args, **kwargs): | 
				
			||||||
 | 
					        """Log a given statistic if metric, value and type are present | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            extra = kwargs.get("extra", None) | 
				
			||||||
 | 
					            if extra is not None: | 
				
			||||||
 | 
					                metric = extra.get(METRIC_VAR, None) | 
				
			||||||
 | 
					                value = extra.get(VALUE_VAR, None) | 
				
			||||||
 | 
					                typ = extra.get(MTYPE_VAR, None) | 
				
			||||||
 | 
					                if metric and value and typ: | 
				
			||||||
 | 
					                    if typ == GAUGE_TYPE: | 
				
			||||||
 | 
					                        self.gauge(metric, value) | 
				
			||||||
 | 
					                    elif typ == COUNTER_TYPE: | 
				
			||||||
 | 
					                        self.increment(metric, value) | 
				
			||||||
 | 
					                    elif typ == HISTOGRAM_TYPE: | 
				
			||||||
 | 
					                        self.histogram(metric, value) | 
				
			||||||
 | 
					                    else: | 
				
			||||||
 | 
					                        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # Log to parent logger only if there is something to say | 
				
			||||||
 | 
					            if msg: | 
				
			||||||
 | 
					                Logger.log(self, lvl, msg, *args, **kwargs) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            Logger.warning(self, "Failed to log to statsd", exc_info=True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # access logging | 
				
			||||||
 | 
					    def access(self, resp, req, environ, request_time): | 
				
			||||||
 | 
					        """Measure request duration | 
				
			||||||
 | 
					        request_time is a datetime.timedelta | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Logger.access(self, resp, req, environ, request_time) | 
				
			||||||
 | 
					        duration_in_ms = request_time.seconds * 1000 + float(request_time.microseconds) / 10 ** 3 | 
				
			||||||
 | 
					        status = resp.status | 
				
			||||||
 | 
					        if isinstance(status, str): | 
				
			||||||
 | 
					            status = int(status.split(None, 1)[0]) | 
				
			||||||
 | 
					        self.histogram("gunicorn.request.duration", duration_in_ms) | 
				
			||||||
 | 
					        self.increment("gunicorn.requests", 1) | 
				
			||||||
 | 
					        self.increment("gunicorn.request.status.%d" % status, 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # statsD methods | 
				
			||||||
 | 
					    # you can use those directly if you want | 
				
			||||||
 | 
					    def gauge(self, name, value): | 
				
			||||||
 | 
					        self._sock_send("{0}{1}:{2}|g".format(self.prefix, name, value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def increment(self, name, value, sampling_rate=1.0): | 
				
			||||||
 | 
					        self._sock_send("{0}{1}:{2}|c|@{3}".format(self.prefix, name, value, sampling_rate)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decrement(self, name, value, sampling_rate=1.0): | 
				
			||||||
 | 
					        self._sock_send("{0}{1}:-{2}|c|@{3}".format(self.prefix, name, value, sampling_rate)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def histogram(self, name, value): | 
				
			||||||
 | 
					        self._sock_send("{0}{1}:{2}|ms".format(self.prefix, name, value)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _sock_send(self, msg): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            if isinstance(msg, six.text_type): | 
				
			||||||
 | 
					                msg = msg.encode("ascii") | 
				
			||||||
 | 
					            if self.sock: | 
				
			||||||
 | 
					                self.sock.send(msg) | 
				
			||||||
 | 
					        except Exception: | 
				
			||||||
 | 
					            Logger.warning(self, "Error sending message to statsd", exc_info=True) | 
				
			||||||
@ -0,0 +1,86 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import errno | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import tempfile | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Pidfile(object): | 
				
			||||||
 | 
					    """\ | 
				
			||||||
 | 
					    Manage a PID file. If a specific name is provided | 
				
			||||||
 | 
					    it and '"%s.oldpid" % name' will be used. Otherwise | 
				
			||||||
 | 
					    we create a temp file using os.mkstemp. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, fname): | 
				
			||||||
 | 
					        self.fname = fname | 
				
			||||||
 | 
					        self.pid = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create(self, pid): | 
				
			||||||
 | 
					        oldpid = self.validate() | 
				
			||||||
 | 
					        if oldpid: | 
				
			||||||
 | 
					            if oldpid == os.getpid(): | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					            msg = "Already running on PID %s (or pid file '%s' is stale)" | 
				
			||||||
 | 
					            raise RuntimeError(msg % (oldpid, self.fname)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.pid = pid | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Write pidfile | 
				
			||||||
 | 
					        fdir = os.path.dirname(self.fname) | 
				
			||||||
 | 
					        if fdir and not os.path.isdir(fdir): | 
				
			||||||
 | 
					            raise RuntimeError("%s doesn't exist. Can't create pidfile." % fdir) | 
				
			||||||
 | 
					        fd, fname = tempfile.mkstemp(dir=fdir) | 
				
			||||||
 | 
					        os.write(fd, ("%s\n" % self.pid).encode('utf-8')) | 
				
			||||||
 | 
					        if self.fname: | 
				
			||||||
 | 
					            os.rename(fname, self.fname) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.fname = fname | 
				
			||||||
 | 
					        os.close(fd) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # set permissions to -rw-r--r-- | 
				
			||||||
 | 
					        os.chmod(self.fname, 420) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def rename(self, path): | 
				
			||||||
 | 
					        self.unlink() | 
				
			||||||
 | 
					        self.fname = path | 
				
			||||||
 | 
					        self.create(self.pid) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def unlink(self): | 
				
			||||||
 | 
					        """ delete pidfile""" | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            with open(self.fname, "r") as f: | 
				
			||||||
 | 
					                pid1 = int(f.read() or 0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if pid1 == self.pid: | 
				
			||||||
 | 
					                os.unlink(self.fname) | 
				
			||||||
 | 
					        except: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def validate(self): | 
				
			||||||
 | 
					        """ Validate pidfile and make it stale if needed""" | 
				
			||||||
 | 
					        if not self.fname: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            with open(self.fname, "r") as f: | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    wpid = int(f.read()) | 
				
			||||||
 | 
					                except ValueError: | 
				
			||||||
 | 
					                    return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    os.kill(wpid, 0) | 
				
			||||||
 | 
					                    return wpid | 
				
			||||||
 | 
					                except OSError as e: | 
				
			||||||
 | 
					                    if e.args[0] == errno.EPERM: | 
				
			||||||
 | 
					                        return wpid | 
				
			||||||
 | 
					                    if e.args[0] == errno.ESRCH: | 
				
			||||||
 | 
					                        return | 
				
			||||||
 | 
					                    raise | 
				
			||||||
 | 
					        except IOError as e: | 
				
			||||||
 | 
					            if e.args[0] == errno.ENOENT: | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					            raise | 
				
			||||||
@ -0,0 +1,130 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import os.path | 
				
			||||||
 | 
					import re | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					import threading | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Reloader(threading.Thread): | 
				
			||||||
 | 
					    def __init__(self, extra_files=None, interval=1, callback=None): | 
				
			||||||
 | 
					        super(Reloader, self).__init__() | 
				
			||||||
 | 
					        self.setDaemon(True) | 
				
			||||||
 | 
					        self._extra_files = set(extra_files or ()) | 
				
			||||||
 | 
					        self._extra_files_lock = threading.RLock() | 
				
			||||||
 | 
					        self._interval = interval | 
				
			||||||
 | 
					        self._callback = callback | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_extra_file(self, filename): | 
				
			||||||
 | 
					        with self._extra_files_lock: | 
				
			||||||
 | 
					            self._extra_files.add(filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_files(self): | 
				
			||||||
 | 
					        fnames = [ | 
				
			||||||
 | 
					            re.sub('py[co]$', 'py', module.__file__) | 
				
			||||||
 | 
					            for module in list(sys.modules.values()) | 
				
			||||||
 | 
					            if getattr(module, '__file__', None) | 
				
			||||||
 | 
					        ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with self._extra_files_lock: | 
				
			||||||
 | 
					            fnames.extend(self._extra_files) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return fnames | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self): | 
				
			||||||
 | 
					        mtimes = {} | 
				
			||||||
 | 
					        while True: | 
				
			||||||
 | 
					            for filename in self.get_files(): | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    mtime = os.stat(filename).st_mtime | 
				
			||||||
 | 
					                except OSError: | 
				
			||||||
 | 
					                    continue | 
				
			||||||
 | 
					                old_time = mtimes.get(filename) | 
				
			||||||
 | 
					                if old_time is None: | 
				
			||||||
 | 
					                    mtimes[filename] = mtime | 
				
			||||||
 | 
					                    continue | 
				
			||||||
 | 
					                elif mtime > old_time: | 
				
			||||||
 | 
					                    if self._callback: | 
				
			||||||
 | 
					                        self._callback(filename) | 
				
			||||||
 | 
					            time.sleep(self._interval) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					has_inotify = False | 
				
			||||||
 | 
					if sys.platform.startswith('linux'): | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        from inotify.adapters import Inotify | 
				
			||||||
 | 
					        import inotify.constants | 
				
			||||||
 | 
					        has_inotify = True | 
				
			||||||
 | 
					    except ImportError: | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if has_inotify: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class InotifyReloader(threading.Thread): | 
				
			||||||
 | 
					        event_mask = (inotify.constants.IN_CREATE | inotify.constants.IN_DELETE | 
				
			||||||
 | 
					                      | inotify.constants.IN_DELETE_SELF | inotify.constants.IN_MODIFY | 
				
			||||||
 | 
					                      | inotify.constants.IN_MOVE_SELF | inotify.constants.IN_MOVED_FROM | 
				
			||||||
 | 
					                      | inotify.constants.IN_MOVED_TO) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def __init__(self, extra_files=None, callback=None): | 
				
			||||||
 | 
					            super(InotifyReloader, self).__init__() | 
				
			||||||
 | 
					            self.setDaemon(True) | 
				
			||||||
 | 
					            self._callback = callback | 
				
			||||||
 | 
					            self._dirs = set() | 
				
			||||||
 | 
					            self._watcher = Inotify() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for extra_file in extra_files: | 
				
			||||||
 | 
					                self.add_extra_file(extra_file) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def add_extra_file(self, filename): | 
				
			||||||
 | 
					            dirname = os.path.dirname(filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if dirname in self._dirs: | 
				
			||||||
 | 
					                return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self._watcher.add_watch(dirname, mask=self.event_mask) | 
				
			||||||
 | 
					            self._dirs.add(dirname) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def get_dirs(self): | 
				
			||||||
 | 
					            fnames = [ | 
				
			||||||
 | 
					                os.path.dirname(re.sub('py[co]$', 'py', module.__file__)) | 
				
			||||||
 | 
					                for module in list(sys.modules.values()) | 
				
			||||||
 | 
					                if hasattr(module, '__file__') | 
				
			||||||
 | 
					            ] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return set(fnames) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def run(self): | 
				
			||||||
 | 
					            self._dirs = self.get_dirs() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for dirname in self._dirs: | 
				
			||||||
 | 
					                self._watcher.add_watch(dirname, mask=self.event_mask) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for event in self._watcher.event_gen(): | 
				
			||||||
 | 
					                if event is None: | 
				
			||||||
 | 
					                    continue | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                filename = event[3] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self._callback(filename) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class InotifyReloader(object): | 
				
			||||||
 | 
					        def __init__(self, callback=None): | 
				
			||||||
 | 
					            raise ImportError('You must have the inotify module installed to ' | 
				
			||||||
 | 
					                              'use the inotify reloader') | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					preferred_reloader = InotifyReloader if has_inotify else Reloader | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reloader_engines = { | 
				
			||||||
 | 
					    'auto': preferred_reloader, | 
				
			||||||
 | 
					    'poll': Reloader, | 
				
			||||||
 | 
					    'inotify': InotifyReloader, | 
				
			||||||
 | 
					} | 
				
			||||||
@ -0,0 +1,592 @@ | 
				
			|||||||
 | 
					"""Selectors module. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This module allows high-level and efficient I/O multiplexing, built upon the | 
				
			||||||
 | 
					`select` module primitives. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The following code adapted from trollius.selectors. | 
				
			||||||
 | 
					""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from abc import ABCMeta, abstractmethod | 
				
			||||||
 | 
					from collections import namedtuple, Mapping | 
				
			||||||
 | 
					import math | 
				
			||||||
 | 
					import select | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn._compat import wrap_error, InterruptedError | 
				
			||||||
 | 
					from gunicorn import six | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# generic events, that must be mapped to implementation-specific ones | 
				
			||||||
 | 
					EVENT_READ = (1 << 0) | 
				
			||||||
 | 
					EVENT_WRITE = (1 << 1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _fileobj_to_fd(fileobj): | 
				
			||||||
 | 
					    """Return a file descriptor from a file object. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Parameters: | 
				
			||||||
 | 
					    fileobj -- file object or file descriptor | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns: | 
				
			||||||
 | 
					    corresponding file descriptor | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Raises: | 
				
			||||||
 | 
					    ValueError if the object is invalid | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    if isinstance(fileobj, six.integer_types): | 
				
			||||||
 | 
					        fd = fileobj | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            fd = int(fileobj.fileno()) | 
				
			||||||
 | 
					        except (AttributeError, TypeError, ValueError): | 
				
			||||||
 | 
					            raise ValueError("Invalid file object: " | 
				
			||||||
 | 
					                             "{0!r}".format(fileobj)) | 
				
			||||||
 | 
					    if fd < 0: | 
				
			||||||
 | 
					        raise ValueError("Invalid file descriptor: {0}".format(fd)) | 
				
			||||||
 | 
					    return fd | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data']) | 
				
			||||||
 | 
					"""Object used to associate a file object to its backing file descriptor, | 
				
			||||||
 | 
					selected event mask and attached data.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _SelectorMapping(Mapping): | 
				
			||||||
 | 
					    """Mapping of file objects to selector keys.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, selector): | 
				
			||||||
 | 
					        self._selector = selector | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __len__(self): | 
				
			||||||
 | 
					        return len(self._selector._fd_to_key) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getitem__(self, fileobj): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            fd = self._selector._fileobj_lookup(fileobj) | 
				
			||||||
 | 
					            return self._selector._fd_to_key[fd] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise KeyError("{0!r} is not registered".format(fileobj)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self): | 
				
			||||||
 | 
					        return iter(self._selector._fd_to_key) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseSelector(six.with_metaclass(ABCMeta)): | 
				
			||||||
 | 
					    """Selector abstract base class. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A selector supports registering file objects to be monitored for specific | 
				
			||||||
 | 
					    I/O events. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A file object is a file descriptor or any object with a `fileno()` method. | 
				
			||||||
 | 
					    An arbitrary object can be attached to the file object, which can be used | 
				
			||||||
 | 
					    for example to store context information, a callback, etc. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    A selector can use various implementations (select(), poll(), epoll()...) | 
				
			||||||
 | 
					    depending on the platform. The default `Selector` class uses the most | 
				
			||||||
 | 
					    efficient implementation on the current platform. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abstractmethod | 
				
			||||||
 | 
					    def register(self, fileobj, events, data=None): | 
				
			||||||
 | 
					        """Register a file object. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters: | 
				
			||||||
 | 
					        fileobj -- file object or file descriptor | 
				
			||||||
 | 
					        events  -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE) | 
				
			||||||
 | 
					        data    -- attached data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns: | 
				
			||||||
 | 
					        SelectorKey instance | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises: | 
				
			||||||
 | 
					        ValueError if events is invalid | 
				
			||||||
 | 
					        KeyError if fileobj is already registered | 
				
			||||||
 | 
					        OSError if fileobj is closed or otherwise is unacceptable to | 
				
			||||||
 | 
					                the underlying system call (if a system call is made) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Note: | 
				
			||||||
 | 
					        OSError may or may not be raised | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abstractmethod | 
				
			||||||
 | 
					    def unregister(self, fileobj): | 
				
			||||||
 | 
					        """Unregister a file object. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters: | 
				
			||||||
 | 
					        fileobj -- file object or file descriptor | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns: | 
				
			||||||
 | 
					        SelectorKey instance | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises: | 
				
			||||||
 | 
					        KeyError if fileobj is not registered | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Note: | 
				
			||||||
 | 
					        If fileobj is registered but has since been closed this does | 
				
			||||||
 | 
					        *not* raise OSError (even if the wrapped syscall does) | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def modify(self, fileobj, events, data=None): | 
				
			||||||
 | 
					        """Change a registered file object monitored events or attached data. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters: | 
				
			||||||
 | 
					        fileobj -- file object or file descriptor | 
				
			||||||
 | 
					        events  -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE) | 
				
			||||||
 | 
					        data    -- attached data | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns: | 
				
			||||||
 | 
					        SelectorKey instance | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises: | 
				
			||||||
 | 
					        Anything that unregister() or register() raises | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        self.unregister(fileobj) | 
				
			||||||
 | 
					        return self.register(fileobj, events, data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abstractmethod | 
				
			||||||
 | 
					    def select(self, timeout=None): | 
				
			||||||
 | 
					        """Perform the actual selection, until some monitored file objects are | 
				
			||||||
 | 
					        ready or a timeout expires. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters: | 
				
			||||||
 | 
					        timeout -- if timeout > 0, this specifies the maximum wait time, in | 
				
			||||||
 | 
					                   seconds | 
				
			||||||
 | 
					                   if timeout <= 0, the select() call won't block, and will | 
				
			||||||
 | 
					                   report the currently ready file objects | 
				
			||||||
 | 
					                   if timeout is None, select() will block until a monitored | 
				
			||||||
 | 
					                   file object becomes ready | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns: | 
				
			||||||
 | 
					        list of (key, events) for ready file objects | 
				
			||||||
 | 
					        `events` is a bitwise mask of EVENT_READ|EVENT_WRITE | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def close(self): | 
				
			||||||
 | 
					        """Close the selector. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This must be called to make sure that any underlying resource is freed. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        pass | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_key(self, fileobj): | 
				
			||||||
 | 
					        """Return the key associated to a registered file object. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns: | 
				
			||||||
 | 
					        SelectorKey for this file object | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        mapping = self.get_map() | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return mapping[fileobj] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise KeyError("{0!r} is not registered".format(fileobj)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abstractmethod | 
				
			||||||
 | 
					    def get_map(self): | 
				
			||||||
 | 
					        """Return a mapping of file objects to selector keys.""" | 
				
			||||||
 | 
					        raise NotImplementedError | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __enter__(self): | 
				
			||||||
 | 
					        return self | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, *args): | 
				
			||||||
 | 
					        self.close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _BaseSelectorImpl(BaseSelector): | 
				
			||||||
 | 
					    """Base selector implementation.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self): | 
				
			||||||
 | 
					        # this maps file descriptors to keys | 
				
			||||||
 | 
					        self._fd_to_key = {} | 
				
			||||||
 | 
					        # read-only mapping returned by get_map() | 
				
			||||||
 | 
					        self._map = _SelectorMapping(self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _fileobj_lookup(self, fileobj): | 
				
			||||||
 | 
					        """Return a file descriptor from a file object. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This wraps _fileobj_to_fd() to do an exhaustive search in case | 
				
			||||||
 | 
					        the object is invalid but we still have it in our map.  This | 
				
			||||||
 | 
					        is used by unregister() so we can unregister an object that | 
				
			||||||
 | 
					        was previously registered even if it is closed.  It is also | 
				
			||||||
 | 
					        used by _SelectorMapping. | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return _fileobj_to_fd(fileobj) | 
				
			||||||
 | 
					        except ValueError: | 
				
			||||||
 | 
					            # Do an exhaustive search. | 
				
			||||||
 | 
					            for key in self._fd_to_key.values(): | 
				
			||||||
 | 
					                if key.fileobj is fileobj: | 
				
			||||||
 | 
					                    return key.fd | 
				
			||||||
 | 
					            # Raise ValueError after all. | 
				
			||||||
 | 
					            raise | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def register(self, fileobj, events, data=None): | 
				
			||||||
 | 
					        if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)): | 
				
			||||||
 | 
					            raise ValueError("Invalid events: {0!r}".format(events)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if key.fd in self._fd_to_key: | 
				
			||||||
 | 
					            raise KeyError("{0!r} (FD {1}) is already registered" | 
				
			||||||
 | 
					                           .format(fileobj, key.fd)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._fd_to_key[key.fd] = key | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def unregister(self, fileobj): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            key = self._fd_to_key.pop(self._fileobj_lookup(fileobj)) | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise KeyError("{0!r} is not registered".format(fileobj)) | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def modify(self, fileobj, events, data=None): | 
				
			||||||
 | 
					        # TODO: Subclasses can probably optimize this even further. | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            key = self._fd_to_key[self._fileobj_lookup(fileobj)] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise KeyError("{0!r} is not registered".format(fileobj)) | 
				
			||||||
 | 
					        if events != key.events: | 
				
			||||||
 | 
					            self.unregister(fileobj) | 
				
			||||||
 | 
					            key = self.register(fileobj, events, data) | 
				
			||||||
 | 
					        elif data != key.data: | 
				
			||||||
 | 
					            # Use a shortcut to update the data. | 
				
			||||||
 | 
					            key = key._replace(data=data) | 
				
			||||||
 | 
					            self._fd_to_key[key.fd] = key | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def close(self): | 
				
			||||||
 | 
					        self._fd_to_key.clear() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_map(self): | 
				
			||||||
 | 
					        return self._map | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _key_from_fd(self, fd): | 
				
			||||||
 | 
					        """Return the key associated to a given file descriptor. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Parameters: | 
				
			||||||
 | 
					        fd -- file descriptor | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns: | 
				
			||||||
 | 
					        corresponding key, or None if not found | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return self._fd_to_key[fd] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            return None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SelectSelector(_BaseSelectorImpl): | 
				
			||||||
 | 
					    """Select-based selector.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self): | 
				
			||||||
 | 
					        super(SelectSelector, self).__init__() | 
				
			||||||
 | 
					        self._readers = set() | 
				
			||||||
 | 
					        self._writers = set() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def register(self, fileobj, events, data=None): | 
				
			||||||
 | 
					        key = super(SelectSelector, self).register(fileobj, events, data) | 
				
			||||||
 | 
					        if events & EVENT_READ: | 
				
			||||||
 | 
					            self._readers.add(key.fd) | 
				
			||||||
 | 
					        if events & EVENT_WRITE: | 
				
			||||||
 | 
					            self._writers.add(key.fd) | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def unregister(self, fileobj): | 
				
			||||||
 | 
					        key = super(SelectSelector, self).unregister(fileobj) | 
				
			||||||
 | 
					        self._readers.discard(key.fd) | 
				
			||||||
 | 
					        self._writers.discard(key.fd) | 
				
			||||||
 | 
					        return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sys.platform == 'win32': | 
				
			||||||
 | 
					        def _select(self, r, w, _, timeout=None): | 
				
			||||||
 | 
					            r, w, x = select.select(r, w, w, timeout) | 
				
			||||||
 | 
					            return r, w + x, [] | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        _select = select.select | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def select(self, timeout=None): | 
				
			||||||
 | 
					        timeout = None if timeout is None else max(timeout, 0) | 
				
			||||||
 | 
					        ready = [] | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            r, w, _ = wrap_error(self._select, | 
				
			||||||
 | 
					                                 self._readers, self._writers, [], timeout) | 
				
			||||||
 | 
					        except InterruptedError: | 
				
			||||||
 | 
					            return ready | 
				
			||||||
 | 
					        r = set(r) | 
				
			||||||
 | 
					        w = set(w) | 
				
			||||||
 | 
					        for fd in r | w: | 
				
			||||||
 | 
					            events = 0 | 
				
			||||||
 | 
					            if fd in r: | 
				
			||||||
 | 
					                events |= EVENT_READ | 
				
			||||||
 | 
					            if fd in w: | 
				
			||||||
 | 
					                events |= EVENT_WRITE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            key = self._key_from_fd(fd) | 
				
			||||||
 | 
					            if key: | 
				
			||||||
 | 
					                ready.append((key, events & key.events)) | 
				
			||||||
 | 
					        return ready | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if hasattr(select, 'poll'): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class PollSelector(_BaseSelectorImpl): | 
				
			||||||
 | 
					        """Poll-based selector.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def __init__(self): | 
				
			||||||
 | 
					            super(PollSelector, self).__init__() | 
				
			||||||
 | 
					            self._poll = select.poll() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def register(self, fileobj, events, data=None): | 
				
			||||||
 | 
					            key = super(PollSelector, self).register(fileobj, events, data) | 
				
			||||||
 | 
					            poll_events = 0 | 
				
			||||||
 | 
					            if events & EVENT_READ: | 
				
			||||||
 | 
					                poll_events |= select.POLLIN | 
				
			||||||
 | 
					            if events & EVENT_WRITE: | 
				
			||||||
 | 
					                poll_events |= select.POLLOUT | 
				
			||||||
 | 
					            self._poll.register(key.fd, poll_events) | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def unregister(self, fileobj): | 
				
			||||||
 | 
					            key = super(PollSelector, self).unregister(fileobj) | 
				
			||||||
 | 
					            self._poll.unregister(key.fd) | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def select(self, timeout=None): | 
				
			||||||
 | 
					            if timeout is None: | 
				
			||||||
 | 
					                timeout = None | 
				
			||||||
 | 
					            elif timeout <= 0: | 
				
			||||||
 | 
					                timeout = 0 | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                # poll() has a resolution of 1 millisecond, round away from | 
				
			||||||
 | 
					                # zero to wait *at least* timeout seconds. | 
				
			||||||
 | 
					                timeout = int(math.ceil(timeout * 1e3)) | 
				
			||||||
 | 
					            ready = [] | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                fd_event_list = wrap_error(self._poll.poll, timeout) | 
				
			||||||
 | 
					            except InterruptedError: | 
				
			||||||
 | 
					                return ready | 
				
			||||||
 | 
					            for fd, event in fd_event_list: | 
				
			||||||
 | 
					                events = 0 | 
				
			||||||
 | 
					                if event & ~select.POLLIN: | 
				
			||||||
 | 
					                    events |= EVENT_WRITE | 
				
			||||||
 | 
					                if event & ~select.POLLOUT: | 
				
			||||||
 | 
					                    events |= EVENT_READ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                key = self._key_from_fd(fd) | 
				
			||||||
 | 
					                if key: | 
				
			||||||
 | 
					                    ready.append((key, events & key.events)) | 
				
			||||||
 | 
					            return ready | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if hasattr(select, 'epoll'): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class EpollSelector(_BaseSelectorImpl): | 
				
			||||||
 | 
					        """Epoll-based selector.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def __init__(self): | 
				
			||||||
 | 
					            super(EpollSelector, self).__init__() | 
				
			||||||
 | 
					            self._epoll = select.epoll() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def fileno(self): | 
				
			||||||
 | 
					            return self._epoll.fileno() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def register(self, fileobj, events, data=None): | 
				
			||||||
 | 
					            key = super(EpollSelector, self).register(fileobj, events, data) | 
				
			||||||
 | 
					            epoll_events = 0 | 
				
			||||||
 | 
					            if events & EVENT_READ: | 
				
			||||||
 | 
					                epoll_events |= select.EPOLLIN | 
				
			||||||
 | 
					            if events & EVENT_WRITE: | 
				
			||||||
 | 
					                epoll_events |= select.EPOLLOUT | 
				
			||||||
 | 
					            self._epoll.register(key.fd, epoll_events) | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def unregister(self, fileobj): | 
				
			||||||
 | 
					            key = super(EpollSelector, self).unregister(fileobj) | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                self._epoll.unregister(key.fd) | 
				
			||||||
 | 
					            except OSError: | 
				
			||||||
 | 
					                # This can happen if the FD was closed since it | 
				
			||||||
 | 
					                # was registered. | 
				
			||||||
 | 
					                pass | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def select(self, timeout=None): | 
				
			||||||
 | 
					            if timeout is None: | 
				
			||||||
 | 
					                timeout = -1 | 
				
			||||||
 | 
					            elif timeout <= 0: | 
				
			||||||
 | 
					                timeout = 0 | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                # epoll_wait() has a resolution of 1 millisecond, round away | 
				
			||||||
 | 
					                # from zero to wait *at least* timeout seconds. | 
				
			||||||
 | 
					                timeout = math.ceil(timeout * 1e3) * 1e-3 | 
				
			||||||
 | 
					            max_ev = len(self._fd_to_key) | 
				
			||||||
 | 
					            ready = [] | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                fd_event_list = wrap_error(self._epoll.poll, timeout, max_ev) | 
				
			||||||
 | 
					            except InterruptedError: | 
				
			||||||
 | 
					                return ready | 
				
			||||||
 | 
					            for fd, event in fd_event_list: | 
				
			||||||
 | 
					                events = 0 | 
				
			||||||
 | 
					                if event & ~select.EPOLLIN: | 
				
			||||||
 | 
					                    events |= EVENT_WRITE | 
				
			||||||
 | 
					                if event & ~select.EPOLLOUT: | 
				
			||||||
 | 
					                    events |= EVENT_READ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                key = self._key_from_fd(fd) | 
				
			||||||
 | 
					                if key: | 
				
			||||||
 | 
					                    ready.append((key, events & key.events)) | 
				
			||||||
 | 
					            return ready | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def close(self): | 
				
			||||||
 | 
					            self._epoll.close() | 
				
			||||||
 | 
					            super(EpollSelector, self).close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if hasattr(select, 'devpoll'): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class DevpollSelector(_BaseSelectorImpl): | 
				
			||||||
 | 
					        """Solaris /dev/poll selector.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def __init__(self): | 
				
			||||||
 | 
					            super(DevpollSelector, self).__init__() | 
				
			||||||
 | 
					            self._devpoll = select.devpoll() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def fileno(self): | 
				
			||||||
 | 
					            return self._devpoll.fileno() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def register(self, fileobj, events, data=None): | 
				
			||||||
 | 
					            key = super(DevpollSelector, self).register(fileobj, events, data) | 
				
			||||||
 | 
					            poll_events = 0 | 
				
			||||||
 | 
					            if events & EVENT_READ: | 
				
			||||||
 | 
					                poll_events |= select.POLLIN | 
				
			||||||
 | 
					            if events & EVENT_WRITE: | 
				
			||||||
 | 
					                poll_events |= select.POLLOUT | 
				
			||||||
 | 
					            self._devpoll.register(key.fd, poll_events) | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def unregister(self, fileobj): | 
				
			||||||
 | 
					            key = super(DevpollSelector, self).unregister(fileobj) | 
				
			||||||
 | 
					            self._devpoll.unregister(key.fd) | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def select(self, timeout=None): | 
				
			||||||
 | 
					            if timeout is None: | 
				
			||||||
 | 
					                timeout = None | 
				
			||||||
 | 
					            elif timeout <= 0: | 
				
			||||||
 | 
					                timeout = 0 | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                # devpoll() has a resolution of 1 millisecond, round away from | 
				
			||||||
 | 
					                # zero to wait *at least* timeout seconds. | 
				
			||||||
 | 
					                timeout = math.ceil(timeout * 1e3) | 
				
			||||||
 | 
					            ready = [] | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                fd_event_list = self._devpoll.poll(timeout) | 
				
			||||||
 | 
					            except InterruptedError: | 
				
			||||||
 | 
					                return ready | 
				
			||||||
 | 
					            for fd, event in fd_event_list: | 
				
			||||||
 | 
					                events = 0 | 
				
			||||||
 | 
					                if event & ~select.POLLIN: | 
				
			||||||
 | 
					                    events |= EVENT_WRITE | 
				
			||||||
 | 
					                if event & ~select.POLLOUT: | 
				
			||||||
 | 
					                    events |= EVENT_READ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                key = self._key_from_fd(fd) | 
				
			||||||
 | 
					                if key: | 
				
			||||||
 | 
					                    ready.append((key, events & key.events)) | 
				
			||||||
 | 
					            return ready | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def close(self): | 
				
			||||||
 | 
					            self._devpoll.close() | 
				
			||||||
 | 
					            super(DevpollSelector, self).close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if hasattr(select, 'kqueue'): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class KqueueSelector(_BaseSelectorImpl): | 
				
			||||||
 | 
					        """Kqueue-based selector.""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def __init__(self): | 
				
			||||||
 | 
					            super(KqueueSelector, self).__init__() | 
				
			||||||
 | 
					            self._kqueue = select.kqueue() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def fileno(self): | 
				
			||||||
 | 
					            return self._kqueue.fileno() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def register(self, fileobj, events, data=None): | 
				
			||||||
 | 
					            key = super(KqueueSelector, self).register(fileobj, events, data) | 
				
			||||||
 | 
					            if events & EVENT_READ: | 
				
			||||||
 | 
					                kev = select.kevent(key.fd, select.KQ_FILTER_READ, | 
				
			||||||
 | 
					                                    select.KQ_EV_ADD) | 
				
			||||||
 | 
					                self._kqueue.control([kev], 0, 0) | 
				
			||||||
 | 
					            if events & EVENT_WRITE: | 
				
			||||||
 | 
					                kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, | 
				
			||||||
 | 
					                                    select.KQ_EV_ADD) | 
				
			||||||
 | 
					                self._kqueue.control([kev], 0, 0) | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def unregister(self, fileobj): | 
				
			||||||
 | 
					            key = super(KqueueSelector, self).unregister(fileobj) | 
				
			||||||
 | 
					            if key.events & EVENT_READ: | 
				
			||||||
 | 
					                kev = select.kevent(key.fd, select.KQ_FILTER_READ, | 
				
			||||||
 | 
					                                    select.KQ_EV_DELETE) | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    self._kqueue.control([kev], 0, 0) | 
				
			||||||
 | 
					                except OSError: | 
				
			||||||
 | 
					                    # This can happen if the FD was closed since it | 
				
			||||||
 | 
					                    # was registered. | 
				
			||||||
 | 
					                    pass | 
				
			||||||
 | 
					            if key.events & EVENT_WRITE: | 
				
			||||||
 | 
					                kev = select.kevent(key.fd, select.KQ_FILTER_WRITE, | 
				
			||||||
 | 
					                                    select.KQ_EV_DELETE) | 
				
			||||||
 | 
					                try: | 
				
			||||||
 | 
					                    self._kqueue.control([kev], 0, 0) | 
				
			||||||
 | 
					                except OSError: | 
				
			||||||
 | 
					                    # See comment above. | 
				
			||||||
 | 
					                    pass | 
				
			||||||
 | 
					            return key | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def select(self, timeout=None): | 
				
			||||||
 | 
					            timeout = None if timeout is None else max(timeout, 0) | 
				
			||||||
 | 
					            max_ev = len(self._fd_to_key) | 
				
			||||||
 | 
					            ready = [] | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                kev_list = wrap_error(self._kqueue.control, | 
				
			||||||
 | 
					                                      None, max_ev, timeout) | 
				
			||||||
 | 
					            except InterruptedError: | 
				
			||||||
 | 
					                return ready | 
				
			||||||
 | 
					            for kev in kev_list: | 
				
			||||||
 | 
					                fd = kev.ident | 
				
			||||||
 | 
					                flag = kev.filter | 
				
			||||||
 | 
					                events = 0 | 
				
			||||||
 | 
					                if flag == select.KQ_FILTER_READ: | 
				
			||||||
 | 
					                    events |= EVENT_READ | 
				
			||||||
 | 
					                if flag == select.KQ_FILTER_WRITE: | 
				
			||||||
 | 
					                    events |= EVENT_WRITE | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                key = self._key_from_fd(fd) | 
				
			||||||
 | 
					                if key: | 
				
			||||||
 | 
					                    ready.append((key, events & key.events)) | 
				
			||||||
 | 
					            return ready | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def close(self): | 
				
			||||||
 | 
					            self._kqueue.close() | 
				
			||||||
 | 
					            super(KqueueSelector, self).close() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Choose the best implementation: roughly, epoll|kqueue|devpoll > poll > select. | 
				
			||||||
 | 
					# select() also can't accept a FD > FD_SETSIZE (usually around 1024) | 
				
			||||||
 | 
					if 'KqueueSelector' in globals(): | 
				
			||||||
 | 
					    DefaultSelector = KqueueSelector | 
				
			||||||
 | 
					elif 'EpollSelector' in globals(): | 
				
			||||||
 | 
					    DefaultSelector = EpollSelector | 
				
			||||||
 | 
					elif 'DevpollSelector' in globals(): | 
				
			||||||
 | 
					    DefaultSelector = DevpollSelector | 
				
			||||||
 | 
					elif 'PollSelector' in globals(): | 
				
			||||||
 | 
					    DefaultSelector = PollSelector | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    DefaultSelector = SelectSelector | 
				
			||||||
@ -0,0 +1,762 @@ | 
				
			|||||||
 | 
					"""Utilities for writing code that runs on Python 2 and 3""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Copyright (c) 2010-2014 Benjamin Peterson | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# Permission is hereby granted, free of charge, to any person obtaining a copy | 
				
			||||||
 | 
					# of this software and associated documentation files (the "Software"), to deal | 
				
			||||||
 | 
					# in the Software without restriction, including without limitation the rights | 
				
			||||||
 | 
					# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
				
			||||||
 | 
					# copies of the Software, and to permit persons to whom the Software is | 
				
			||||||
 | 
					# furnished to do so, subject to the following conditions: | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# The above copyright notice and this permission notice shall be included in all | 
				
			||||||
 | 
					# copies or substantial portions of the Software. | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
				
			||||||
 | 
					# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
				
			||||||
 | 
					# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
				
			||||||
 | 
					# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
				
			||||||
 | 
					# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
				
			||||||
 | 
					# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
				
			||||||
 | 
					# SOFTWARE. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from __future__ import absolute_import | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import functools | 
				
			||||||
 | 
					import operator | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import types | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__author__ = "Benjamin Peterson <benjamin@python.org>" | 
				
			||||||
 | 
					__version__ = "1.8.0" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Useful for very coarse version differentiation. | 
				
			||||||
 | 
					PY2 = sys.version_info[0] == 2 | 
				
			||||||
 | 
					PY3 = sys.version_info[0] == 3 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    string_types = str, | 
				
			||||||
 | 
					    integer_types = int, | 
				
			||||||
 | 
					    class_types = type, | 
				
			||||||
 | 
					    text_type = str | 
				
			||||||
 | 
					    binary_type = bytes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MAXSIZE = sys.maxsize | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    string_types = basestring, | 
				
			||||||
 | 
					    integer_types = (int, long) | 
				
			||||||
 | 
					    class_types = (type, types.ClassType) | 
				
			||||||
 | 
					    text_type = unicode | 
				
			||||||
 | 
					    binary_type = str | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if sys.platform.startswith("java"): | 
				
			||||||
 | 
					        # Jython always uses 32 bits. | 
				
			||||||
 | 
					        MAXSIZE = int((1 << 31) - 1) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        # It's possible to have sizeof(long) != sizeof(Py_ssize_t). | 
				
			||||||
 | 
					        class X(object): | 
				
			||||||
 | 
					            def __len__(self): | 
				
			||||||
 | 
					                return 1 << 31 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            len(X()) | 
				
			||||||
 | 
					        except OverflowError: | 
				
			||||||
 | 
					            # 32-bit | 
				
			||||||
 | 
					            MAXSIZE = int((1 << 31) - 1) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            # 64-bit | 
				
			||||||
 | 
					            MAXSIZE = int((1 << 63) - 1) | 
				
			||||||
 | 
					        del X | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _add_doc(func, doc): | 
				
			||||||
 | 
					    """Add documentation to a function.""" | 
				
			||||||
 | 
					    func.__doc__ = doc | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _import_module(name): | 
				
			||||||
 | 
					    """Import module, returning the module after the last dot.""" | 
				
			||||||
 | 
					    __import__(name) | 
				
			||||||
 | 
					    return sys.modules[name] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _LazyDescr(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, name): | 
				
			||||||
 | 
					        self.name = name | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __get__(self, obj, tp): | 
				
			||||||
 | 
					        result = self._resolve() | 
				
			||||||
 | 
					        setattr(obj, self.name, result) # Invokes __set__. | 
				
			||||||
 | 
					        # This is a bit ugly, but it avoids running this again. | 
				
			||||||
 | 
					        delattr(obj.__class__, self.name) | 
				
			||||||
 | 
					        return result | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MovedModule(_LazyDescr): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, name, old, new=None): | 
				
			||||||
 | 
					        super(MovedModule, self).__init__(name) | 
				
			||||||
 | 
					        if PY3: | 
				
			||||||
 | 
					            if new is None: | 
				
			||||||
 | 
					                new = name | 
				
			||||||
 | 
					            self.mod = new | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.mod = old | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _resolve(self): | 
				
			||||||
 | 
					        return _import_module(self.mod) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, attr): | 
				
			||||||
 | 
					        _module = self._resolve() | 
				
			||||||
 | 
					        value = getattr(_module, attr) | 
				
			||||||
 | 
					        setattr(self, attr, value) | 
				
			||||||
 | 
					        return value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _LazyModule(types.ModuleType): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, name): | 
				
			||||||
 | 
					        super(_LazyModule, self).__init__(name) | 
				
			||||||
 | 
					        self.__doc__ = self.__class__.__doc__ | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __dir__(self): | 
				
			||||||
 | 
					        attrs = ["__doc__", "__name__"] | 
				
			||||||
 | 
					        attrs += [attr.name for attr in self._moved_attributes] | 
				
			||||||
 | 
					        return attrs | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Subclasses should override this | 
				
			||||||
 | 
					    _moved_attributes = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MovedAttribute(_LazyDescr): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): | 
				
			||||||
 | 
					        super(MovedAttribute, self).__init__(name) | 
				
			||||||
 | 
					        if PY3: | 
				
			||||||
 | 
					            if new_mod is None: | 
				
			||||||
 | 
					                new_mod = name | 
				
			||||||
 | 
					            self.mod = new_mod | 
				
			||||||
 | 
					            if new_attr is None: | 
				
			||||||
 | 
					                if old_attr is None: | 
				
			||||||
 | 
					                    new_attr = name | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    new_attr = old_attr | 
				
			||||||
 | 
					            self.attr = new_attr | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            self.mod = old_mod | 
				
			||||||
 | 
					            if old_attr is None: | 
				
			||||||
 | 
					                old_attr = name | 
				
			||||||
 | 
					            self.attr = old_attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _resolve(self): | 
				
			||||||
 | 
					        module = _import_module(self.mod) | 
				
			||||||
 | 
					        return getattr(module, self.attr) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _SixMetaPathImporter(object): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    A meta path importer to import six.moves and its submodules. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This class implements a PEP302 finder and loader. It should be compatible | 
				
			||||||
 | 
					    with Python 2.5 and all existing versions of Python3 | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    def __init__(self, six_module_name): | 
				
			||||||
 | 
					        self.name = six_module_name | 
				
			||||||
 | 
					        self.known_modules = {} | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _add_module(self, mod, *fullnames): | 
				
			||||||
 | 
					        for fullname in fullnames: | 
				
			||||||
 | 
					            self.known_modules[self.name + "." + fullname] = mod | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_module(self, fullname): | 
				
			||||||
 | 
					        return self.known_modules[self.name + "." + fullname] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def find_module(self, fullname, path=None): | 
				
			||||||
 | 
					        if fullname in self.known_modules: | 
				
			||||||
 | 
					            return self | 
				
			||||||
 | 
					        return None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __get_module(self, fullname): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            return self.known_modules[fullname] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise ImportError("This loader does not know module " + fullname) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_module(self, fullname): | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            # in case of a reload | 
				
			||||||
 | 
					            return sys.modules[fullname] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            pass | 
				
			||||||
 | 
					        mod = self.__get_module(fullname) | 
				
			||||||
 | 
					        if isinstance(mod, MovedModule): | 
				
			||||||
 | 
					            mod = mod._resolve() | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            mod.__loader__ = self | 
				
			||||||
 | 
					        sys.modules[fullname] = mod | 
				
			||||||
 | 
					        return mod | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def is_package(self, fullname): | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        Return true, if the named module is a package. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        We need this method to get correct spec objects with | 
				
			||||||
 | 
					        Python 3.4 (see PEP451) | 
				
			||||||
 | 
					        """ | 
				
			||||||
 | 
					        return hasattr(self.__get_module(fullname), "__path__") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_code(self, fullname): | 
				
			||||||
 | 
					        """Return None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Required, if is_package is implemented""" | 
				
			||||||
 | 
					        self.__get_module(fullname)  # eventually raises ImportError | 
				
			||||||
 | 
					        return None | 
				
			||||||
 | 
					    get_source = get_code  # same as get_code | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_importer = _SixMetaPathImporter(__name__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _MovedItems(_LazyModule): | 
				
			||||||
 | 
					    """Lazy loading of moved objects""" | 
				
			||||||
 | 
					    __path__ = []  # mark as package | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_moved_attributes = [ | 
				
			||||||
 | 
					    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), | 
				
			||||||
 | 
					    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), | 
				
			||||||
 | 
					    MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), | 
				
			||||||
 | 
					    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), | 
				
			||||||
 | 
					    MovedAttribute("intern", "__builtin__", "sys"), | 
				
			||||||
 | 
					    MovedAttribute("map", "itertools", "builtins", "imap", "map"), | 
				
			||||||
 | 
					    MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), | 
				
			||||||
 | 
					    MovedAttribute("reload_module", "__builtin__", "imp", "reload"), | 
				
			||||||
 | 
					    MovedAttribute("reduce", "__builtin__", "functools"), | 
				
			||||||
 | 
					    MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), | 
				
			||||||
 | 
					    MovedAttribute("StringIO", "StringIO", "io"), | 
				
			||||||
 | 
					    MovedAttribute("UserDict", "UserDict", "collections"), | 
				
			||||||
 | 
					    MovedAttribute("UserList", "UserList", "collections"), | 
				
			||||||
 | 
					    MovedAttribute("UserString", "UserString", "collections"), | 
				
			||||||
 | 
					    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), | 
				
			||||||
 | 
					    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), | 
				
			||||||
 | 
					    MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MovedModule("builtins", "__builtin__"), | 
				
			||||||
 | 
					    MovedModule("configparser", "ConfigParser"), | 
				
			||||||
 | 
					    MovedModule("copyreg", "copy_reg"), | 
				
			||||||
 | 
					    MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), | 
				
			||||||
 | 
					    MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), | 
				
			||||||
 | 
					    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), | 
				
			||||||
 | 
					    MovedModule("http_cookies", "Cookie", "http.cookies"), | 
				
			||||||
 | 
					    MovedModule("html_entities", "htmlentitydefs", "html.entities"), | 
				
			||||||
 | 
					    MovedModule("html_parser", "HTMLParser", "html.parser"), | 
				
			||||||
 | 
					    MovedModule("http_client", "httplib", "http.client"), | 
				
			||||||
 | 
					    MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), | 
				
			||||||
 | 
					    MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), | 
				
			||||||
 | 
					    MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), | 
				
			||||||
 | 
					    MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), | 
				
			||||||
 | 
					    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), | 
				
			||||||
 | 
					    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), | 
				
			||||||
 | 
					    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), | 
				
			||||||
 | 
					    MovedModule("cPickle", "cPickle", "pickle"), | 
				
			||||||
 | 
					    MovedModule("queue", "Queue"), | 
				
			||||||
 | 
					    MovedModule("reprlib", "repr"), | 
				
			||||||
 | 
					    MovedModule("socketserver", "SocketServer"), | 
				
			||||||
 | 
					    MovedModule("_thread", "thread", "_thread"), | 
				
			||||||
 | 
					    MovedModule("tkinter", "Tkinter"), | 
				
			||||||
 | 
					    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), | 
				
			||||||
 | 
					    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), | 
				
			||||||
 | 
					    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), | 
				
			||||||
 | 
					    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), | 
				
			||||||
 | 
					    MovedModule("tkinter_tix", "Tix", "tkinter.tix"), | 
				
			||||||
 | 
					    MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), | 
				
			||||||
 | 
					    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), | 
				
			||||||
 | 
					    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), | 
				
			||||||
 | 
					    MovedModule("tkinter_colorchooser", "tkColorChooser", | 
				
			||||||
 | 
					                "tkinter.colorchooser"), | 
				
			||||||
 | 
					    MovedModule("tkinter_commondialog", "tkCommonDialog", | 
				
			||||||
 | 
					                "tkinter.commondialog"), | 
				
			||||||
 | 
					    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), | 
				
			||||||
 | 
					    MovedModule("tkinter_font", "tkFont", "tkinter.font"), | 
				
			||||||
 | 
					    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), | 
				
			||||||
 | 
					    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", | 
				
			||||||
 | 
					                "tkinter.simpledialog"), | 
				
			||||||
 | 
					    MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), | 
				
			||||||
 | 
					    MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), | 
				
			||||||
 | 
					    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), | 
				
			||||||
 | 
					    MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), | 
				
			||||||
 | 
					    MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), | 
				
			||||||
 | 
					    MovedModule("winreg", "_winreg"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _moved_attributes: | 
				
			||||||
 | 
					    setattr(_MovedItems, attr.name, attr) | 
				
			||||||
 | 
					    if isinstance(attr, MovedModule): | 
				
			||||||
 | 
					        _importer._add_module(attr, "moves." + attr.name) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_MovedItems._moved_attributes = _moved_attributes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					moves = _MovedItems(__name__ + ".moves") | 
				
			||||||
 | 
					_importer._add_module(moves, "moves") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_parse(_LazyModule): | 
				
			||||||
 | 
					    """Lazy loading of moved objects in six.moves.urllib_parse""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_urllib_parse_moved_attributes = [ | 
				
			||||||
 | 
					    MovedAttribute("ParseResult", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("SplitResult", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("parse_qs", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("urldefrag", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("urljoin", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("urlparse", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("urlsplit", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("urlunparse", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("quote", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("quote_plus", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("unquote", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("unquote_plus", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("urlencode", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("splitquery", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("splittag", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("splituser", "urllib", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("uses_params", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("uses_query", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					    MovedAttribute("uses_relative", "urlparse", "urllib.parse"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _urllib_parse_moved_attributes: | 
				
			||||||
 | 
					    setattr(Module_six_moves_urllib_parse, attr.name, attr) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), | 
				
			||||||
 | 
					                      "moves.urllib_parse", "moves.urllib.parse") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_error(_LazyModule): | 
				
			||||||
 | 
					    """Lazy loading of moved objects in six.moves.urllib_error""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_urllib_error_moved_attributes = [ | 
				
			||||||
 | 
					    MovedAttribute("URLError", "urllib2", "urllib.error"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPError", "urllib2", "urllib.error"), | 
				
			||||||
 | 
					    MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _urllib_error_moved_attributes: | 
				
			||||||
 | 
					    setattr(Module_six_moves_urllib_error, attr.name, attr) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), | 
				
			||||||
 | 
					                      "moves.urllib_error", "moves.urllib.error") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_request(_LazyModule): | 
				
			||||||
 | 
					    """Lazy loading of moved objects in six.moves.urllib_request""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_urllib_request_moved_attributes = [ | 
				
			||||||
 | 
					    MovedAttribute("urlopen", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("install_opener", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("build_opener", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("pathname2url", "urllib", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("url2pathname", "urllib", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("getproxies", "urllib", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("Request", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("BaseHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("FileHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("FTPHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("urlretrieve", "urllib", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("urlcleanup", "urllib", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("URLopener", "urllib", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("FancyURLopener", "urllib", "urllib.request"), | 
				
			||||||
 | 
					    MovedAttribute("proxy_bypass", "urllib", "urllib.request"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _urllib_request_moved_attributes: | 
				
			||||||
 | 
					    setattr(Module_six_moves_urllib_request, attr.name, attr) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), | 
				
			||||||
 | 
					                      "moves.urllib_request", "moves.urllib.request") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_response(_LazyModule): | 
				
			||||||
 | 
					    """Lazy loading of moved objects in six.moves.urllib_response""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_urllib_response_moved_attributes = [ | 
				
			||||||
 | 
					    MovedAttribute("addbase", "urllib", "urllib.response"), | 
				
			||||||
 | 
					    MovedAttribute("addclosehook", "urllib", "urllib.response"), | 
				
			||||||
 | 
					    MovedAttribute("addinfo", "urllib", "urllib.response"), | 
				
			||||||
 | 
					    MovedAttribute("addinfourl", "urllib", "urllib.response"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _urllib_response_moved_attributes: | 
				
			||||||
 | 
					    setattr(Module_six_moves_urllib_response, attr.name, attr) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), | 
				
			||||||
 | 
					                      "moves.urllib_response", "moves.urllib.response") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib_robotparser(_LazyModule): | 
				
			||||||
 | 
					    """Lazy loading of moved objects in six.moves.urllib_robotparser""" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_urllib_robotparser_moved_attributes = [ | 
				
			||||||
 | 
					    MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), | 
				
			||||||
 | 
					] | 
				
			||||||
 | 
					for attr in _urllib_robotparser_moved_attributes: | 
				
			||||||
 | 
					    setattr(Module_six_moves_urllib_robotparser, attr.name, attr) | 
				
			||||||
 | 
					del attr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), | 
				
			||||||
 | 
					                      "moves.urllib_robotparser", "moves.urllib.robotparser") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Module_six_moves_urllib(types.ModuleType): | 
				
			||||||
 | 
					    """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" | 
				
			||||||
 | 
					    __path__ = []  # mark as package | 
				
			||||||
 | 
					    parse = _importer._get_module("moves.urllib_parse") | 
				
			||||||
 | 
					    error = _importer._get_module("moves.urllib_error") | 
				
			||||||
 | 
					    request = _importer._get_module("moves.urllib_request") | 
				
			||||||
 | 
					    response = _importer._get_module("moves.urllib_response") | 
				
			||||||
 | 
					    robotparser = _importer._get_module("moves.urllib_robotparser") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __dir__(self): | 
				
			||||||
 | 
					        return ['parse', 'error', 'request', 'response', 'robotparser'] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), | 
				
			||||||
 | 
					                      "moves.urllib") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def add_move(move): | 
				
			||||||
 | 
					    """Add an item to six.moves.""" | 
				
			||||||
 | 
					    setattr(_MovedItems, move.name, move) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def remove_move(name): | 
				
			||||||
 | 
					    """Remove item from six.moves.""" | 
				
			||||||
 | 
					    try: | 
				
			||||||
 | 
					        delattr(_MovedItems, name) | 
				
			||||||
 | 
					    except AttributeError: | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            del moves.__dict__[name] | 
				
			||||||
 | 
					        except KeyError: | 
				
			||||||
 | 
					            raise AttributeError("no such move, %r" % (name,)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    _meth_func = "__func__" | 
				
			||||||
 | 
					    _meth_self = "__self__" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _func_closure = "__closure__" | 
				
			||||||
 | 
					    _func_code = "__code__" | 
				
			||||||
 | 
					    _func_defaults = "__defaults__" | 
				
			||||||
 | 
					    _func_globals = "__globals__" | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    _meth_func = "im_func" | 
				
			||||||
 | 
					    _meth_self = "im_self" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _func_closure = "func_closure" | 
				
			||||||
 | 
					    _func_code = "func_code" | 
				
			||||||
 | 
					    _func_defaults = "func_defaults" | 
				
			||||||
 | 
					    _func_globals = "func_globals" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    advance_iterator = next | 
				
			||||||
 | 
					except NameError: | 
				
			||||||
 | 
					    def advance_iterator(it): | 
				
			||||||
 | 
					        return it.next() | 
				
			||||||
 | 
					next = advance_iterator | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try: | 
				
			||||||
 | 
					    callable = callable | 
				
			||||||
 | 
					except NameError: | 
				
			||||||
 | 
					    def callable(obj): | 
				
			||||||
 | 
					        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    def get_unbound_function(unbound): | 
				
			||||||
 | 
					        return unbound | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    create_bound_method = types.MethodType | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Iterator = object | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def get_unbound_function(unbound): | 
				
			||||||
 | 
					        return unbound.im_func | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_bound_method(func, obj): | 
				
			||||||
 | 
					        return types.MethodType(func, obj, obj.__class__) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Iterator(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def next(self): | 
				
			||||||
 | 
					            return type(self).__next__(self) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    callable = callable | 
				
			||||||
 | 
					_add_doc(get_unbound_function, | 
				
			||||||
 | 
					         """Get the function out of a possibly unbound function""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					get_method_function = operator.attrgetter(_meth_func) | 
				
			||||||
 | 
					get_method_self = operator.attrgetter(_meth_self) | 
				
			||||||
 | 
					get_function_closure = operator.attrgetter(_func_closure) | 
				
			||||||
 | 
					get_function_code = operator.attrgetter(_func_code) | 
				
			||||||
 | 
					get_function_defaults = operator.attrgetter(_func_defaults) | 
				
			||||||
 | 
					get_function_globals = operator.attrgetter(_func_globals) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    def iterkeys(d, **kw): | 
				
			||||||
 | 
					        return iter(d.keys(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def itervalues(d, **kw): | 
				
			||||||
 | 
					        return iter(d.values(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def iteritems(d, **kw): | 
				
			||||||
 | 
					        return iter(d.items(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def iterlists(d, **kw): | 
				
			||||||
 | 
					        return iter(d.lists(**kw)) | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def iterkeys(d, **kw): | 
				
			||||||
 | 
					        return iter(d.iterkeys(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def itervalues(d, **kw): | 
				
			||||||
 | 
					        return iter(d.itervalues(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def iteritems(d, **kw): | 
				
			||||||
 | 
					        return iter(d.iteritems(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def iterlists(d, **kw): | 
				
			||||||
 | 
					        return iter(d.iterlists(**kw)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") | 
				
			||||||
 | 
					_add_doc(itervalues, "Return an iterator over the values of a dictionary.") | 
				
			||||||
 | 
					_add_doc(iteritems, | 
				
			||||||
 | 
					         "Return an iterator over the (key, value) pairs of a dictionary.") | 
				
			||||||
 | 
					_add_doc(iterlists, | 
				
			||||||
 | 
					         "Return an iterator over the (key, [values]) pairs of a dictionary.") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    def b(s): | 
				
			||||||
 | 
					        return s.encode("latin-1") | 
				
			||||||
 | 
					    def u(s): | 
				
			||||||
 | 
					        return s | 
				
			||||||
 | 
					    unichr = chr | 
				
			||||||
 | 
					    if sys.version_info[1] <= 1: | 
				
			||||||
 | 
					        def int2byte(i): | 
				
			||||||
 | 
					            return bytes((i,)) | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        # This is about 2x faster than the implementation above on 3.2+ | 
				
			||||||
 | 
					        int2byte = operator.methodcaller("to_bytes", 1, "big") | 
				
			||||||
 | 
					    byte2int = operator.itemgetter(0) | 
				
			||||||
 | 
					    indexbytes = operator.getitem | 
				
			||||||
 | 
					    iterbytes = iter | 
				
			||||||
 | 
					    import io | 
				
			||||||
 | 
					    StringIO = io.StringIO | 
				
			||||||
 | 
					    BytesIO = io.BytesIO | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def b(s): | 
				
			||||||
 | 
					        return s | 
				
			||||||
 | 
					    # Workaround for standalone backslash | 
				
			||||||
 | 
					    def u(s): | 
				
			||||||
 | 
					        return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") | 
				
			||||||
 | 
					    unichr = unichr | 
				
			||||||
 | 
					    int2byte = chr | 
				
			||||||
 | 
					    def byte2int(bs): | 
				
			||||||
 | 
					        return ord(bs[0]) | 
				
			||||||
 | 
					    def indexbytes(buf, i): | 
				
			||||||
 | 
					        return ord(buf[i]) | 
				
			||||||
 | 
					    def iterbytes(buf): | 
				
			||||||
 | 
					        return (ord(byte) for byte in buf) | 
				
			||||||
 | 
					    import StringIO | 
				
			||||||
 | 
					    StringIO = BytesIO = StringIO.StringIO | 
				
			||||||
 | 
					_add_doc(b, """Byte literal""") | 
				
			||||||
 | 
					_add_doc(u, """Text literal""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if PY3: | 
				
			||||||
 | 
					    exec_ = getattr(moves.builtins, "exec") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def reraise(tp, value, tb=None): | 
				
			||||||
 | 
					        if value is None: | 
				
			||||||
 | 
					            value = tp() | 
				
			||||||
 | 
					        if value.__traceback__ is not tb: | 
				
			||||||
 | 
					            raise value.with_traceback(tb) | 
				
			||||||
 | 
					        raise value | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    def exec_(_code_, _globs_=None, _locs_=None): | 
				
			||||||
 | 
					        """Execute code in a namespace.""" | 
				
			||||||
 | 
					        if _globs_ is None: | 
				
			||||||
 | 
					            frame = sys._getframe(1) | 
				
			||||||
 | 
					            _globs_ = frame.f_globals | 
				
			||||||
 | 
					            if _locs_ is None: | 
				
			||||||
 | 
					                _locs_ = frame.f_locals | 
				
			||||||
 | 
					            del frame | 
				
			||||||
 | 
					        elif _locs_ is None: | 
				
			||||||
 | 
					            _locs_ = _globs_ | 
				
			||||||
 | 
					        exec("""exec _code_ in _globs_, _locs_""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    exec_("""def reraise(tp, value, tb=None): | 
				
			||||||
 | 
					    raise tp, value, tb | 
				
			||||||
 | 
					""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					print_ = getattr(moves.builtins, "print", None) | 
				
			||||||
 | 
					if print_ is None: | 
				
			||||||
 | 
					    def print_(*args, **kwargs): | 
				
			||||||
 | 
					        """The new-style print function for Python 2.4 and 2.5.""" | 
				
			||||||
 | 
					        fp = kwargs.pop("file", sys.stdout) | 
				
			||||||
 | 
					        if fp is None: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					        def write(data): | 
				
			||||||
 | 
					            if not isinstance(data, basestring): | 
				
			||||||
 | 
					                data = str(data) | 
				
			||||||
 | 
					            # If the file has an encoding, encode unicode with it. | 
				
			||||||
 | 
					            if (isinstance(fp, file) and | 
				
			||||||
 | 
					                isinstance(data, unicode) and | 
				
			||||||
 | 
					                fp.encoding is not None): | 
				
			||||||
 | 
					                errors = getattr(fp, "errors", None) | 
				
			||||||
 | 
					                if errors is None: | 
				
			||||||
 | 
					                    errors = "strict" | 
				
			||||||
 | 
					                data = data.encode(fp.encoding, errors) | 
				
			||||||
 | 
					            fp.write(data) | 
				
			||||||
 | 
					        want_unicode = False | 
				
			||||||
 | 
					        sep = kwargs.pop("sep", None) | 
				
			||||||
 | 
					        if sep is not None: | 
				
			||||||
 | 
					            if isinstance(sep, unicode): | 
				
			||||||
 | 
					                want_unicode = True | 
				
			||||||
 | 
					            elif not isinstance(sep, str): | 
				
			||||||
 | 
					                raise TypeError("sep must be None or a string") | 
				
			||||||
 | 
					        end = kwargs.pop("end", None) | 
				
			||||||
 | 
					        if end is not None: | 
				
			||||||
 | 
					            if isinstance(end, unicode): | 
				
			||||||
 | 
					                want_unicode = True | 
				
			||||||
 | 
					            elif not isinstance(end, str): | 
				
			||||||
 | 
					                raise TypeError("end must be None or a string") | 
				
			||||||
 | 
					        if kwargs: | 
				
			||||||
 | 
					            raise TypeError("invalid keyword arguments to print()") | 
				
			||||||
 | 
					        if not want_unicode: | 
				
			||||||
 | 
					            for arg in args: | 
				
			||||||
 | 
					                if isinstance(arg, unicode): | 
				
			||||||
 | 
					                    want_unicode = True | 
				
			||||||
 | 
					                    break | 
				
			||||||
 | 
					        if want_unicode: | 
				
			||||||
 | 
					            newline = unicode("\n") | 
				
			||||||
 | 
					            space = unicode(" ") | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            newline = "\n" | 
				
			||||||
 | 
					            space = " " | 
				
			||||||
 | 
					        if sep is None: | 
				
			||||||
 | 
					            sep = space | 
				
			||||||
 | 
					        if end is None: | 
				
			||||||
 | 
					            end = newline | 
				
			||||||
 | 
					        for i, arg in enumerate(args): | 
				
			||||||
 | 
					            if i: | 
				
			||||||
 | 
					                write(sep) | 
				
			||||||
 | 
					            write(arg) | 
				
			||||||
 | 
					        write(end) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_add_doc(reraise, """Reraise an exception.""") | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info[0:2] < (3, 4): | 
				
			||||||
 | 
					    def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, | 
				
			||||||
 | 
					              updated=functools.WRAPPER_UPDATES): | 
				
			||||||
 | 
					        def wrapper(f): | 
				
			||||||
 | 
					            f = functools.wraps(wrapped)(f) | 
				
			||||||
 | 
					            f.__wrapped__ = wrapped | 
				
			||||||
 | 
					            return f | 
				
			||||||
 | 
					        return wrapper | 
				
			||||||
 | 
					else: | 
				
			||||||
 | 
					    wraps = functools.wraps | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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(meta): | 
				
			||||||
 | 
					        def __new__(cls, name, this_bases, d): | 
				
			||||||
 | 
					            return meta(name, bases, d) | 
				
			||||||
 | 
					    return type.__new__(metaclass, 'temporary_class', (), {}) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def add_metaclass(metaclass): | 
				
			||||||
 | 
					    """Class decorator for creating a class with a metaclass.""" | 
				
			||||||
 | 
					    def wrapper(cls): | 
				
			||||||
 | 
					        orig_vars = cls.__dict__.copy() | 
				
			||||||
 | 
					        slots = orig_vars.get('__slots__') | 
				
			||||||
 | 
					        if slots is not None: | 
				
			||||||
 | 
					            if isinstance(slots, str): | 
				
			||||||
 | 
					                slots = [slots] | 
				
			||||||
 | 
					            for slots_var in slots: | 
				
			||||||
 | 
					                orig_vars.pop(slots_var) | 
				
			||||||
 | 
					        orig_vars.pop('__dict__', None) | 
				
			||||||
 | 
					        orig_vars.pop('__weakref__', None) | 
				
			||||||
 | 
					        return metaclass(cls.__name__, cls.__bases__, orig_vars) | 
				
			||||||
 | 
					    return wrapper | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Complete the moves implementation. | 
				
			||||||
 | 
					# This code is at the end of this module to speed up module loading. | 
				
			||||||
 | 
					# Turn this module into a package. | 
				
			||||||
 | 
					__path__ = []  # required for PEP 302 and PEP 451 | 
				
			||||||
 | 
					__package__ = __name__  # see PEP 366 @ReservedAssignment | 
				
			||||||
 | 
					if globals().get("__spec__") is not None: | 
				
			||||||
 | 
					    __spec__.submodule_search_locations = []  # PEP 451 @UndefinedVariable | 
				
			||||||
 | 
					# Remove other six meta path importers, since they cause problems. This can | 
				
			||||||
 | 
					# happen if six is removed from sys.modules and then reloaded. (Setuptools does | 
				
			||||||
 | 
					# this for some reason.) | 
				
			||||||
 | 
					if sys.meta_path: | 
				
			||||||
 | 
					    for i, importer in enumerate(sys.meta_path): | 
				
			||||||
 | 
					        # Here's some real nastiness: Another "instance" of the six module might | 
				
			||||||
 | 
					        # be floating around. Therefore, we can't use isinstance() to check for | 
				
			||||||
 | 
					        # the six meta path importer, since the other six instance will have | 
				
			||||||
 | 
					        # inserted an importer with different class. | 
				
			||||||
 | 
					        if (type(importer).__name__ == "_SixMetaPathImporter" and | 
				
			||||||
 | 
					            importer.name == __name__): | 
				
			||||||
 | 
					            del sys.meta_path[i] | 
				
			||||||
 | 
					            break | 
				
			||||||
 | 
					    del i, importer | 
				
			||||||
 | 
					# Finally, add the importer to the meta path import hook. | 
				
			||||||
 | 
					sys.meta_path.append(_importer) | 
				
			||||||
@ -0,0 +1,209 @@ | 
				
			|||||||
 | 
					# -*- coding: utf-8 - | 
				
			||||||
 | 
					# | 
				
			||||||
 | 
					# This file is part of gunicorn released under the MIT license. | 
				
			||||||
 | 
					# See the NOTICE for more information. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import errno | 
				
			||||||
 | 
					import os | 
				
			||||||
 | 
					import socket | 
				
			||||||
 | 
					import stat | 
				
			||||||
 | 
					import sys | 
				
			||||||
 | 
					import time | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gunicorn import util | 
				
			||||||
 | 
					from gunicorn.six import string_types | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseSocket(object): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, address, conf, log, fd=None): | 
				
			||||||
 | 
					        self.log = log | 
				
			||||||
 | 
					        self.conf = conf | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.cfg_addr = address | 
				
			||||||
 | 
					        if fd is None: | 
				
			||||||
 | 
					            sock = socket.socket(self.FAMILY, socket.SOCK_STREAM) | 
				
			||||||
 | 
					            bound = False | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            sock = socket.fromfd(fd, self.FAMILY, socket.SOCK_STREAM) | 
				
			||||||
 | 
					            os.close(fd) | 
				
			||||||
 | 
					            bound = True | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.sock = self.set_options(sock, bound=bound) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "<socket %d>" % self.sock.fileno() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getattr__(self, name): | 
				
			||||||
 | 
					        return getattr(self.sock, name) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_options(self, sock, bound=False): | 
				
			||||||
 | 
					        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | 
				
			||||||
 | 
					        if (self.conf.reuse_port | 
				
			||||||
 | 
					            and hasattr(socket, 'SO_REUSEPORT')):  # pragma: no cover | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) | 
				
			||||||
 | 
					            except socket.error as err: | 
				
			||||||
 | 
					                if err.errno not in (errno.ENOPROTOOPT, errno.EINVAL): | 
				
			||||||
 | 
					                    raise | 
				
			||||||
 | 
					        if not bound: | 
				
			||||||
 | 
					            self.bind(sock) | 
				
			||||||
 | 
					        sock.setblocking(0) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # make sure that the socket can be inherited | 
				
			||||||
 | 
					        if hasattr(sock, "set_inheritable"): | 
				
			||||||
 | 
					            sock.set_inheritable(True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sock.listen(self.conf.backlog) | 
				
			||||||
 | 
					        return sock | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def bind(self, sock): | 
				
			||||||
 | 
					        sock.bind(self.cfg_addr) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def close(self): | 
				
			||||||
 | 
					        if self.sock is None: | 
				
			||||||
 | 
					            return | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try: | 
				
			||||||
 | 
					            self.sock.close() | 
				
			||||||
 | 
					        except socket.error as e: | 
				
			||||||
 | 
					            self.log.info("Error while closing socket %s", str(e)) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.sock = None | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TCPSocket(BaseSocket): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FAMILY = socket.AF_INET | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        if self.conf.is_ssl: | 
				
			||||||
 | 
					            scheme = "https" | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            scheme = "http" | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addr = self.sock.getsockname() | 
				
			||||||
 | 
					        return "%s://%s:%d" % (scheme, addr[0], addr[1]) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_options(self, sock, bound=False): | 
				
			||||||
 | 
					        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | 
				
			||||||
 | 
					        return super(TCPSocket, self).set_options(sock, bound=bound) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TCP6Socket(TCPSocket): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FAMILY = socket.AF_INET6 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        (host, port, _, _) = self.sock.getsockname() | 
				
			||||||
 | 
					        return "http://[%s]:%d" % (host, port) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UnixSocket(BaseSocket): | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FAMILY = socket.AF_UNIX | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, addr, conf, log, fd=None): | 
				
			||||||
 | 
					        if fd is None: | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                st = os.stat(addr) | 
				
			||||||
 | 
					            except OSError as e: | 
				
			||||||
 | 
					                if e.args[0] != errno.ENOENT: | 
				
			||||||
 | 
					                    raise | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                if stat.S_ISSOCK(st.st_mode): | 
				
			||||||
 | 
					                    os.remove(addr) | 
				
			||||||
 | 
					                else: | 
				
			||||||
 | 
					                    raise ValueError("%r is not a socket" % addr) | 
				
			||||||
 | 
					        super(UnixSocket, self).__init__(addr, conf, log, fd=fd) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self): | 
				
			||||||
 | 
					        return "unix:%s" % self.cfg_addr | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def bind(self, sock): | 
				
			||||||
 | 
					        old_umask = os.umask(self.conf.umask) | 
				
			||||||
 | 
					        sock.bind(self.cfg_addr) | 
				
			||||||
 | 
					        util.chown(self.cfg_addr, self.conf.uid, self.conf.gid) | 
				
			||||||
 | 
					        os.umask(old_umask) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def _sock_type(addr): | 
				
			||||||
 | 
					    if isinstance(addr, tuple): | 
				
			||||||
 | 
					        if util.is_ipv6(addr[0]): | 
				
			||||||
 | 
					            sock_type = TCP6Socket | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					            sock_type = TCPSocket | 
				
			||||||
 | 
					    elif isinstance(addr, string_types): | 
				
			||||||
 | 
					        sock_type = UnixSocket | 
				
			||||||
 | 
					    else: | 
				
			||||||
 | 
					        raise TypeError("Unable to create socket from: %r" % addr) | 
				
			||||||
 | 
					    return sock_type | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_sockets(conf, log, fds=None): | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    Create a new socket for the configured addresses or file descriptors. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If a configured address is a tuple then a TCP socket is created. | 
				
			||||||
 | 
					    If it is a string, a Unix socket is created. Otherwise, a TypeError is | 
				
			||||||
 | 
					    raised. | 
				
			||||||
 | 
					    """ | 
				
			||||||
 | 
					    listeners = [] | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # get it only once | 
				
			||||||
 | 
					    laddr = conf.address | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # check ssl config early to raise the error on startup | 
				
			||||||
 | 
					    # only the certfile is needed since it can contains the keyfile | 
				
			||||||
 | 
					    if conf.certfile and not os.path.exists(conf.certfile): | 
				
			||||||
 | 
					        raise ValueError('certfile "%s" does not exist' % conf.certfile) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if conf.keyfile and not os.path.exists(conf.keyfile): | 
				
			||||||
 | 
					        raise ValueError('keyfile "%s" does not exist' % conf.keyfile) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # sockets are already bound | 
				
			||||||
 | 
					    if fds is not None: | 
				
			||||||
 | 
					        for fd in fds: | 
				
			||||||
 | 
					            sock = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM) | 
				
			||||||
 | 
					            sock_name = sock.getsockname() | 
				
			||||||
 | 
					            sock_type = _sock_type(sock_name) | 
				
			||||||
 | 
					            listener = sock_type(sock_name, conf, log, fd=fd) | 
				
			||||||
 | 
					            listeners.append(listener) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return listeners | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # no sockets is bound, first initialization of gunicorn in this env. | 
				
			||||||
 | 
					    for addr in laddr: | 
				
			||||||
 | 
					        sock_type = _sock_type(addr) | 
				
			||||||
 | 
					        sock = None | 
				
			||||||
 | 
					        for i in range(5): | 
				
			||||||
 | 
					            try: | 
				
			||||||
 | 
					                sock = sock_type(addr, conf, log) | 
				
			||||||
 | 
					            except socket.error as e: | 
				
			||||||
 | 
					                if e.args[0] == errno.EADDRINUSE: | 
				
			||||||
 | 
					                    log.error("Connection in use: %s", str(addr)) | 
				
			||||||
 | 
					                if e.args[0] == errno.EADDRNOTAVAIL: | 
				
			||||||
 | 
					                    log.error("Invalid address: %s", str(addr)) | 
				
			||||||
 | 
					                if i < 5: | 
				
			||||||
 | 
					                    msg = "connection to {addr} failed: {error}" | 
				
			||||||
 | 
					                    log.debug(msg.format(addr=str(addr), error=str(e))) | 
				
			||||||
 | 
					                    log.error("Retrying in 1 second.") | 
				
			||||||
 | 
					                    time.sleep(1) | 
				
			||||||
 | 
					            else: | 
				
			||||||
 | 
					                break | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if sock is None: | 
				
			||||||
 | 
					            log.error("Can't connect to %s", str(addr)) | 
				
			||||||
 | 
					            sys.exit(1) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        listeners.append(sock) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return listeners | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def close_sockets(listeners, unlink=True): | 
				
			||||||
 | 
					    for sock in listeners: | 
				
			||||||
 | 
					        sock_name = sock.getsockname() | 
				
			||||||
 | 
					        sock.close() | 
				
			||||||
 | 
					        if unlink and _sock_type(sock_name) is UnixSocket: | 
				
			||||||
 | 
					            os.unlink(sock_name) | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue