diff -Nru flask-0.12.2/AUTHORS flask-1.0.2/AUTHORS --- flask-0.12.2/AUTHORS 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/AUTHORS 2018-05-02 00:50:32.000000000 +0000 @@ -1,37 +1,12 @@ -Flask is written and maintained by Armin Ronacher and -various contributors: +Flask is developed and maintained by the Pallets team and community +contributors. It was created by Armin Ronacher. The core maintainers +are: -Development Lead -```````````````` +- David Lord (davidism) +- Adrian Mönnich (ThiefMaster) +- Armin Ronacher (mitsuhiko) +- Marcus Unterwaditzer (untitaker) -- Armin Ronacher +A full list of contributors is available from git with:: -Patches and Suggestions -``````````````````````` - -- Adam Zapletal -- Ali Afshar -- Chris Edgemon -- Chris Grindstaff -- Christopher Grebs -- Daniel Neuhäuser -- Dan Sully -- David Lord @davidism -- Edmond Burnett -- Florent Xicluna -- Georg Brandl -- Jeff Widman @jeffwidman -- Justin Quick -- Kenneth Reitz -- Keyan Pishdadian -- Marian Sigler -- Martijn Pieters -- Matt Campell -- Matthew Frazier -- Michael van Tellingen -- Ron DuPlain -- Sebastien Estienne -- Simon Sapin -- Stephane Wirtel -- Thomas Schranz -- Zhao Xiaohong + git shortlog -sne diff -Nru flask-0.12.2/CHANGES flask-1.0.2/CHANGES --- flask-0.12.2/CHANGES 2017-05-16 06:39:14.000000000 +0000 +++ flask-1.0.2/CHANGES 1970-01-01 00:00:00.000000000 +0000 @@ -1,633 +0,0 @@ -Flask Changelog -=============== - -Here you can see the full list of changes between each Flask release. - -Version 0.13 ------------- - -Major release, unreleased - -- Make `app.run()` into a noop if a Flask application is run from the - development server on the command line. This avoids some behavior that - was confusing to debug for newcomers. -- Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify() - method returns compressed response by default, and pretty response in - debug mode. - -Version 0.12.2 --------------- - -Released on May 16 2017 - -- Fix a bug in `safe_join` on Windows. - -Version 0.12.1 --------------- - -Bugfix release, released on March 31st 2017 - -- Prevent `flask run` from showing a NoAppException when an ImportError occurs - within the imported application module. -- Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix - ``#2118``. -- Call `ctx.auto_pop` with the exception object instead of `None`, in the - event that a `BaseException` such as `KeyboardInterrupt` is raised in a - request handler. - -Version 0.12 ------------- - -Released on December 21st 2016, codename Punsch. - -- the cli command now responds to `--version`. -- Mimetype guessing and ETag generation for file-like objects in ``send_file`` - has been removed, as per issue ``#104``. See pull request ``#1849``. -- Mimetype guessing in ``send_file`` now fails loudly and doesn't fall back to - ``application/octet-stream``. See pull request ``#1988``. -- Make ``flask.safe_join`` able to join multiple paths like ``os.path.join`` - (pull request ``#1730``). -- Revert a behavior change that made the dev server crash instead of returning - a Internal Server Error (pull request ``#2006``). -- Correctly invoke response handlers for both regular request dispatching as - well as error handlers. -- Disable logger propagation by default for the app logger. -- Add support for range requests in ``send_file``. -- ``app.test_client`` includes preset default environment, which can now be - directly set, instead of per ``client.get``. - -Version 0.11.2 --------------- - -Bugfix release, unreleased - -- Fix crash when running under PyPy3, see pull request ``#1814``. - -Version 0.11.1 --------------- - -Bugfix release, released on June 7th 2016. - -- Fixed a bug that prevented ``FLASK_APP=foobar/__init__.py`` from working. See - pull request ``#1872``. - -Version 0.11 ------------- - -Released on May 29th 2016, codename Absinthe. - -- Added support to serializing top-level arrays to :func:`flask.jsonify`. This - introduces a security risk in ancient browsers. See - :ref:`json-security` for details. -- Added before_render_template signal. -- Added `**kwargs` to :meth:`flask.Test.test_client` to support passing - additional keyword arguments to the constructor of - :attr:`flask.Flask.test_client_class`. -- Added ``SESSION_REFRESH_EACH_REQUEST`` config key that controls the - set-cookie behavior. If set to ``True`` a permanent session will be - refreshed each request and get their lifetime extended, if set to - ``False`` it will only be modified if the session actually modifies. - Non permanent sessions are not affected by this and will always - expire if the browser window closes. -- Made Flask support custom JSON mimetypes for incoming data. -- Added support for returning tuples in the form ``(response, headers)`` - from a view function. -- Added :meth:`flask.Config.from_json`. -- Added :attr:`flask.Flask.config_class`. -- Added :meth:`flask.Config.get_namespace`. -- Templates are no longer automatically reloaded outside of debug mode. This - can be configured with the new ``TEMPLATES_AUTO_RELOAD`` config key. -- Added a workaround for a limitation in Python 3.3's namespace loader. -- Added support for explicit root paths when using Python 3.3's namespace - packages. -- Added :command:`flask` and the ``flask.cli`` module to start the local - debug server through the click CLI system. This is recommended over the old - ``flask.run()`` method as it works faster and more reliable due to a - different design and also replaces ``Flask-Script``. -- Error handlers that match specific classes are now checked first, - thereby allowing catching exceptions that are subclasses of HTTP - exceptions (in ``werkzeug.exceptions``). This makes it possible - for an extension author to create exceptions that will by default - result in the HTTP error of their choosing, but may be caught with - a custom error handler if desired. -- Added :meth:`flask.Config.from_mapping`. -- Flask will now log by default even if debug is disabled. The log format is - now hardcoded but the default log handling can be disabled through the - ``LOGGER_HANDLER_POLICY`` configuration key. -- Removed deprecated module functionality. -- Added the ``EXPLAIN_TEMPLATE_LOADING`` config flag which when enabled will - instruct Flask to explain how it locates templates. This should help - users debug when the wrong templates are loaded. -- Enforce blueprint handling in the order they were registered for template - loading. -- Ported test suite to py.test. -- Deprecated ``request.json`` in favour of ``request.get_json()``. -- Add "pretty" and "compressed" separators definitions in jsonify() method. - Reduces JSON response size when JSONIFY_PRETTYPRINT_REGULAR=False by removing - unnecessary white space included by default after separators. -- JSON responses are now terminated with a newline character, because it is a - convention that UNIX text files end with a newline and some clients don't - deal well when this newline is missing. See - https://github.com/pallets/flask/pull/1262 -- this came up originally as a - part of https://github.com/kennethreitz/httpbin/issues/168 -- The automatically provided ``OPTIONS`` method is now correctly disabled if - the user registered an overriding rule with the lowercase-version - ``options`` (issue ``#1288``). -- ``flask.json.jsonify`` now supports the ``datetime.date`` type (pull request - ``#1326``). -- Don't leak exception info of already catched exceptions to context teardown - handlers (pull request ``#1393``). -- Allow custom Jinja environment subclasses (pull request ``#1422``). -- ``flask.g`` now has ``pop()`` and ``setdefault`` methods. -- Turn on autoescape for ``flask.templating.render_template_string`` by default - (pull request ``#1515``). -- ``flask.ext`` is now deprecated (pull request ``#1484``). -- ``send_from_directory`` now raises BadRequest if the filename is invalid on - the server OS (pull request ``#1763``). -- Added the ``JSONIFY_MIMETYPE`` configuration variable (pull request ``#1728``). -- Exceptions during teardown handling will no longer leave bad application - contexts lingering around. - -Version 0.10.2 --------------- - -(bugfix release, release date to be announced) - -- Fixed broken `test_appcontext_signals()` test case. -- Raise an :exc:`AttributeError` in :func:`flask.helpers.find_package` with a - useful message explaining why it is raised when a PEP 302 import hook is used - without an `is_package()` method. -- Fixed an issue causing exceptions raised before entering a request or app - context to be passed to teardown handlers. -- Fixed an issue with query parameters getting removed from requests in - the test client when absolute URLs were requested. -- Made `@before_first_request` into a decorator as intended. -- Fixed an etags bug when sending a file streams with a name. -- Fixed `send_from_directory` not expanding to the application root path - correctly. -- Changed logic of before first request handlers to flip the flag after - invoking. This will allow some uses that are potentially dangerous but - should probably be permitted. -- Fixed Python 3 bug when a handler from `app.url_build_error_handlers` - reraises the `BuildError`. - -Version 0.10.1 --------------- - -(bugfix release, released on June 14th 2013) - -- Fixed an issue where ``|tojson`` was not quoting single quotes which - made the filter not work properly in HTML attributes. Now it's - possible to use that filter in single quoted attributes. This should - make using that filter with angular.js easier. -- Added support for byte strings back to the session system. This broke - compatibility with the common case of people putting binary data for - token verification into the session. -- Fixed an issue where registering the same method twice for the same endpoint - would trigger an exception incorrectly. - -Version 0.10 ------------- - -Released on June 13th 2013, codename Limoncello. - -- Changed default cookie serialization format from pickle to JSON to - limit the impact an attacker can do if the secret key leaks. See - :ref:`upgrading-to-010` for more information. -- Added ``template_test`` methods in addition to the already existing - ``template_filter`` method family. -- Added ``template_global`` methods in addition to the already existing - ``template_filter`` method family. -- Set the content-length header for x-sendfile. -- ``tojson`` filter now does not escape script blocks in HTML5 parsers. -- ``tojson`` used in templates is now safe by default due. This was - allowed due to the different escaping behavior. -- Flask will now raise an error if you attempt to register a new function - on an already used endpoint. -- Added wrapper module around simplejson and added default serialization - of datetime objects. This allows much easier customization of how - JSON is handled by Flask or any Flask extension. -- Removed deprecated internal ``flask.session`` module alias. Use - ``flask.sessions`` instead to get the session module. This is not to - be confused with ``flask.session`` the session proxy. -- Templates can now be rendered without request context. The behavior is - slightly different as the ``request``, ``session`` and ``g`` objects - will not be available and blueprint's context processors are not - called. -- The config object is now available to the template as a real global and - not through a context processor which makes it available even in imported - templates by default. -- Added an option to generate non-ascii encoded JSON which should result - in less bytes being transmitted over the network. It's disabled by - default to not cause confusion with existing libraries that might expect - ``flask.json.dumps`` to return bytestrings by default. -- ``flask.g`` is now stored on the app context instead of the request - context. -- ``flask.g`` now gained a ``get()`` method for not erroring out on non - existing items. -- ``flask.g`` now can be used with the ``in`` operator to see what's defined - and it now is iterable and will yield all attributes stored. -- ``flask.Flask.request_globals_class`` got renamed to - ``flask.Flask.app_ctx_globals_class`` which is a better name to what it - does since 0.10. -- `request`, `session` and `g` are now also added as proxies to the template - context which makes them available in imported templates. One has to be - very careful with those though because usage outside of macros might - cause caching. -- Flask will no longer invoke the wrong error handlers if a proxy - exception is passed through. -- Added a workaround for chrome's cookies in localhost not working - as intended with domain names. -- Changed logic for picking defaults for cookie values from sessions - to work better with Google Chrome. -- Added `message_flashed` signal that simplifies flashing testing. -- Added support for copying of request contexts for better working with - greenlets. -- Removed custom JSON HTTP exception subclasses. If you were relying on them - you can reintroduce them again yourself trivially. Using them however is - strongly discouraged as the interface was flawed. -- Python requirements changed: requiring Python 2.6 or 2.7 now to prepare - for Python 3.3 port. -- Changed how the teardown system is informed about exceptions. This is now - more reliable in case something handles an exception halfway through - the error handling process. -- Request context preservation in debug mode now keeps the exception - information around which means that teardown handlers are able to - distinguish error from success cases. -- Added the ``JSONIFY_PRETTYPRINT_REGULAR`` configuration variable. -- Flask now orders JSON keys by default to not trash HTTP caches due to - different hash seeds between different workers. -- Added `appcontext_pushed` and `appcontext_popped` signals. -- The builtin run method now takes the ``SERVER_NAME`` into account when - picking the default port to run on. -- Added `flask.request.get_json()` as a replacement for the old - `flask.request.json` property. - -Version 0.9 ------------ - -Released on July 1st 2012, codename Campari. - -- The :func:`flask.Request.on_json_loading_failed` now returns a JSON formatted - response by default. -- The :func:`flask.url_for` function now can generate anchors to the - generated links. -- The :func:`flask.url_for` function now can also explicitly generate - URL rules specific to a given HTTP method. -- Logger now only returns the debug log setting if it was not set - explicitly. -- Unregister a circular dependency between the WSGI environment and - the request object when shutting down the request. This means that - environ ``werkzeug.request`` will be ``None`` after the response was - returned to the WSGI server but has the advantage that the garbage - collector is not needed on CPython to tear down the request unless - the user created circular dependencies themselves. -- Session is now stored after callbacks so that if the session payload - is stored in the session you can still modify it in an after - request callback. -- The :class:`flask.Flask` class will avoid importing the provided import name - if it can (the required first parameter), to benefit tools which build Flask - instances programmatically. The Flask class will fall back to using import - on systems with custom module hooks, e.g. Google App Engine, or when the - import name is inside a zip archive (usually a .egg) prior to Python 2.7. -- Blueprints now have a decorator to add custom template filters application - wide, :meth:`flask.Blueprint.app_template_filter`. -- The Flask and Blueprint classes now have a non-decorator method for adding - custom template filters application wide, - :meth:`flask.Flask.add_template_filter` and - :meth:`flask.Blueprint.add_app_template_filter`. -- The :func:`flask.get_flashed_messages` function now allows rendering flashed - message categories in separate blocks, through a ``category_filter`` - argument. -- The :meth:`flask.Flask.run` method now accepts ``None`` for `host` and `port` - arguments, using default values when ``None``. This allows for calling run - using configuration values, e.g. ``app.run(app.config.get('MYHOST'), - app.config.get('MYPORT'))``, with proper behavior whether or not a config - file is provided. -- The :meth:`flask.render_template` method now accepts a either an iterable of - template names or a single template name. Previously, it only accepted a - single template name. On an iterable, the first template found is rendered. -- Added :meth:`flask.Flask.app_context` which works very similar to the - request context but only provides access to the current application. This - also adds support for URL generation without an active request context. -- View functions can now return a tuple with the first instance being an - instance of :class:`flask.Response`. This allows for returning - ``jsonify(error="error msg"), 400`` from a view function. -- :class:`~flask.Flask` and :class:`~flask.Blueprint` now provide a - :meth:`~flask.Flask.get_send_file_max_age` hook for subclasses to override - behavior of serving static files from Flask when using - :meth:`flask.Flask.send_static_file` (used for the default static file - handler) and :func:`~flask.helpers.send_file`. This hook is provided a - filename, which for example allows changing cache controls by file extension. - The default max-age for `send_file` and static files can be configured - through a new ``SEND_FILE_MAX_AGE_DEFAULT`` configuration variable, which is - used in the default `get_send_file_max_age` implementation. -- Fixed an assumption in sessions implementation which could break message - flashing on sessions implementations which use external storage. -- Changed the behavior of tuple return values from functions. They are no - longer arguments to the response object, they now have a defined meaning. -- Added :attr:`flask.Flask.request_globals_class` to allow a specific class to - be used on creation of the :data:`~flask.g` instance of each request. -- Added `required_methods` attribute to view functions to force-add methods - on registration. -- Added :func:`flask.after_this_request`. -- Added :func:`flask.stream_with_context` and the ability to push contexts - multiple times without producing unexpected behavior. - -Version 0.8.1 -------------- - -Bugfix release, released on July 1st 2012 - -- Fixed an issue with the undocumented `flask.session` module to not - work properly on Python 2.5. It should not be used but did cause - some problems for package managers. - -Version 0.8 ------------ - -Released on September 29th 2011, codename Rakija - -- Refactored session support into a session interface so that - the implementation of the sessions can be changed without - having to override the Flask class. -- Empty session cookies are now deleted properly automatically. -- View functions can now opt out of getting the automatic - OPTIONS implementation. -- HTTP exceptions and Bad Request errors can now be trapped so that they - show up normally in the traceback. -- Flask in debug mode is now detecting some common problems and tries to - warn you about them. -- Flask in debug mode will now complain with an assertion error if a view - was attached after the first request was handled. This gives earlier - feedback when users forget to import view code ahead of time. -- Added the ability to register callbacks that are only triggered once at - the beginning of the first request. (:meth:`Flask.before_first_request`) -- Malformed JSON data will now trigger a bad request HTTP exception instead - of a value error which usually would result in a 500 internal server - error if not handled. This is a backwards incompatible change. -- Applications now not only have a root path where the resources and modules - are located but also an instance path which is the designated place to - drop files that are modified at runtime (uploads etc.). Also this is - conceptually only instance depending and outside version control so it's - the perfect place to put configuration files etc. For more information - see :ref:`instance-folders`. -- Added the ``APPLICATION_ROOT`` configuration variable. -- Implemented :meth:`~flask.testing.TestClient.session_transaction` to - easily modify sessions from the test environment. -- Refactored test client internally. The ``APPLICATION_ROOT`` configuration - variable as well as ``SERVER_NAME`` are now properly used by the test client - as defaults. -- Added :attr:`flask.views.View.decorators` to support simpler decorating of - pluggable (class-based) views. -- Fixed an issue where the test client if used with the "with" statement did not - trigger the execution of the teardown handlers. -- Added finer control over the session cookie parameters. -- HEAD requests to a method view now automatically dispatch to the `get` - method if no handler was implemented. -- Implemented the virtual :mod:`flask.ext` package to import extensions from. -- The context preservation on exceptions is now an integral component of - Flask itself and no longer of the test client. This cleaned up some - internal logic and lowers the odds of runaway request contexts in unittests. - -Version 0.7.3 -------------- - -Bugfix release, release date to be decided - -- Fixed the Jinja2 environment's list_templates method not returning the - correct names when blueprints or modules were involved. - -Version 0.7.2 -------------- - -Bugfix release, released on July 6th 2011 - -- Fixed an issue with URL processors not properly working on - blueprints. - -Version 0.7.1 -------------- - -Bugfix release, released on June 29th 2011 - -- Added missing future import that broke 2.5 compatibility. -- Fixed an infinite redirect issue with blueprints. - -Version 0.7 ------------ - -Released on June 28th 2011, codename Grappa - -- Added :meth:`~flask.Flask.make_default_options_response` - which can be used by subclasses to alter the default - behavior for ``OPTIONS`` responses. -- Unbound locals now raise a proper :exc:`RuntimeError` instead - of an :exc:`AttributeError`. -- Mimetype guessing and etag support based on file objects is now - deprecated for :func:`flask.send_file` because it was unreliable. - Pass filenames instead or attach your own etags and provide a - proper mimetype by hand. -- Static file handling for modules now requires the name of the - static folder to be supplied explicitly. The previous autodetection - was not reliable and caused issues on Google's App Engine. Until - 1.0 the old behavior will continue to work but issue dependency - warnings. -- fixed a problem for Flask to run on jython. -- added a ``PROPAGATE_EXCEPTIONS`` configuration variable that can be - used to flip the setting of exception propagation which previously - was linked to ``DEBUG`` alone and is now linked to either ``DEBUG`` or - ``TESTING``. -- Flask no longer internally depends on rules being added through the - `add_url_rule` function and can now also accept regular werkzeug - rules added to the url map. -- Added an `endpoint` method to the flask application object which - allows one to register a callback to an arbitrary endpoint with - a decorator. -- Use Last-Modified for static file sending instead of Date which - was incorrectly introduced in 0.6. -- Added `create_jinja_loader` to override the loader creation process. -- Implemented a silent flag for `config.from_pyfile`. -- Added `teardown_request` decorator, for functions that should run at the end - of a request regardless of whether an exception occurred. Also the behavior - for `after_request` was changed. It's now no longer executed when an exception - is raised. See :ref:`upgrading-to-new-teardown-handling` -- Implemented :func:`flask.has_request_context` -- Deprecated `init_jinja_globals`. Override the - :meth:`~flask.Flask.create_jinja_environment` method instead to - achieve the same functionality. -- Added :func:`flask.safe_join` -- The automatic JSON request data unpacking now looks at the charset - mimetype parameter. -- Don't modify the session on :func:`flask.get_flashed_messages` if there - are no messages in the session. -- `before_request` handlers are now able to abort requests with errors. -- it is not possible to define user exception handlers. That way you can - provide custom error messages from a central hub for certain errors that - might occur during request processing (for instance database connection - errors, timeouts from remote resources etc.). -- Blueprints can provide blueprint specific error handlers. -- Implemented generic :ref:`views` (class-based views). - -Version 0.6.1 -------------- - -Bugfix release, released on December 31st 2010 - -- Fixed an issue where the default ``OPTIONS`` response was - not exposing all valid methods in the ``Allow`` header. -- Jinja2 template loading syntax now allows "./" in front of - a template load path. Previously this caused issues with - module setups. -- Fixed an issue where the subdomain setting for modules was - ignored for the static folder. -- Fixed a security problem that allowed clients to download arbitrary files - if the host server was a windows based operating system and the client - uses backslashes to escape the directory the files where exposed from. - -Version 0.6 ------------ - -Released on July 27th 2010, codename Whisky - -- after request functions are now called in reverse order of - registration. -- OPTIONS is now automatically implemented by Flask unless the - application explicitly adds 'OPTIONS' as method to the URL rule. - In this case no automatic OPTIONS handling kicks in. -- static rules are now even in place if there is no static folder - for the module. This was implemented to aid GAE which will - remove the static folder if it's part of a mapping in the .yml - file. -- the :attr:`~flask.Flask.config` is now available in the templates - as `config`. -- context processors will no longer override values passed directly - to the render function. -- added the ability to limit the incoming request data with the - new ``MAX_CONTENT_LENGTH`` configuration value. -- the endpoint for the :meth:`flask.Module.add_url_rule` method - is now optional to be consistent with the function of the - same name on the application object. -- added a :func:`flask.make_response` function that simplifies - creating response object instances in views. -- added signalling support based on blinker. This feature is currently - optional and supposed to be used by extensions and applications. If - you want to use it, make sure to have `blinker`_ installed. -- refactored the way URL adapters are created. This process is now - fully customizable with the :meth:`~flask.Flask.create_url_adapter` - method. -- modules can now register for a subdomain instead of just an URL - prefix. This makes it possible to bind a whole module to a - configurable subdomain. - -.. _blinker: https://pypi.python.org/pypi/blinker - -Version 0.5.2 -------------- - -Bugfix Release, released on July 15th 2010 - -- fixed another issue with loading templates from directories when - modules were used. - -Version 0.5.1 -------------- - -Bugfix Release, released on July 6th 2010 - -- fixes an issue with template loading from directories when modules - where used. - -Version 0.5 ------------ - -Released on July 6th 2010, codename Calvados - -- fixed a bug with subdomains that was caused by the inability to - specify the server name. The server name can now be set with - the ``SERVER_NAME`` config key. This key is now also used to set - the session cookie cross-subdomain wide. -- autoescaping is no longer active for all templates. Instead it - is only active for ``.html``, ``.htm``, ``.xml`` and ``.xhtml``. - Inside templates this behavior can be changed with the - ``autoescape`` tag. -- refactored Flask internally. It now consists of more than a - single file. -- :func:`flask.send_file` now emits etags and has the ability to - do conditional responses builtin. -- (temporarily) dropped support for zipped applications. This was a - rarely used feature and led to some confusing behavior. -- added support for per-package template and static-file directories. -- removed support for `create_jinja_loader` which is no longer used - in 0.5 due to the improved module support. -- added a helper function to expose files from any directory. - -Version 0.4 ------------ - -Released on June 18th 2010, codename Rakia - -- added the ability to register application wide error handlers - from modules. -- :meth:`~flask.Flask.after_request` handlers are now also invoked - if the request dies with an exception and an error handling page - kicks in. -- test client has not the ability to preserve the request context - for a little longer. This can also be used to trigger custom - requests that do not pop the request stack for testing. -- because the Python standard library caches loggers, the name of - the logger is configurable now to better support unittests. -- added ``TESTING`` switch that can activate unittesting helpers. -- the logger switches to ``DEBUG`` mode now if debug is enabled. - -Version 0.3.1 -------------- - -Bugfix release, released on May 28th 2010 - -- fixed a error reporting bug with :meth:`flask.Config.from_envvar` -- removed some unused code from flask -- release does no longer include development leftover files (.git - folder for themes, built documentation in zip and pdf file and - some .pyc files) - -Version 0.3 ------------ - -Released on May 28th 2010, codename Schnaps - -- added support for categories for flashed messages. -- the application now configures a :class:`logging.Handler` and will - log request handling exceptions to that logger when not in debug - mode. This makes it possible to receive mails on server errors - for example. -- added support for context binding that does not require the use of - the with statement for playing in the console. -- the request context is now available within the with statement making - it possible to further push the request context or pop it. -- added support for configurations. - -Version 0.2 ------------ - -Released on May 12th 2010, codename Jägermeister - -- various bugfixes -- integrated JSON support -- added :func:`~flask.get_template_attribute` helper function. -- :meth:`~flask.Flask.add_url_rule` can now also register a - view function. -- refactored internal request dispatching. -- server listens on 127.0.0.1 by default now to fix issues with chrome. -- added external URL support. -- added support for :func:`~flask.send_file` -- module support and internal request handling refactoring - to better support pluggable applications. -- sessions can be set to be permanent now on a per-session basis. -- better error reporting on missing secret keys. -- added support for Google Appengine. - -Version 0.1 ------------ - -First public preview release. diff -Nru flask-0.12.2/CHANGES.rst flask-1.0.2/CHANGES.rst --- flask-0.12.2/CHANGES.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/CHANGES.rst 2018-05-02 14:16:05.000000000 +0000 @@ -0,0 +1,904 @@ +.. currentmodule:: flask + +Flask Changelog +=============== + + +Version 1.0.2 +------------- + +Released on May 2nd 2018 + +- Fix more backwards compatibility issues with merging slashes between + a blueprint prefix and route. (`#2748`_) +- Fix error with ``flask routes`` command when there are no routes. + (`#2751`_) + +.. _#2748: https://github.com/pallets/flask/pull/2748 +.. _#2751: https://github.com/pallets/flask/issues/2751 + + +Version 1.0.1 +------------- + +Released on April 29th 2018 + +- Fix registering partials (with no ``__name__``) as view functions. + (`#2730`_) +- Don't treat lists returned from view functions the same as tuples. + Only tuples are interpreted as response data. (`#2736`_) +- Extra slashes between a blueprint's ``url_prefix`` and a route URL + are merged. This fixes some backwards compatibility issues with the + change in 1.0. (`#2731`_, `#2742`_) +- Only trap ``BadRequestKeyError`` errors in debug mode, not all + ``BadRequest`` errors. This allows ``abort(400)`` to continue + working as expected. (`#2735`_) +- The ``FLASK_SKIP_DOTENV`` environment variable can be set to ``1`` + to skip automatically loading dotenv files. (`#2722`_) + +.. _#2722: https://github.com/pallets/flask/issues/2722 +.. _#2730: https://github.com/pallets/flask/pull/2730 +.. _#2731: https://github.com/pallets/flask/issues/2731 +.. _#2735: https://github.com/pallets/flask/issues/2735 +.. _#2736: https://github.com/pallets/flask/issues/2736 +.. _#2742: https://github.com/pallets/flask/issues/2742 + + +Version 1.0 +----------- + +Released on April 26th 2018 + +- **Python 2.6 and 3.3 are no longer supported.** (`pallets/meta#24`_) +- Bump minimum dependency versions to the latest stable versions: + Werkzeug >= 0.14, Jinja >= 2.10, itsdangerous >= 0.24, Click >= 5.1. + (`#2586`_) +- Skip :meth:`app.run ` when a Flask application is run + from the command line. This avoids some behavior that was confusing + to debug. +- Change the default for :data:`JSONIFY_PRETTYPRINT_REGULAR` to + ``False``. :func:`~json.jsonify` returns a compact format by + default, and an indented format in debug mode. (`#2193`_) +- :meth:`Flask.__init__ ` accepts the ``host_matching`` + argument and sets it on :attr:`~Flask.url_map`. (`#1559`_) +- :meth:`Flask.__init__ ` accepts the ``static_host`` argument + and passes it as the ``host`` argument when defining the static + route. (`#1559`_) +- :func:`send_file` supports Unicode in ``attachment_filename``. + (`#2223`_) +- Pass ``_scheme`` argument from :func:`url_for` to + :meth:`~Flask.handle_url_build_error`. (`#2017`_) +- :meth:`~Flask.add_url_rule` accepts the + ``provide_automatic_options`` argument to disable adding the + ``OPTIONS`` method. (`#1489`_) +- :class:`~views.MethodView` subclasses inherit method handlers from + base classes. (`#1936`_) +- Errors caused while opening the session at the beginning of the + request are handled by the app's error handlers. (`#2254`_) +- Blueprints gained :attr:`~Blueprint.json_encoder` and + :attr:`~Blueprint.json_decoder` attributes to override the app's + encoder and decoder. (`#1898`_) +- :meth:`Flask.make_response` raises ``TypeError`` instead of + ``ValueError`` for bad response types. The error messages have been + improved to describe why the type is invalid. (`#2256`_) +- Add ``routes`` CLI command to output routes registered on the + application. (`#2259`_) +- Show warning when session cookie domain is a bare hostname or an IP + address, as these may not behave properly in some browsers, such as + Chrome. (`#2282`_) +- Allow IP address as exact session cookie domain. (`#2282`_) +- ``SESSION_COOKIE_DOMAIN`` is set if it is detected through + ``SERVER_NAME``. (`#2282`_) +- Auto-detect zero-argument app factory called ``create_app`` or + ``make_app`` from ``FLASK_APP``. (`#2297`_) +- Factory functions are not required to take a ``script_info`` + parameter to work with the ``flask`` command. If they take a single + parameter or a parameter named ``script_info``, the + :class:`~cli.ScriptInfo` object will be passed. (`#2319`_) +- ``FLASK_APP`` can be set to an app factory, with arguments if + needed, for example ``FLASK_APP=myproject.app:create_app('dev')``. + (`#2326`_) +- ``FLASK_APP`` can point to local packages that are not installed in + editable mode, although ``pip install -e`` is still preferred. + (`#2414`_) +- The :class:`~views.View` class attribute + :attr:`~views.View.provide_automatic_options` is set in + :meth:`~views.View.as_view`, to be detected by + :meth:`~Flask.add_url_rule`. (`#2316`_) +- Error handling will try handlers registered for ``blueprint, code``, + ``app, code``, ``blueprint, exception``, ``app, exception``. + (`#2314`_) +- ``Cookie`` is added to the response's ``Vary`` header if the session + is accessed at all during the request (and not deleted). (`#2288`_) +- :meth:`~Flask.test_request_context` accepts ``subdomain`` and + ``url_scheme`` arguments for use when building the base URL. + (`#1621`_) +- Set :data:`APPLICATION_ROOT` to ``'/'`` by default. This was already + the implicit default when it was set to ``None``. +- :data:`TRAP_BAD_REQUEST_ERRORS` is enabled by default in debug mode. + ``BadRequestKeyError`` has a message with the bad key in debug mode + instead of the generic bad request message. (`#2348`_) +- Allow registering new tags with + :class:`~json.tag.TaggedJSONSerializer` to support storing other + types in the session cookie. (`#2352`_) +- Only open the session if the request has not been pushed onto the + context stack yet. This allows :func:`~stream_with_context` + generators to access the same session that the containing view uses. + (`#2354`_) +- Add ``json`` keyword argument for the test client request methods. + This will dump the given object as JSON and set the appropriate + content type. (`#2358`_) +- Extract JSON handling to a mixin applied to both the + :class:`Request` and :class:`Response` classes. This adds the + :meth:`~Response.is_json` and :meth:`~Response.get_json` methods to + the response to make testing JSON response much easier. (`#2358`_) +- Removed error handler caching because it caused unexpected results + for some exception inheritance hierarchies. Register handlers + explicitly for each exception if you want to avoid traversing the + MRO. (`#2362`_) +- Fix incorrect JSON encoding of aware, non-UTC datetimes. (`#2374`_) +- Template auto reloading will honor debug mode even even if + :attr:`~Flask.jinja_env` was already accessed. (`#2373`_) +- The following old deprecated code was removed. (`#2385`_) + + - ``flask.ext`` - import extensions directly by their name instead + of through the ``flask.ext`` namespace. For example, + ``import flask.ext.sqlalchemy`` becomes + ``import flask_sqlalchemy``. + - ``Flask.init_jinja_globals`` - extend + :meth:`Flask.create_jinja_environment` instead. + - ``Flask.error_handlers`` - tracked by + :attr:`Flask.error_handler_spec`, use :meth:`Flask.errorhandler` + to register handlers. + - ``Flask.request_globals_class`` - use + :attr:`Flask.app_ctx_globals_class` instead. + - ``Flask.static_path`` - use :attr:`Flask.static_url_path` + instead. + - ``Request.module`` - use :attr:`Request.blueprint` instead. + +- The :attr:`Request.json` property is no longer deprecated. + (`#1421`_) +- Support passing a :class:`~werkzeug.test.EnvironBuilder` or + ``dict`` to :meth:`test_client.open `. + (`#2412`_) +- The ``flask`` command and :meth:`Flask.run` will load environment + variables from ``.env`` and ``.flaskenv`` files if python-dotenv is + installed. (`#2416`_) +- When passing a full URL to the test client, the scheme in the URL is + used instead of :data:`PREFERRED_URL_SCHEME`. (`#2430`_) +- :attr:`Flask.logger` has been simplified. ``LOGGER_NAME`` and + ``LOGGER_HANDLER_POLICY`` config was removed. The logger is always + named ``flask.app``. The level is only set on first access, it + doesn't check :attr:`Flask.debug` each time. Only one format is + used, not different ones depending on :attr:`Flask.debug`. No + handlers are removed, and a handler is only added if no handlers are + already configured. (`#2436`_) +- Blueprint view function names may not contain dots. (`#2450`_) +- Fix a ``ValueError`` caused by invalid ``Range`` requests in some + cases. (`#2526`_) +- The development server uses threads by default. (`#2529`_) +- Loading config files with ``silent=True`` will ignore + :data:`~errno.ENOTDIR` errors. (`#2581`_) +- Pass ``--cert`` and ``--key`` options to ``flask run`` to run the + development server over HTTPS. (`#2606`_) +- Added :data:`SESSION_COOKIE_SAMESITE` to control the ``SameSite`` + attribute on the session cookie. (`#2607`_) +- Added :meth:`~flask.Flask.test_cli_runner` to create a Click runner + that can invoke Flask CLI commands for testing. (`#2636`_) +- Subdomain matching is disabled by default and setting + :data:`SERVER_NAME` does not implicily enable it. It can be enabled + by passing ``subdomain_matching=True`` to the ``Flask`` constructor. + (`#2635`_) +- A single trailing slash is stripped from the blueprint + ``url_prefix`` when it is registered with the app. (`#2629`_) +- :meth:`Request.get_json` doesn't cache the + result if parsing fails when ``silent`` is true. (`#2651`_) +- :func:`Request.get_json` no longer accepts arbitrary encodings. + Incoming JSON should be encoded using UTF-8 per :rfc:`8259`, but + Flask will autodetect UTF-8, -16, or -32. (`#2691`_) +- Added :data:`MAX_COOKIE_SIZE` and :attr:`Response.max_cookie_size` + to control when Werkzeug warns about large cookies that browsers may + ignore. (`#2693`_) +- Updated documentation theme to make docs look better in small + windows. (`#2709`_) +- Rewrote the tutorial docs and example project to take a more + structured approach to help new users avoid common pitfalls. + (`#2676`_) + +.. _pallets/meta#24: https://github.com/pallets/meta/issues/24 +.. _#1421: https://github.com/pallets/flask/issues/1421 +.. _#1489: https://github.com/pallets/flask/pull/1489 +.. _#1559: https://github.com/pallets/flask/issues/1559 +.. _#1621: https://github.com/pallets/flask/pull/1621 +.. _#1898: https://github.com/pallets/flask/pull/1898 +.. _#1936: https://github.com/pallets/flask/pull/1936 +.. _#2017: https://github.com/pallets/flask/pull/2017 +.. _#2193: https://github.com/pallets/flask/pull/2193 +.. _#2223: https://github.com/pallets/flask/pull/2223 +.. _#2254: https://github.com/pallets/flask/pull/2254 +.. _#2256: https://github.com/pallets/flask/pull/2256 +.. _#2259: https://github.com/pallets/flask/pull/2259 +.. _#2282: https://github.com/pallets/flask/pull/2282 +.. _#2288: https://github.com/pallets/flask/pull/2288 +.. _#2297: https://github.com/pallets/flask/pull/2297 +.. _#2314: https://github.com/pallets/flask/pull/2314 +.. _#2316: https://github.com/pallets/flask/pull/2316 +.. _#2319: https://github.com/pallets/flask/pull/2319 +.. _#2326: https://github.com/pallets/flask/pull/2326 +.. _#2348: https://github.com/pallets/flask/pull/2348 +.. _#2352: https://github.com/pallets/flask/pull/2352 +.. _#2354: https://github.com/pallets/flask/pull/2354 +.. _#2358: https://github.com/pallets/flask/pull/2358 +.. _#2362: https://github.com/pallets/flask/pull/2362 +.. _#2374: https://github.com/pallets/flask/pull/2374 +.. _#2373: https://github.com/pallets/flask/pull/2373 +.. _#2385: https://github.com/pallets/flask/issues/2385 +.. _#2412: https://github.com/pallets/flask/pull/2412 +.. _#2414: https://github.com/pallets/flask/pull/2414 +.. _#2416: https://github.com/pallets/flask/pull/2416 +.. _#2430: https://github.com/pallets/flask/pull/2430 +.. _#2436: https://github.com/pallets/flask/pull/2436 +.. _#2450: https://github.com/pallets/flask/pull/2450 +.. _#2526: https://github.com/pallets/flask/issues/2526 +.. _#2529: https://github.com/pallets/flask/pull/2529 +.. _#2586: https://github.com/pallets/flask/issues/2586 +.. _#2581: https://github.com/pallets/flask/pull/2581 +.. _#2606: https://github.com/pallets/flask/pull/2606 +.. _#2607: https://github.com/pallets/flask/pull/2607 +.. _#2636: https://github.com/pallets/flask/pull/2636 +.. _#2635: https://github.com/pallets/flask/pull/2635 +.. _#2629: https://github.com/pallets/flask/pull/2629 +.. _#2651: https://github.com/pallets/flask/issues/2651 +.. _#2676: https://github.com/pallets/flask/pull/2676 +.. _#2691: https://github.com/pallets/flask/pull/2691 +.. _#2693: https://github.com/pallets/flask/pull/2693 +.. _#2709: https://github.com/pallets/flask/pull/2709 + + +Version 0.12.4 +-------------- + +Released on April 29 2018 + +- Repackage 0.12.3 to fix package layout issue. (`#2728`_) + +.. _#2728: https://github.com/pallets/flask/issues/2728 + + +Version 0.12.3 +-------------- + +Released on April 26th 2018 + +- :func:`Request.get_json` no longer accepts arbitrary encodings. + Incoming JSON should be encoded using UTF-8 per :rfc:`8259`, but + Flask will autodetect UTF-8, -16, or -32. (`#2692`_) +- Fix a Python warning about imports when using ``python -m flask``. + (`#2666`_) +- Fix a ``ValueError`` caused by invalid ``Range`` requests in some + cases. + +.. _#2666: https://github.com/pallets/flask/issues/2666 +.. _#2692: https://github.com/pallets/flask/issues/2692 + + +Version 0.12.2 +-------------- + +Released on May 16 2017 + +- Fix a bug in `safe_join` on Windows. + +Version 0.12.1 +-------------- + +Bugfix release, released on March 31st 2017 + +- Prevent `flask run` from showing a NoAppException when an ImportError occurs + within the imported application module. +- Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix + ``#2118``. +- Use the ``SERVER_NAME`` config if it is present as default values for + ``app.run``. ``#2109``, ``#2152`` +- Call `ctx.auto_pop` with the exception object instead of `None`, in the + event that a `BaseException` such as `KeyboardInterrupt` is raised in a + request handler. + +Version 0.12 +------------ + +Released on December 21st 2016, codename Punsch. + +- the cli command now responds to `--version`. +- Mimetype guessing and ETag generation for file-like objects in ``send_file`` + has been removed, as per issue ``#104``. See pull request ``#1849``. +- Mimetype guessing in ``send_file`` now fails loudly and doesn't fall back to + ``application/octet-stream``. See pull request ``#1988``. +- Make ``flask.safe_join`` able to join multiple paths like ``os.path.join`` + (pull request ``#1730``). +- Revert a behavior change that made the dev server crash instead of returning + a Internal Server Error (pull request ``#2006``). +- Correctly invoke response handlers for both regular request dispatching as + well as error handlers. +- Disable logger propagation by default for the app logger. +- Add support for range requests in ``send_file``. +- ``app.test_client`` includes preset default environment, which can now be + directly set, instead of per ``client.get``. + +Version 0.11.2 +-------------- + +Bugfix release, unreleased + +- Fix crash when running under PyPy3, see pull request ``#1814``. + +Version 0.11.1 +-------------- + +Bugfix release, released on June 7th 2016. + +- Fixed a bug that prevented ``FLASK_APP=foobar/__init__.py`` from working. See + pull request ``#1872``. + +Version 0.11 +------------ + +Released on May 29th 2016, codename Absinthe. + +- Added support to serializing top-level arrays to :func:`flask.jsonify`. This + introduces a security risk in ancient browsers. See + :ref:`json-security` for details. +- Added before_render_template signal. +- Added `**kwargs` to :meth:`flask.Test.test_client` to support passing + additional keyword arguments to the constructor of + :attr:`flask.Flask.test_client_class`. +- Added ``SESSION_REFRESH_EACH_REQUEST`` config key that controls the + set-cookie behavior. If set to ``True`` a permanent session will be + refreshed each request and get their lifetime extended, if set to + ``False`` it will only be modified if the session actually modifies. + Non permanent sessions are not affected by this and will always + expire if the browser window closes. +- Made Flask support custom JSON mimetypes for incoming data. +- Added support for returning tuples in the form ``(response, headers)`` + from a view function. +- Added :meth:`flask.Config.from_json`. +- Added :attr:`flask.Flask.config_class`. +- Added :meth:`flask.Config.get_namespace`. +- Templates are no longer automatically reloaded outside of debug mode. This + can be configured with the new ``TEMPLATES_AUTO_RELOAD`` config key. +- Added a workaround for a limitation in Python 3.3's namespace loader. +- Added support for explicit root paths when using Python 3.3's namespace + packages. +- Added :command:`flask` and the ``flask.cli`` module to start the local + debug server through the click CLI system. This is recommended over the old + ``flask.run()`` method as it works faster and more reliable due to a + different design and also replaces ``Flask-Script``. +- Error handlers that match specific classes are now checked first, + thereby allowing catching exceptions that are subclasses of HTTP + exceptions (in ``werkzeug.exceptions``). This makes it possible + for an extension author to create exceptions that will by default + result in the HTTP error of their choosing, but may be caught with + a custom error handler if desired. +- Added :meth:`flask.Config.from_mapping`. +- Flask will now log by default even if debug is disabled. The log format is + now hardcoded but the default log handling can be disabled through the + ``LOGGER_HANDLER_POLICY`` configuration key. +- Removed deprecated module functionality. +- Added the ``EXPLAIN_TEMPLATE_LOADING`` config flag which when enabled will + instruct Flask to explain how it locates templates. This should help + users debug when the wrong templates are loaded. +- Enforce blueprint handling in the order they were registered for template + loading. +- Ported test suite to py.test. +- Deprecated ``request.json`` in favour of ``request.get_json()``. +- Add "pretty" and "compressed" separators definitions in jsonify() method. + Reduces JSON response size when JSONIFY_PRETTYPRINT_REGULAR=False by removing + unnecessary white space included by default after separators. +- JSON responses are now terminated with a newline character, because it is a + convention that UNIX text files end with a newline and some clients don't + deal well when this newline is missing. See + https://github.com/pallets/flask/pull/1262 -- this came up originally as a + part of https://github.com/kennethreitz/httpbin/issues/168 +- The automatically provided ``OPTIONS`` method is now correctly disabled if + the user registered an overriding rule with the lowercase-version + ``options`` (issue ``#1288``). +- ``flask.json.jsonify`` now supports the ``datetime.date`` type (pull request + ``#1326``). +- Don't leak exception info of already catched exceptions to context teardown + handlers (pull request ``#1393``). +- Allow custom Jinja environment subclasses (pull request ``#1422``). +- Updated extension dev guidelines. + +- ``flask.g`` now has ``pop()`` and ``setdefault`` methods. +- Turn on autoescape for ``flask.templating.render_template_string`` by default + (pull request ``#1515``). +- ``flask.ext`` is now deprecated (pull request ``#1484``). +- ``send_from_directory`` now raises BadRequest if the filename is invalid on + the server OS (pull request ``#1763``). +- Added the ``JSONIFY_MIMETYPE`` configuration variable (pull request ``#1728``). +- Exceptions during teardown handling will no longer leave bad application + contexts lingering around. + +Version 0.10.2 +-------------- + +(bugfix release, release date to be announced) + +- Fixed broken `test_appcontext_signals()` test case. +- Raise an :exc:`AttributeError` in :func:`flask.helpers.find_package` with a + useful message explaining why it is raised when a PEP 302 import hook is used + without an `is_package()` method. +- Fixed an issue causing exceptions raised before entering a request or app + context to be passed to teardown handlers. +- Fixed an issue with query parameters getting removed from requests in + the test client when absolute URLs were requested. +- Made `@before_first_request` into a decorator as intended. +- Fixed an etags bug when sending a file streams with a name. +- Fixed `send_from_directory` not expanding to the application root path + correctly. +- Changed logic of before first request handlers to flip the flag after + invoking. This will allow some uses that are potentially dangerous but + should probably be permitted. +- Fixed Python 3 bug when a handler from `app.url_build_error_handlers` + reraises the `BuildError`. + +Version 0.10.1 +-------------- + +(bugfix release, released on June 14th 2013) + +- Fixed an issue where ``|tojson`` was not quoting single quotes which + made the filter not work properly in HTML attributes. Now it's + possible to use that filter in single quoted attributes. This should + make using that filter with angular.js easier. +- Added support for byte strings back to the session system. This broke + compatibility with the common case of people putting binary data for + token verification into the session. +- Fixed an issue where registering the same method twice for the same endpoint + would trigger an exception incorrectly. + +Version 0.10 +------------ + +Released on June 13th 2013, codename Limoncello. + +- Changed default cookie serialization format from pickle to JSON to + limit the impact an attacker can do if the secret key leaks. See + :ref:`upgrading-to-010` for more information. +- Added ``template_test`` methods in addition to the already existing + ``template_filter`` method family. +- Added ``template_global`` methods in addition to the already existing + ``template_filter`` method family. +- Set the content-length header for x-sendfile. +- ``tojson`` filter now does not escape script blocks in HTML5 parsers. +- ``tojson`` used in templates is now safe by default due. This was + allowed due to the different escaping behavior. +- Flask will now raise an error if you attempt to register a new function + on an already used endpoint. +- Added wrapper module around simplejson and added default serialization + of datetime objects. This allows much easier customization of how + JSON is handled by Flask or any Flask extension. +- Removed deprecated internal ``flask.session`` module alias. Use + ``flask.sessions`` instead to get the session module. This is not to + be confused with ``flask.session`` the session proxy. +- Templates can now be rendered without request context. The behavior is + slightly different as the ``request``, ``session`` and ``g`` objects + will not be available and blueprint's context processors are not + called. +- The config object is now available to the template as a real global and + not through a context processor which makes it available even in imported + templates by default. +- Added an option to generate non-ascii encoded JSON which should result + in less bytes being transmitted over the network. It's disabled by + default to not cause confusion with existing libraries that might expect + ``flask.json.dumps`` to return bytestrings by default. +- ``flask.g`` is now stored on the app context instead of the request + context. +- ``flask.g`` now gained a ``get()`` method for not erroring out on non + existing items. +- ``flask.g`` now can be used with the ``in`` operator to see what's defined + and it now is iterable and will yield all attributes stored. +- ``flask.Flask.request_globals_class`` got renamed to + ``flask.Flask.app_ctx_globals_class`` which is a better name to what it + does since 0.10. +- `request`, `session` and `g` are now also added as proxies to the template + context which makes them available in imported templates. One has to be + very careful with those though because usage outside of macros might + cause caching. +- Flask will no longer invoke the wrong error handlers if a proxy + exception is passed through. +- Added a workaround for chrome's cookies in localhost not working + as intended with domain names. +- Changed logic for picking defaults for cookie values from sessions + to work better with Google Chrome. +- Added `message_flashed` signal that simplifies flashing testing. +- Added support for copying of request contexts for better working with + greenlets. +- Removed custom JSON HTTP exception subclasses. If you were relying on them + you can reintroduce them again yourself trivially. Using them however is + strongly discouraged as the interface was flawed. +- Python requirements changed: requiring Python 2.6 or 2.7 now to prepare + for Python 3.3 port. +- Changed how the teardown system is informed about exceptions. This is now + more reliable in case something handles an exception halfway through + the error handling process. +- Request context preservation in debug mode now keeps the exception + information around which means that teardown handlers are able to + distinguish error from success cases. +- Added the ``JSONIFY_PRETTYPRINT_REGULAR`` configuration variable. +- Flask now orders JSON keys by default to not trash HTTP caches due to + different hash seeds between different workers. +- Added `appcontext_pushed` and `appcontext_popped` signals. +- The builtin run method now takes the ``SERVER_NAME`` into account when + picking the default port to run on. +- Added `flask.request.get_json()` as a replacement for the old + `flask.request.json` property. + +Version 0.9 +----------- + +Released on July 1st 2012, codename Campari. + +- The :func:`flask.Request.on_json_loading_failed` now returns a JSON formatted + response by default. +- The :func:`flask.url_for` function now can generate anchors to the + generated links. +- The :func:`flask.url_for` function now can also explicitly generate + URL rules specific to a given HTTP method. +- Logger now only returns the debug log setting if it was not set + explicitly. +- Unregister a circular dependency between the WSGI environment and + the request object when shutting down the request. This means that + environ ``werkzeug.request`` will be ``None`` after the response was + returned to the WSGI server but has the advantage that the garbage + collector is not needed on CPython to tear down the request unless + the user created circular dependencies themselves. +- Session is now stored after callbacks so that if the session payload + is stored in the session you can still modify it in an after + request callback. +- The :class:`flask.Flask` class will avoid importing the provided import name + if it can (the required first parameter), to benefit tools which build Flask + instances programmatically. The Flask class will fall back to using import + on systems with custom module hooks, e.g. Google App Engine, or when the + import name is inside a zip archive (usually a .egg) prior to Python 2.7. +- Blueprints now have a decorator to add custom template filters application + wide, :meth:`flask.Blueprint.app_template_filter`. +- The Flask and Blueprint classes now have a non-decorator method for adding + custom template filters application wide, + :meth:`flask.Flask.add_template_filter` and + :meth:`flask.Blueprint.add_app_template_filter`. +- The :func:`flask.get_flashed_messages` function now allows rendering flashed + message categories in separate blocks, through a ``category_filter`` + argument. +- The :meth:`flask.Flask.run` method now accepts ``None`` for `host` and `port` + arguments, using default values when ``None``. This allows for calling run + using configuration values, e.g. ``app.run(app.config.get('MYHOST'), + app.config.get('MYPORT'))``, with proper behavior whether or not a config + file is provided. +- The :meth:`flask.render_template` method now accepts a either an iterable of + template names or a single template name. Previously, it only accepted a + single template name. On an iterable, the first template found is rendered. +- Added :meth:`flask.Flask.app_context` which works very similar to the + request context but only provides access to the current application. This + also adds support for URL generation without an active request context. +- View functions can now return a tuple with the first instance being an + instance of :class:`flask.Response`. This allows for returning + ``jsonify(error="error msg"), 400`` from a view function. +- :class:`~flask.Flask` and :class:`~flask.Blueprint` now provide a + :meth:`~flask.Flask.get_send_file_max_age` hook for subclasses to override + behavior of serving static files from Flask when using + :meth:`flask.Flask.send_static_file` (used for the default static file + handler) and :func:`~flask.helpers.send_file`. This hook is provided a + filename, which for example allows changing cache controls by file extension. + The default max-age for `send_file` and static files can be configured + through a new ``SEND_FILE_MAX_AGE_DEFAULT`` configuration variable, which is + used in the default `get_send_file_max_age` implementation. +- Fixed an assumption in sessions implementation which could break message + flashing on sessions implementations which use external storage. +- Changed the behavior of tuple return values from functions. They are no + longer arguments to the response object, they now have a defined meaning. +- Added :attr:`flask.Flask.request_globals_class` to allow a specific class to + be used on creation of the :data:`~flask.g` instance of each request. +- Added `required_methods` attribute to view functions to force-add methods + on registration. +- Added :func:`flask.after_this_request`. +- Added :func:`flask.stream_with_context` and the ability to push contexts + multiple times without producing unexpected behavior. + +Version 0.8.1 +------------- + +Bugfix release, released on July 1st 2012 + +- Fixed an issue with the undocumented `flask.session` module to not + work properly on Python 2.5. It should not be used but did cause + some problems for package managers. + +Version 0.8 +----------- + +Released on September 29th 2011, codename Rakija + +- Refactored session support into a session interface so that + the implementation of the sessions can be changed without + having to override the Flask class. +- Empty session cookies are now deleted properly automatically. +- View functions can now opt out of getting the automatic + OPTIONS implementation. +- HTTP exceptions and Bad Request errors can now be trapped so that they + show up normally in the traceback. +- Flask in debug mode is now detecting some common problems and tries to + warn you about them. +- Flask in debug mode will now complain with an assertion error if a view + was attached after the first request was handled. This gives earlier + feedback when users forget to import view code ahead of time. +- Added the ability to register callbacks that are only triggered once at + the beginning of the first request. (:meth:`Flask.before_first_request`) +- Malformed JSON data will now trigger a bad request HTTP exception instead + of a value error which usually would result in a 500 internal server + error if not handled. This is a backwards incompatible change. +- Applications now not only have a root path where the resources and modules + are located but also an instance path which is the designated place to + drop files that are modified at runtime (uploads etc.). Also this is + conceptually only instance depending and outside version control so it's + the perfect place to put configuration files etc. For more information + see :ref:`instance-folders`. +- Added the ``APPLICATION_ROOT`` configuration variable. +- Implemented :meth:`~flask.testing.TestClient.session_transaction` to + easily modify sessions from the test environment. +- Refactored test client internally. The ``APPLICATION_ROOT`` configuration + variable as well as ``SERVER_NAME`` are now properly used by the test client + as defaults. +- Added :attr:`flask.views.View.decorators` to support simpler decorating of + pluggable (class-based) views. +- Fixed an issue where the test client if used with the "with" statement did not + trigger the execution of the teardown handlers. +- Added finer control over the session cookie parameters. +- HEAD requests to a method view now automatically dispatch to the `get` + method if no handler was implemented. +- Implemented the virtual :mod:`flask.ext` package to import extensions from. +- The context preservation on exceptions is now an integral component of + Flask itself and no longer of the test client. This cleaned up some + internal logic and lowers the odds of runaway request contexts in unittests. + +Version 0.7.3 +------------- + +Bugfix release, release date to be decided + +- Fixed the Jinja2 environment's list_templates method not returning the + correct names when blueprints or modules were involved. + +Version 0.7.2 +------------- + +Bugfix release, released on July 6th 2011 + +- Fixed an issue with URL processors not properly working on + blueprints. + +Version 0.7.1 +------------- + +Bugfix release, released on June 29th 2011 + +- Added missing future import that broke 2.5 compatibility. +- Fixed an infinite redirect issue with blueprints. + +Version 0.7 +----------- + +Released on June 28th 2011, codename Grappa + +- Added :meth:`~flask.Flask.make_default_options_response` + which can be used by subclasses to alter the default + behavior for ``OPTIONS`` responses. +- Unbound locals now raise a proper :exc:`RuntimeError` instead + of an :exc:`AttributeError`. +- Mimetype guessing and etag support based on file objects is now + deprecated for :func:`flask.send_file` because it was unreliable. + Pass filenames instead or attach your own etags and provide a + proper mimetype by hand. +- Static file handling for modules now requires the name of the + static folder to be supplied explicitly. The previous autodetection + was not reliable and caused issues on Google's App Engine. Until + 1.0 the old behavior will continue to work but issue dependency + warnings. +- fixed a problem for Flask to run on jython. +- added a ``PROPAGATE_EXCEPTIONS`` configuration variable that can be + used to flip the setting of exception propagation which previously + was linked to ``DEBUG`` alone and is now linked to either ``DEBUG`` or + ``TESTING``. +- Flask no longer internally depends on rules being added through the + `add_url_rule` function and can now also accept regular werkzeug + rules added to the url map. +- Added an `endpoint` method to the flask application object which + allows one to register a callback to an arbitrary endpoint with + a decorator. +- Use Last-Modified for static file sending instead of Date which + was incorrectly introduced in 0.6. +- Added `create_jinja_loader` to override the loader creation process. +- Implemented a silent flag for `config.from_pyfile`. +- Added `teardown_request` decorator, for functions that should run at the end + of a request regardless of whether an exception occurred. Also the behavior + for `after_request` was changed. It's now no longer executed when an exception + is raised. See :ref:`upgrading-to-new-teardown-handling` +- Implemented :func:`flask.has_request_context` +- Deprecated `init_jinja_globals`. Override the + :meth:`~flask.Flask.create_jinja_environment` method instead to + achieve the same functionality. +- Added :func:`flask.safe_join` +- The automatic JSON request data unpacking now looks at the charset + mimetype parameter. +- Don't modify the session on :func:`flask.get_flashed_messages` if there + are no messages in the session. +- `before_request` handlers are now able to abort requests with errors. +- it is not possible to define user exception handlers. That way you can + provide custom error messages from a central hub for certain errors that + might occur during request processing (for instance database connection + errors, timeouts from remote resources etc.). +- Blueprints can provide blueprint specific error handlers. +- Implemented generic :ref:`views` (class-based views). + +Version 0.6.1 +------------- + +Bugfix release, released on December 31st 2010 + +- Fixed an issue where the default ``OPTIONS`` response was + not exposing all valid methods in the ``Allow`` header. +- Jinja2 template loading syntax now allows "./" in front of + a template load path. Previously this caused issues with + module setups. +- Fixed an issue where the subdomain setting for modules was + ignored for the static folder. +- Fixed a security problem that allowed clients to download arbitrary files + if the host server was a windows based operating system and the client + uses backslashes to escape the directory the files where exposed from. + +Version 0.6 +----------- + +Released on July 27th 2010, codename Whisky + +- after request functions are now called in reverse order of + registration. +- OPTIONS is now automatically implemented by Flask unless the + application explicitly adds 'OPTIONS' as method to the URL rule. + In this case no automatic OPTIONS handling kicks in. +- static rules are now even in place if there is no static folder + for the module. This was implemented to aid GAE which will + remove the static folder if it's part of a mapping in the .yml + file. +- the :attr:`~flask.Flask.config` is now available in the templates + as `config`. +- context processors will no longer override values passed directly + to the render function. +- added the ability to limit the incoming request data with the + new ``MAX_CONTENT_LENGTH`` configuration value. +- the endpoint for the :meth:`flask.Module.add_url_rule` method + is now optional to be consistent with the function of the + same name on the application object. +- added a :func:`flask.make_response` function that simplifies + creating response object instances in views. +- added signalling support based on blinker. This feature is currently + optional and supposed to be used by extensions and applications. If + you want to use it, make sure to have `blinker`_ installed. +- refactored the way URL adapters are created. This process is now + fully customizable with the :meth:`~flask.Flask.create_url_adapter` + method. +- modules can now register for a subdomain instead of just an URL + prefix. This makes it possible to bind a whole module to a + configurable subdomain. + +.. _blinker: https://pypi.org/project/blinker/ + +Version 0.5.2 +------------- + +Bugfix Release, released on July 15th 2010 + +- fixed another issue with loading templates from directories when + modules were used. + +Version 0.5.1 +------------- + +Bugfix Release, released on July 6th 2010 + +- fixes an issue with template loading from directories when modules + where used. + +Version 0.5 +----------- + +Released on July 6th 2010, codename Calvados + +- fixed a bug with subdomains that was caused by the inability to + specify the server name. The server name can now be set with + the ``SERVER_NAME`` config key. This key is now also used to set + the session cookie cross-subdomain wide. +- autoescaping is no longer active for all templates. Instead it + is only active for ``.html``, ``.htm``, ``.xml`` and ``.xhtml``. + Inside templates this behavior can be changed with the + ``autoescape`` tag. +- refactored Flask internally. It now consists of more than a + single file. +- :func:`flask.send_file` now emits etags and has the ability to + do conditional responses builtin. +- (temporarily) dropped support for zipped applications. This was a + rarely used feature and led to some confusing behavior. +- added support for per-package template and static-file directories. +- removed support for `create_jinja_loader` which is no longer used + in 0.5 due to the improved module support. +- added a helper function to expose files from any directory. + +Version 0.4 +----------- + +Released on June 18th 2010, codename Rakia + +- added the ability to register application wide error handlers + from modules. +- :meth:`~flask.Flask.after_request` handlers are now also invoked + if the request dies with an exception and an error handling page + kicks in. +- test client has not the ability to preserve the request context + for a little longer. This can also be used to trigger custom + requests that do not pop the request stack for testing. +- because the Python standard library caches loggers, the name of + the logger is configurable now to better support unittests. +- added ``TESTING`` switch that can activate unittesting helpers. +- the logger switches to ``DEBUG`` mode now if debug is enabled. + +Version 0.3.1 +------------- + +Bugfix release, released on May 28th 2010 + +- fixed a error reporting bug with :meth:`flask.Config.from_envvar` +- removed some unused code from flask +- release does no longer include development leftover files (.git + folder for themes, built documentation in zip and pdf file and + some .pyc files) + +Version 0.3 +----------- + +Released on May 28th 2010, codename Schnaps + +- added support for categories for flashed messages. +- the application now configures a :class:`logging.Handler` and will + log request handling exceptions to that logger when not in debug + mode. This makes it possible to receive mails on server errors + for example. +- added support for context binding that does not require the use of + the with statement for playing in the console. +- the request context is now available within the with statement making + it possible to further push the request context or pop it. +- added support for configurations. + +Version 0.2 +----------- + +Released on May 12th 2010, codename Jägermeister + +- various bugfixes +- integrated JSON support +- added :func:`~flask.get_template_attribute` helper function. +- :meth:`~flask.Flask.add_url_rule` can now also register a + view function. +- refactored internal request dispatching. +- server listens on 127.0.0.1 by default now to fix issues with chrome. +- added external URL support. +- added support for :func:`~flask.send_file` +- module support and internal request handling refactoring + to better support pluggable applications. +- sessions can be set to be permanent now on a per-session basis. +- better error reporting on missing secret keys. +- added support for Google Appengine. + +Version 0.1 +----------- + +First public preview release. diff -Nru flask-0.12.2/debian/changelog flask-1.0.2/debian/changelog --- flask-0.12.2/debian/changelog 2018-01-15 13:59:22.000000000 +0000 +++ flask-1.0.2/debian/changelog 2018-11-15 15:04:12.000000000 +0000 @@ -1,3 +1,54 @@ +flask (1.0.2-3~cloud0) bionic-stein; urgency=medium + + * New update for the Ubuntu Cloud Archive. + + -- Openstack Ubuntu Testing Bot Thu, 15 Nov 2018 15:04:12 +0000 + +flask (1.0.2-3) unstable; urgency=medium + + * Set https_proxy during building docs to prevent internet access + (Closes: #909570) + + -- Ondřej Nový Thu, 27 Sep 2018 10:50:39 +0200 + +flask (1.0.2-2) unstable; urgency=medium + + * Convert git repository from git-dpm to gbp layout + * Use 'python3 -m sphinx' instead of sphinx-build for building docs + * Add d/p/0002-remove-DocVersion-related.patch and + d/p/0003-fix-issue-no-theme-named-flask-found.patch and + d/p/0004-empty-CONTRIBUTING-rst.patch and + d/p/0005-remove-carbon-ads.patch: + Make docs build compatible with newer pallets_sphinx_themes + (Closes: #908688) + * Bump standards version to 4.2.1 (no changes) + + -- Ondřej Nový Tue, 18 Sep 2018 11:42:45 +0200 + +flask (1.0.2-1) unstable; urgency=medium + + * New upstream release + * d/control: + - Remove ancient X-Python{,3}-Version field + - Bump required version of B-D a D for new upstream release + - B-D on python3-pallets-sphinx-themes + * d/patches/*: Drop. All applied upstream + * d/copyright: Change upstream copyright holders for new upstream release + * d/rules: Upstream renamed CHANGES -> CHANGES.rst + * Standards-Version is 4.1.4 now (no changes needed) + * Install examples + * Add upstream metadata + * d/copyright: Bump my copyright year + + -- Ondřej Nový Wed, 16 May 2018 15:39:05 +0200 + +flask (0.12.2-4) unstable; urgency=medium + + * d/control: Set Vcs-* to salsa.debian.org + * Add correct upstream changelog (Closes: #891342) + + -- Ondřej Nový Mon, 05 Mar 2018 14:20:35 +0100 + flask (0.12.2-3) unstable; urgency=medium * Change priority from extra to optional diff -Nru flask-0.12.2/debian/control flask-1.0.2/debian/control --- flask-0.12.2/debian/control 2018-01-15 13:48:48.000000000 +0000 +++ flask-1.0.2/debian/control 2018-09-18 09:42:22.000000000 +0000 @@ -8,22 +8,22 @@ python-all, python3-all, python-setuptools, python3-setuptools, # for docs: - python3-sphinx, + python3-sphinx, python3-pallets-sphinx-themes, # for tests: - python-pytest, python3-pytest, - python-werkzeug, python-jinja2, python-itsdangerous, python-blinker, python-click, - python3-werkzeug, python3-jinja2, python3-itsdangerous, python3-blinker, python3-click, -Standards-Version: 4.1.3 -Vcs-Git: https://anonscm.debian.org/git/python-modules/packages/flask.git -Vcs-Browser: https://anonscm.debian.org/cgit/python-modules/packages/flask.git + python-pytest (>= 3), python3-pytest (>= 3), + python-werkzeug (>= 0.14), python-jinja2 (>= 2.10), python-itsdangerous (>= 0.24), python-blinker, python-click (>= 5.1), + python3-werkzeug (>= 0.14), python3-jinja2 (>= 2.10), python3-itsdangerous (>= 0.24), python3-blinker, python3-click (>= 5.1), +Standards-Version: 4.2.1 +Vcs-Git: https://salsa.debian.org/python-team/modules/flask.git +Vcs-Browser: https://salsa.debian.org/python-team/modules/flask Homepage: http://flask.pocoo.org/ -X-Python-Version: >= 2.6 -X-Python3-Version: >= 3.3 Testsuite: autopkgtest-pkg-python Package: python-flask Architecture: all -Depends: ${python:Depends}, ${misc:Depends} +Depends: + python-werkzeug (>= 0.14), python-jinja2 (>= 2.10), python-itsdangerous (>= 0.24), python-click (>= 5.1), + ${python:Depends}, ${misc:Depends} Recommends: python-pkg-resources, python-blinker Suggests: python-flask-doc diff -Nru flask-0.12.2/debian/copyright flask-1.0.2/debian/copyright --- flask-0.12.2/debian/copyright 2018-01-15 13:44:06.000000000 +0000 +++ flask-1.0.2/debian/copyright 2018-05-14 15:21:50.000000000 +0000 @@ -3,12 +3,16 @@ Source: https://pypi.python.org/pypi/Flask Files: * -Copyright: (c) 2013, Armin Ronacher and contributors. See AUTHORS +Copyright: (c) 2010-2018, Pallets team and community contributors + (c) 2010-2018, David Lord (davidism) + (c) 2010-2018, Adrian Mönnich (ThiefMaster) + (c) 2010-2018, Armin Ronacher (mitsuhiko) + (c) 2010-2018, Marcus Unterwaditzer (untitaker) License: BSD-3-clause Files: debian/* Copyright: (c) 2010-2016, Piotr Ożarowski - (c) 2016 Ondřej Nový + (c) 2016-2018, Ondřej Nový License: BSD-3-clause License: BSD-3-clause diff -Nru flask-0.12.2/debian/.git-dpm flask-1.0.2/debian/.git-dpm --- flask-0.12.2/debian/.git-dpm 2018-01-15 13:44:06.000000000 +0000 +++ flask-1.0.2/debian/.git-dpm 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -# see git-dpm(1) from git-dpm package -a00ee8bbda87ad35207eb83968a1d638dfdd9dd1 -a00ee8bbda87ad35207eb83968a1d638dfdd9dd1 -846a463e9be7e6e5bfb1fabbed465308171aca54 -846a463e9be7e6e5bfb1fabbed465308171aca54 -flask_0.12.2.orig.tar.gz -8193757ded6a4f0e7c9a3ce291bf3ae3a1d402c5 -548510 -debianTag="debian/%e%v" -patchedTag="patched/%e%v" -upstreamTag="upstream/%e%u" diff -Nru flask-0.12.2/debian/patches/0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch flask-1.0.2/debian/patches/0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch --- flask-0.12.2/debian/patches/0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/debian/patches/0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch 2018-05-14 12:50:59.000000000 +0000 @@ -0,0 +1,21 @@ +From fa0e83cd6d5ca44c1583624ef4373730dca05d16 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= +Date: Mon, 14 May 2018 14:50:56 +0200 +Subject: Don't require sphinxcontrib.log_cabinet extension + +--- + docs/conf.py | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/docs/conf.py b/docs/conf.py +index 0417244..dd13886 100644 +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -20,7 +20,6 @@ master_doc = 'index' + extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', +- 'sphinxcontrib.log_cabinet', + ] + + intersphinx_mapping = { diff -Nru flask-0.12.2/debian/patches/0001-Use-SOURCE_DATE_EPOCH-for-copyright-year-to-make-bui.patch flask-1.0.2/debian/patches/0001-Use-SOURCE_DATE_EPOCH-for-copyright-year-to-make-bui.patch --- flask-0.12.2/debian/patches/0001-Use-SOURCE_DATE_EPOCH-for-copyright-year-to-make-bui.patch 2018-01-15 13:44:06.000000000 +0000 +++ flask-1.0.2/debian/patches/0001-Use-SOURCE_DATE_EPOCH-for-copyright-year-to-make-bui.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -From c8fb21caab67574be1cbd928816068c1a156078f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= -Date: Fri, 13 Jan 2017 10:46:21 +0100 -Subject: Use SOURCE_DATE_EPOCH for copyright year to make build reproducible - ---- - docs/conf.py | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/docs/conf.py b/docs/conf.py -index b37427a..81106a3 100644 ---- a/docs/conf.py -+++ b/docs/conf.py -@@ -11,10 +11,13 @@ - # All configuration values have a default; values that are commented out - # serve to show the default. - from __future__ import print_function --from datetime import datetime - import os - import sys - import pkg_resources -+import time -+import datetime -+ -+BUILD_DATE = datetime.datetime.utcfromtimestamp(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))) - - # If extensions (or modules to document with autodoc) are in another directory, - # add these directories to sys.path here. If the directory is relative to the -@@ -49,7 +52,7 @@ master_doc = 'index' - - # General information about the project. - project = u'Flask' --copyright = u'2010 - {0}, Armin Ronacher'.format(datetime.utcnow().year) -+copyright = u'2010 - {0}, Armin Ronacher'.format(BUILD_DATE.year) - - # The version info for the project you're documenting, acts as replacement for - # |version| and |release|, also used in various other places throughout the diff -Nru flask-0.12.2/debian/patches/0002-Added-Python-3.6-support-for-tests.patch flask-1.0.2/debian/patches/0002-Added-Python-3.6-support-for-tests.patch --- flask-0.12.2/debian/patches/0002-Added-Python-3.6-support-for-tests.patch 2018-01-15 13:44:06.000000000 +0000 +++ flask-1.0.2/debian/patches/0002-Added-Python-3.6-support-for-tests.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -From 130485c6780fd28275ef5716e7effa8cb4b1561b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= -Date: Sun, 9 Jul 2017 18:28:31 +0200 -Subject: Added Python 3.6 support for tests - -Author: Andrew Arendt ---- - tests/test_basic.py | 2 +- - tests/test_ext.py | 4 ++-- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/tests/test_basic.py b/tests/test_basic.py -index c5ec9f5..7441d1b 100644 ---- a/tests/test_basic.py -+++ b/tests/test_basic.py -@@ -333,7 +333,7 @@ def test_session_expiration(): - client = app.test_client() - rv = client.get('/') - assert 'set-cookie' in rv.headers -- match = re.search(r'\bexpires=([^;]+)(?i)', rv.headers['set-cookie']) -+ match = re.search(r'(?i)\bexpires=([^;]+)', rv.headers['set-cookie']) - expires = parse_date(match.group()) - expected = datetime.utcnow() + app.permanent_session_lifetime - assert expires.year == expected.year -diff --git a/tests/test_ext.py b/tests/test_ext.py -index d336e40..ebb5f02 100644 ---- a/tests/test_ext.py -+++ b/tests/test_ext.py -@@ -179,8 +179,8 @@ def test_flaskext_broken_package_no_module_caching(flaskext_broken): - def test_no_error_swallowing(flaskext_broken): - with pytest.raises(ImportError) as excinfo: - import flask.ext.broken -- -- assert excinfo.type is ImportError -+ # python3.6 raises a subclass of ImportError: 'ModuleNotFoundError' -+ assert issubclass(excinfo.type, ImportError) - if PY2: - message = 'No module named missing_module' - else: diff -Nru flask-0.12.2/debian/patches/0002-remove-DocVersion-related.patch flask-1.0.2/debian/patches/0002-remove-DocVersion-related.patch --- flask-0.12.2/debian/patches/0002-remove-DocVersion-related.patch 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/debian/patches/0002-remove-DocVersion-related.patch 2018-09-18 07:54:41.000000000 +0000 @@ -0,0 +1,33 @@ +From 067c772588f69f397eeff70019320506b47bb239 Mon Sep 17 00:00:00 2001 +From: chengkang <1412950785@qq.com> +Date: Mon, 17 Sep 2018 21:05:53 +0800 +Subject: [PATCH] remove DocVersion related +Origin: https://github.com/pallets/flask/commit/067c772588f69f397eeff70019320506b47bb239 + +--- + docs/conf.py | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -4,7 +4,7 @@ + import inspect + import re + +-from pallets_sphinx_themes import DocVersion, ProjectLink, get_version ++from pallets_sphinx_themes import ProjectLink, get_version + + # Project -------------------------------------------------------------- + +@@ -45,11 +45,6 @@ + ProjectLink( + 'Issue Tracker', 'https://github.com/pallets/flask/issues/'), + ], +- 'versions': [ +- DocVersion('dev', 'Development', 'unstable'), +- DocVersion('1.0', 'Flask 1.0', 'stable'), +- DocVersion('0.12', 'Flask 0.12'), +- ], + 'canonical_url': 'http://flask.pocoo.org/docs/{}/'.format(version), + 'carbon_ads_args': 'zoneid=1673&serve=C6AILKT&placement=pocooorg', + } diff -Nru flask-0.12.2/debian/patches/0003-Don-t-rely-on-X-Requested-With-for-pretty-print-json.patch flask-1.0.2/debian/patches/0003-Don-t-rely-on-X-Requested-With-for-pretty-print-json.patch --- flask-0.12.2/debian/patches/0003-Don-t-rely-on-X-Requested-With-for-pretty-print-json.patch 2018-01-15 13:44:06.000000000 +0000 +++ flask-1.0.2/debian/patches/0003-Don-t-rely-on-X-Requested-With-for-pretty-print-json.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -From a00ee8bbda87ad35207eb83968a1d638dfdd9dd1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= -Date: Mon, 15 Jan 2018 14:43:41 +0100 -Subject: Don't rely on X-Requested-With for pretty print json response - -Origin: https://github.com/pallets/flask/commit/a7f1a21c1204828388eaed1e3903a74c904c8147 ---- - docs/config.rst | 8 +++----- - flask/app.py | 2 +- - flask/json.py | 2 +- - tests/test_basic.py | 2 +- - tests/test_helpers.py | 2 ++ - 5 files changed, 8 insertions(+), 8 deletions(-) - -diff --git a/docs/config.rst b/docs/config.rst -index 6d37c1e..458270e 100644 ---- a/docs/config.rst -+++ b/docs/config.rst -@@ -178,11 +178,9 @@ The following configuration values are used internally by Flask: - This is not recommended but might give - you a performance improvement on the - cost of cacheability. --``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` (the default) -- jsonify responses will be pretty printed -- if they are not requested by an -- XMLHttpRequest object (controlled by -- the ``X-Requested-With`` header) -+``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` or the Flask app -+ is running in debug mode, jsonify responses -+ will be pretty printed. - ``JSONIFY_MIMETYPE`` MIME type used for jsonify responses. - ``TEMPLATES_AUTO_RELOAD`` Whether to check for modifications of - the template source and reload it -diff --git a/flask/app.py b/flask/app.py -index 1404e17..fc5db71 100644 ---- a/flask/app.py -+++ b/flask/app.py -@@ -314,7 +314,7 @@ class Flask(_PackageBoundObject): - 'PREFERRED_URL_SCHEME': 'http', - 'JSON_AS_ASCII': True, - 'JSON_SORT_KEYS': True, -- 'JSONIFY_PRETTYPRINT_REGULAR': True, -+ 'JSONIFY_PRETTYPRINT_REGULAR': False, - 'JSONIFY_MIMETYPE': 'application/json', - 'TEMPLATES_AUTO_RELOAD': None, - }) -diff --git a/flask/json.py b/flask/json.py -index 16e0c29..bc32c3b 100644 ---- a/flask/json.py -+++ b/flask/json.py -@@ -248,7 +248,7 @@ def jsonify(*args, **kwargs): - indent = None - separators = (',', ':') - -- if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] and not request.is_xhr: -+ if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug: - indent = 2 - separators = (', ', ': ') - -diff --git a/tests/test_basic.py b/tests/test_basic.py -index 7441d1b..9fdc7ea 100644 ---- a/tests/test_basic.py -+++ b/tests/test_basic.py -@@ -1012,7 +1012,7 @@ def test_make_response_with_response_instance(): - rv = flask.make_response( - flask.jsonify({'msg': 'W00t'}), 400) - assert rv.status_code == 400 -- assert rv.data == b'{\n "msg": "W00t"\n}\n' -+ assert rv.data == b'{"msg":"W00t"}\n' - assert rv.mimetype == 'application/json' - - rv = flask.make_response( -diff --git a/tests/test_helpers.py b/tests/test_helpers.py -index 9320ef7..7f9d3d6 100644 ---- a/tests/test_helpers.py -+++ b/tests/test_helpers.py -@@ -289,6 +289,8 @@ class TestJSON(object): - def test_json_key_sorting(self): - app = flask.Flask(__name__) - app.testing = True -+ app.debug = True -+ - assert app.config['JSON_SORT_KEYS'] == True - d = dict.fromkeys(range(20), 'foo') - diff -Nru flask-0.12.2/debian/patches/0003-fix-issue-no-theme-named-flask-found.patch flask-1.0.2/debian/patches/0003-fix-issue-no-theme-named-flask-found.patch --- flask-0.12.2/debian/patches/0003-fix-issue-no-theme-named-flask-found.patch 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/debian/patches/0003-fix-issue-no-theme-named-flask-found.patch 2018-09-18 07:54:30.000000000 +0000 @@ -0,0 +1,20 @@ +From 3e86fd25e9638898618cda8352402f4281bf809b Mon Sep 17 00:00:00 2001 +From: chengkang <1412950785@qq.com> +Date: Mon, 17 Sep 2018 16:18:52 +0800 +Subject: [PATCH] fix issue: no theme named 'flask' found +Origin: https://github.com/pallets/flask/commit/3e86fd25e9638898618cda8352402f4281bf809b + +--- + docs/conf.py | 1 + + 1 file changed, 1 insertion(+) + +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -20,6 +20,7 @@ + extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', ++ 'pallets_sphinx_themes', + ] + + intersphinx_mapping = { diff -Nru flask-0.12.2/debian/patches/0004-empty-CONTRIBUTING-rst.patch flask-1.0.2/debian/patches/0004-empty-CONTRIBUTING-rst.patch --- flask-0.12.2/debian/patches/0004-empty-CONTRIBUTING-rst.patch 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/debian/patches/0004-empty-CONTRIBUTING-rst.patch 2018-09-18 09:36:21.000000000 +0000 @@ -0,0 +1,8 @@ +Author: Ondřej Nový +Description: Add empty CONTRIBUTING.rst +Forwarded: sort of, https://github.com/pallets/flask/pull/2913 + +--- /dev/null ++++ b/CONTRIBUTING.rst +@@ -0,0 +1 @@ ++ diff -Nru flask-0.12.2/debian/patches/0005-remove-carbon-ads.patch flask-1.0.2/debian/patches/0005-remove-carbon-ads.patch --- flask-0.12.2/debian/patches/0005-remove-carbon-ads.patch 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/debian/patches/0005-remove-carbon-ads.patch 2018-09-18 09:36:15.000000000 +0000 @@ -0,0 +1,27 @@ +Author: Ondřej Nový +Description: Remove Carbod Ads +Origin: https://github.com/pallets/flask/commit/ba868d681aac8273ad785ebf28f4a043b737ab78 + +--- a/docs/conf.py ++++ b/docs/conf.py +@@ -47,20 +47,17 @@ + 'Issue Tracker', 'https://github.com/pallets/flask/issues/'), + ], + 'canonical_url': 'http://flask.pocoo.org/docs/{}/'.format(version), +- 'carbon_ads_args': 'zoneid=1673&serve=C6AILKT&placement=pocooorg', + } + html_sidebars = { + 'index': [ + 'project.html', + 'versions.html', +- 'carbon_ads.html', + 'searchbox.html', + ], + '**': [ + 'localtoc.html', + 'relations.html', + 'versions.html', +- 'carbon_ads.html', + 'searchbox.html', + ] + } diff -Nru flask-0.12.2/debian/patches/series flask-1.0.2/debian/patches/series --- flask-0.12.2/debian/patches/series 2018-01-15 13:44:06.000000000 +0000 +++ flask-1.0.2/debian/patches/series 2018-09-18 09:36:26.000000000 +0000 @@ -1,3 +1,5 @@ -0001-Use-SOURCE_DATE_EPOCH-for-copyright-year-to-make-bui.patch -0002-Added-Python-3.6-support-for-tests.patch -0003-Don-t-rely-on-X-Requested-With-for-pretty-print-json.patch +0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch +0002-remove-DocVersion-related.patch +0003-fix-issue-no-theme-named-flask-found.patch +0004-empty-CONTRIBUTING-rst.patch +0005-remove-carbon-ads.patch diff -Nru flask-0.12.2/debian/python3-flask.examples flask-1.0.2/debian/python3-flask.examples --- flask-0.12.2/debian/python3-flask.examples 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/debian/python3-flask.examples 2018-05-14 12:50:58.000000000 +0000 @@ -0,0 +1 @@ +examples/tutorial/* diff -Nru flask-0.12.2/debian/python-flask.examples flask-1.0.2/debian/python-flask.examples --- flask-0.12.2/debian/python-flask.examples 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/debian/python-flask.examples 2018-05-14 12:50:58.000000000 +0000 @@ -0,0 +1 @@ +examples/tutorial/* diff -Nru flask-0.12.2/debian/rules flask-1.0.2/debian/rules --- flask-0.12.2/debian/rules 2018-01-15 13:44:06.000000000 +0000 +++ flask-1.0.2/debian/rules 2018-09-27 08:43:06.000000000 +0000 @@ -16,9 +16,15 @@ override_dh_sphinxdoc: ifeq (,$(findstring nodocs, $(DEB_BUILD_OPTIONS))) - http_proxy='http://127.0.0.1:9/' PYTHONPATH=. sphinx-build -N -q -E -b html docs/ debian/python-flask-doc/usr/share/doc/python-flask-doc/html/ + http_proxy='http://127.0.0.1:9/' \ + https_proxy='http://127.0.0.1:9/' \ + PYTHONPATH=. \ + python3 -m sphinx -N -q -E -b html docs/ debian/python-flask-doc/usr/share/doc/python-flask-doc/html/ dh_sphinxdoc # Remove unwanted license file, we already reference d/copyright rm -f $(CURDIR)/debian/python-flask-doc/usr/share/doc/python-flask-doc/html/_sources/license.rst.txt rm -f $(CURDIR)/debian/python-flask-doc/usr/share/doc/python-flask-doc/html/license.html endif + +override_dh_installchangelogs: + dh_installchangelogs CHANGES.rst diff -Nru flask-0.12.2/debian/upstream/metadata flask-1.0.2/debian/upstream/metadata --- flask-0.12.2/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/debian/upstream/metadata 2018-05-14 15:18:33.000000000 +0000 @@ -0,0 +1,5 @@ +Bug-Database: https://github.com/pallets/flask/issues +Bug-Submit: https://github.com/pallets/flask/issues/new +Name: flask +Repository: https://github.com/pallets/flask.git +Repository-Browse: https://github.com/pallets/flask diff -Nru flask-0.12.2/docs/advanced_foreword.rst flask-1.0.2/docs/advanced_foreword.rst --- flask-0.12.2/docs/advanced_foreword.rst 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/docs/advanced_foreword.rst 2018-05-02 00:50:32.000000000 +0000 @@ -45,11 +45,3 @@ Flask is no different from any other framework in that you the developer must build with caution, watching for exploits when building to your requirements. - -Python 3 Support in Flask -------------------------- - -Flask, its dependencies, and most Flask extensions all support Python 3. -If you want to use Flask with Python 3 have a look at the :ref:`python3-support` page. - -Continue to :ref:`installation` or the :ref:`quickstart`. diff -Nru flask-0.12.2/docs/api.rst flask-1.0.2/docs/api.rst --- flask-0.12.2/docs/api.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/api.rst 2018-05-02 00:50:32.000000000 +0000 @@ -30,61 +30,12 @@ .. autoclass:: Request :members: - - .. attribute:: form - - A :class:`~werkzeug.datastructures.MultiDict` with the parsed form data from ``POST`` - or ``PUT`` requests. Please keep in mind that file uploads will not - end up here, but instead in the :attr:`files` attribute. - - .. attribute:: args - - A :class:`~werkzeug.datastructures.MultiDict` with the parsed contents of the query - string. (The part in the URL after the question mark). - - .. attribute:: values - - A :class:`~werkzeug.datastructures.CombinedMultiDict` with the contents of both - :attr:`form` and :attr:`args`. - - .. attribute:: cookies - - A :class:`dict` with the contents of all cookies transmitted with - the request. - - .. attribute:: stream - - If the incoming form data was not encoded with a known mimetype - the data is stored unmodified in this stream for consumption. Most - of the time it is a better idea to use :attr:`data` which will give - you that data as a string. The stream only returns the data once. - - .. attribute:: headers - - The incoming request headers as a dictionary like object. - - .. attribute:: data - - Contains the incoming request data as string in case it came with - a mimetype Flask does not handle. - - .. attribute:: files - - A :class:`~werkzeug.datastructures.MultiDict` with files uploaded as part of a - ``POST`` or ``PUT`` request. Each file is stored as - :class:`~werkzeug.datastructures.FileStorage` object. It basically behaves like a - standard file object you know from Python, with the difference that - it also has a :meth:`~werkzeug.datastructures.FileStorage.save` function that can - store the file on the filesystem. + :inherited-members: .. attribute:: environ The underlying WSGI environment. - .. attribute:: method - - The current request method (``POST``, ``GET`` etc.) - .. attribute:: path .. attribute:: full_path .. attribute:: script_root @@ -114,15 +65,8 @@ `url_root` ``u'http://www.example.com/myapplication/'`` ============= ====================================================== - .. attribute:: is_xhr - - ``True`` if the request was triggered via a JavaScript - `XMLHttpRequest`. This only works with libraries that support the - ``X-Requested-With`` header and set it to `XMLHttpRequest`. - Libraries that do that are prototype, jQuery and Mochikit and - probably some more. -.. class:: request +.. attribute:: request To access incoming request data, you can use the global `request` object. Flask parses incoming request data for you and gives you @@ -141,7 +85,7 @@ ---------------- .. autoclass:: flask.Response - :members: set_cookie, data, mimetype + :members: set_cookie, max_cookie_size, data, mimetype, is_json, get_json .. attribute:: headers @@ -159,12 +103,12 @@ Sessions -------- -If you have the :attr:`Flask.secret_key` set you can use sessions in Flask -applications. A session basically makes it possible to remember -information from one request to another. The way Flask does this is by -using a signed cookie. So the user can look at the session contents, but -not modify it unless they know the secret key, so make sure to set that -to something complex and unguessable. +If you have set :attr:`Flask.secret_key` (or configured it from +:data:`SECRET_KEY`) you can use sessions in Flask applications. A session makes +it possible to remember information from one request to another. The way Flask +does this is by using a signed cookie. The user can look at the session +contents, but can't modify it unless they know the secret key, so make sure to +set that to something complex and unguessable. To access the current session you can use the :class:`session` object: @@ -227,18 +171,6 @@ .. autoclass:: SessionMixin :members: -.. autodata:: session_json_serializer - - This object provides dumping and loading methods similar to simplejson - but it also tags certain builtin Python objects that commonly appear in - sessions. Currently the following extended values are supported in - the JSON it dumps: - - - :class:`~markupsafe.Markup` objects - - :class:`~uuid.UUID` objects - - :class:`~datetime.datetime` objects - - :class:`tuple`\s - .. admonition:: Notice The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer @@ -256,6 +188,15 @@ :members: +Test CLI Runner +--------------- + +.. currentmodule:: flask.testing + +.. autoclass:: FlaskCliRunner + :members: + + Application Globals ------------------- @@ -270,29 +211,22 @@ .. data:: g - Just store on this whatever you want. For example a database - connection or the user that is currently logged in. - - Starting with Flask 0.10 this is stored on the application context and - no longer on the request context which means it becomes available if - only the application context is bound and not yet a request. This - is especially useful when combined with the :ref:`faking-resources` - pattern for testing. - - Additionally as of 0.10 you can use the :meth:`get` method to - get an attribute or ``None`` (or the second argument) if it's not set. - These two usages are now equivalent:: + A namespace object that can store data during an + :doc:`application context `. This is an instance of + :attr:`Flask.app_ctx_globals_class`, which defaults to + :class:`ctx._AppCtxGlobals`. + + This is a good place to store resources during a request. During + testing, you can use the :ref:`faking-resources` pattern to + pre-configure such resources. - user = getattr(flask.g, 'user', None) - user = flask.g.get('user', None) + This is a proxy. See :ref:`notes-on-proxies` for more information. - It's now also possible to use the ``in`` operator on it to see if an - attribute is defined and it yields all keys on iteration. + .. versionchanged:: 0.10 + Bound to the application context instead of the request context. - As of 0.11 you can use :meth:`pop` and :meth:`setdefault` in the same - way you would use them on a dictionary. - - This is a proxy. See :ref:`notes-on-proxies` for more information. +.. autoclass:: flask.ctx._AppCtxGlobals + :members: Useful Functions and Classes @@ -300,13 +234,17 @@ .. data:: current_app - Points to the application handling the request. This is useful for - extensions that want to support multiple applications running side - by side. This is powered by the application context and not by the - request context, so you can change the value of this proxy by - using the :meth:`~flask.Flask.app_context` method. + A proxy to the application handling the current request. This is + useful to access the application without needing to import it, or if + it can't be imported, such as when using the application factory + pattern or in blueprints and extensions. + + This is only available when an + :doc:`application context ` is pushed. This happens + automatically during requests and CLI commands. It can be controlled + manually with :meth:`~flask.Flask.app_context`. - This is a proxy. See :ref:`notes-on-proxies` for more information. + This is a proxy. See :ref:`notes-on-proxies` for more information. .. autofunction:: has_request_context @@ -410,6 +348,8 @@ .. autoclass:: JSONDecoder :members: +.. automodule:: flask.json.tag + Template Rendering ------------------ @@ -427,22 +367,6 @@ .. autoclass:: Config :members: -Extensions ----------- - -.. data:: flask.ext - - This module acts as redirect import module to Flask extensions. It was - added in 0.8 as the canonical way to import Flask extensions and makes - it possible for us to have more flexibility in how we distribute - extensions. - - If you want to use an extension named “Flask-Foo” you would import it - from :data:`~flask.ext` as follows:: - - from flask.ext import foo - - .. versionadded:: 0.8 Stream Helpers -------------- @@ -457,50 +381,54 @@ .. data:: _request_ctx_stack - The internal :class:`~werkzeug.local.LocalStack` that is used to implement - all the context local objects used in Flask. This is a documented - instance and can be used by extensions and application code but the - use is discouraged in general. + The internal :class:`~werkzeug.local.LocalStack` that holds + :class:`~flask.ctx.RequestContext` instances. Typically, the + :data:`request` and :data:`session` proxies should be accessed + instead of the stack. It may be useful to access the stack in + extension code. - The following attributes are always present on each layer of the - stack: + The following attributes are always present on each layer of the + stack: - `app` + `app` the active Flask application. - `url_adapter` + `url_adapter` the URL adapter that was used to match the request. - `request` + `request` the current request object. - `session` + `session` the active session object. - `g` + `g` an object with all the attributes of the :data:`flask.g` object. - `flashes` + `flashes` an internal cache for the flashed messages. - Example usage:: + Example usage:: - from flask import _request_ctx_stack + from flask import _request_ctx_stack - def get_session(): - ctx = _request_ctx_stack.top - if ctx is not None: - return ctx.session + def get_session(): + ctx = _request_ctx_stack.top + if ctx is not None: + return ctx.session .. autoclass:: flask.ctx.AppContext :members: .. data:: _app_ctx_stack - Works similar to the request context but only binds the application. - This is mainly there for extensions to store data. + The internal :class:`~werkzeug.local.LocalStack` that holds + :class:`~flask.ctx.AppContext` instances. Typically, the + :data:`current_app` and :data:`g` proxies should be accessed instead + of the stack. Extensions can access the contexts on the stack as a + namespace to store data. - .. versionadded:: 0.9 + .. versionadded:: 0.9 .. autoclass:: flask.blueprints.BlueprintSetupState :members: @@ -703,8 +631,9 @@ operations, including connecting. -.. _blinker: https://pypi.python.org/pypi/blinker +.. _blinker: https://pypi.org/project/blinker/ +.. _class-based-views: Class-Based Views ----------------- @@ -788,7 +717,18 @@ pass This specifies that ``/users/`` will be the URL for page one and -``/users/page/N`` will be the URL for page `N`. +``/users/page/N`` will be the URL for page ``N``. + +If a URL contains a default value, it will be redirected to its simpler +form with a 301 redirect. In the above example, ``/users/page/1`` will +be redirected to ``/users/``. If your route handles ``GET`` and ``POST`` +requests, make sure the default route only handles ``GET``, as redirects +can't preserve form data. :: + + @app.route('/region/', defaults={'id': 1}) + @app.route('/region/', methods=['GET', 'POST']) + def region(id): + pass Here are the parameters that :meth:`~flask.Flask.route` and :meth:`~flask.Flask.add_url_rule` accept. The only difference is that @@ -879,6 +819,8 @@ .. autoclass:: ScriptInfo :members: +.. autofunction:: load_dotenv + .. autofunction:: with_appcontext .. autofunction:: pass_script_info diff -Nru flask-0.12.2/docs/appcontext.rst flask-1.0.2/docs/appcontext.rst --- flask-0.12.2/docs/appcontext.rst 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/docs/appcontext.rst 2018-05-02 00:50:32.000000000 +0000 @@ -1,138 +1,159 @@ +.. currentmodule:: flask + .. _app-context: The Application Context ======================= -.. versionadded:: 0.9 +The application context keeps track of the application-level data during +a request, CLI command, or other activity. Rather than passing the +application around to each function, the :data:`current_app` and +:data:`g` proxies are accessed instead. + +This is similar to the :doc:`/reqcontext`, which keeps track of +request-level data during a request. A corresponding application context +is pushed when a request context is pushed. + +Purpose of the Context +---------------------- + +The :class:`Flask` application object has attributes, such as +:attr:`~Flask.config`, that are useful to access within views and +:doc:`CLI commands `. However, importing the ``app`` instance +within the modules in your project is prone to circular import issues. +When using the :doc:`app factory pattern ` or +writing reusable :doc:`blueprints ` or +:doc:`extensions ` there won't be an ``app`` instance to +import at all. + +Flask solves this issue with the *application context*. Rather than +referring to an ``app`` directly, you use the the :data:`current_app` +proxy, which points to the application handling the current activity. + +Flask automatically *pushes* an application context when handling a +request. View functions, error handlers, and other functions that run +during a request will have access to :data:`current_app`. -One of the design ideas behind Flask is that there are two different -“states” in which code is executed. The application setup state in which -the application implicitly is on the module level. It starts when the -:class:`Flask` object is instantiated, and it implicitly ends when the -first request comes in. While the application is in this state a few -assumptions are true: - -- the programmer can modify the application object safely. -- no request handling happened so far -- you have to have a reference to the application object in order to - modify it, there is no magic proxy that can give you a reference to - the application object you're currently creating or modifying. - -In contrast, during request handling, a couple of other rules exist: - -- while a request is active, the context local objects - (:data:`flask.request` and others) point to the current request. -- any code can get hold of these objects at any time. - -There is a third state which is sitting in between a little bit. -Sometimes you are dealing with an application in a way that is similar to -how you interact with applications during request handling; just that there -is no request active. Consider, for instance, that you're sitting in an -interactive Python shell and interacting with the application, or a -command line application. - -The application context is what powers the :data:`~flask.current_app` -context local. - -Purpose of the Application Context ----------------------------------- - -The main reason for the application's context existence is that in the -past a bunch of functionality was attached to the request context for lack -of a better solution. Since one of the pillars of Flask's design is that -you can have more than one application in the same Python process. - -So how does the code find the “right” application? In the past we -recommended passing applications around explicitly, but that caused issues -with libraries that were not designed with that in mind. - -A common workaround for that problem was to use the -:data:`~flask.current_app` proxy later on, which was bound to the current -request's application reference. Since creating such a request context is -an unnecessarily expensive operation in case there is no request around, -the application context was introduced. - -Creating an Application Context -------------------------------- - -There are two ways to make an application context. The first one is -implicit: whenever a request context is pushed, an application context -will be created alongside if this is necessary. As a result, you can -ignore the existence of the application context unless you need it. - -The second way is the explicit way using the -:meth:`~flask.Flask.app_context` method:: - - from flask import Flask, current_app - - app = Flask(__name__) - with app.app_context(): - # within this block, current_app points to app. - print current_app.name - -The application context is also used by the :func:`~flask.url_for` -function in case a ``SERVER_NAME`` was configured. This allows you to -generate URLs even in the absence of a request. +Flask will also automatically push an app context when running CLI +commands registered with :attr:`Flask.cli` using ``@app.cli.command()``. -If no request context has been pushed and an application context has -not been explicitly set, a ``RuntimeError`` will be raised. :: - RuntimeError: Working outside of application context. +Lifetime of the Context +----------------------- + +The application context is created and destroyed as necessary. When a +Flask application begins handling a request, it pushes an application +context and a :doc:`request context `. When the request +ends it pops the request context then the application context. +Typically, an application context will have the same lifetime as a +request. -Locality of the Context +See :doc:`/reqcontext` for more information about how the contexts work +and the full lifecycle of a request. + + +Manually Push a Context ----------------------- -The application context is created and destroyed as necessary. It never -moves between threads and it will not be shared between requests. As such -it is the perfect place to store database connection information and other -things. The internal stack object is called :data:`flask._app_ctx_stack`. -Extensions are free to store additional information on the topmost level, -assuming they pick a sufficiently unique name and should put their -information there, instead of on the :data:`flask.g` object which is reserved -for user code. - -For more information about that, see :ref:`extension-dev`. - -Context Usage -------------- - -The context is typically used to cache resources that need to be created -on a per-request or usage case. For instance, database connections are -destined to go there. When storing things on the application context -unique names should be chosen as this is a place that is shared between -Flask applications and extensions. - -The most common usage is to split resource management into two parts: - -1. an implicit resource caching on the context. -2. a context teardown based resource deallocation. - -Generally there would be a ``get_X()`` function that creates resource -``X`` if it does not exist yet and otherwise returns the same resource, -and a ``teardown_X()`` function that is registered as teardown handler. +If you try to access :data:`current_app`, or anything that uses it, +outside an application context, you'll get this error message: + +.. code-block:: pytb + + RuntimeError: 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(). + +If you see that error while configuring your application, such as when +initializing an extension, you can push a context manually since you +have direct access to the ``app``. Use :meth:`~Flask.app_context` in a +``with`` block, and everything that runs in the block will have access +to :data:`current_app`. :: + + def create_app(): + app = Flask(__name__) + + with app.app_context(): + init_db() + + return app + +If you see that error somewhere else in your code not related to +configuring the application, it most likely indicates that you should +move that code into a view function or CLI command. -This is an example that connects to a database:: - import sqlite3 +Storing Data +------------ + +The application context is a good place to store common data during a +request or CLI command. Flask provides the :data:`g object ` for this +purpose. It is a simple namespace object that has the same lifetime as +an application context. + +.. note:: + The ``g`` name stands for "global", but that is referring to the + data being global *within a context*. The data on ``g`` is lost + after the context ends, and it is not an appropriate place to store + data between requests. Use the :data:`session` or a database to + store data across requests. + +A common use for :data:`g` is to manage resources during a request. + +1. ``get_X()`` creates resource ``X`` if it does not exist, caching it + as ``g.X``. +2. ``teardown_X()`` closes or otherwise deallocates the resource if it + exists. It is registered as a :meth:`~Flask.teardown_appcontext` + handler. + +For example, you can manage a database connection using this pattern:: + from flask import g def get_db(): - db = getattr(g, '_database', None) - if db is None: - db = g._database = connect_to_database() - return db + if 'db' not in g: + g.db = connect_to_database() + + return g.db @app.teardown_appcontext - def teardown_db(exception): - db = getattr(g, '_database', None) + def teardown_db(): + db = g.pop('db', None) + if db is not None: db.close() -The first time ``get_db()`` is called the connection will be established. -To make this implicit a :class:`~werkzeug.local.LocalProxy` can be used:: +During a request, every call to ``get_db()`` will return the same +connection, and it will be closed automatically at the end of the +request. + +You can use :class:`~werkzeug.local.LocalProxy` to make a new context +local from ``get_db()``:: from werkzeug.local import LocalProxy db = LocalProxy(get_db) -That way a user can directly access ``db`` which internally calls -``get_db()``. +Accessing ``db`` will call ``get_db`` internally, in the same way that +:data:`current_app` works. + +---- + +If you're writing an extension, :data:`g` should be reserved for user +code. You may store internal data on the context itself, but be sure to +use a sufficiently unique name. The current context is accessed with +:data:`_app_ctx_stack.top <_app_ctx_stack>`. For more information see +:doc:`extensiondev`. + + +Events and Signals +------------------ + +The application will call functions registered with +:meth:`~Flask.teardown_appcontext` when the application context is +popped. + +If :data:`~signals.signals_available` is true, the following signals are +sent: :data:`appcontext_pushed`, :data:`appcontext_tearing_down`, and +:data:`appcontext_popped`. diff -Nru flask-0.12.2/docs/blueprints.rst flask-1.0.2/docs/blueprints.rst --- flask-0.12.2/docs/blueprints.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/blueprints.rst 2018-05-02 00:50:32.000000000 +0000 @@ -151,23 +151,31 @@ Static Files ```````````` -A blueprint can expose a folder with static files by providing a path to a -folder on the filesystem via the `static_folder` keyword argument. It can -either be an absolute path or one relative to the folder of the -blueprint:: +A blueprint can expose a folder with static files by providing the path +to the folder on the filesystem with the ``static_folder`` argument. +It is either an absolute path or relative to the blueprint's location:: admin = Blueprint('admin', __name__, static_folder='static') By default the rightmost part of the path is where it is exposed on the -web. Because the folder is called :file:`static` here it will be available at -the location of the blueprint + ``/static``. Say the blueprint is -registered for ``/admin`` the static folder will be at ``/admin/static``. - -The endpoint is named `blueprint_name.static` so you can generate URLs to -it like you would do to the static folder of the application:: +web. This can be changed with the ``static_url_path`` argument. Because the +folder is called ``static`` here it will be available at the +``url_prefix`` of the blueprint + ``/static``. If the blueprint +has the prefix ``/admin``, the static URL will be ``/admin/static``. + +The endpoint is named ``blueprint_name.static``. You can generate URLs +to it with :func:`url_for` like you would with the static folder of the +application:: url_for('admin.static', filename='style.css') +However, if the blueprint does not have a ``url_prefix``, it is not +possible to access the blueprint's static folder. This is because the +URL would be ``/static`` in this case, and the application's ``/static`` +route takes precedence. Unlike template folders, blueprint static +folders are not searched if the file does not exist in the application +static folder. + Templates ````````` @@ -177,11 +185,11 @@ admin = Blueprint('admin', __name__, template_folder='templates') For static files, the path can be absolute or relative to the blueprint -resource folder. +resource folder. -The template folder is added to the search path of templates but with a lower -priority than the actual application's template folder. That way you can -easily override templates that a blueprint provides in the actual application. +The template folder is added to the search path of templates but with a lower +priority than the actual application's template folder. That way you can +easily override templates that a blueprint provides in the actual application. This also means that if you don't want a blueprint template to be accidentally overridden, make sure that no other blueprint or actual application template has the same relative path. When multiple blueprints provide the same relative @@ -194,7 +202,7 @@ this: :file:`yourapplication/admin/templates/admin/index.html`. The reason for the extra ``admin`` folder is to avoid getting our template overridden by a template named ``index.html`` in the actual application template -folder. +folder. To further reiterate this: if you have a blueprint named ``admin`` and you want to render a template called :file:`index.html` which is specific to this @@ -245,4 +253,22 @@ def page_not_found(e): return render_template('pages/404.html') +Most errorhandlers will simply work as expected; however, there is a caveat +concerning handlers for 404 and 405 exceptions. These errorhandlers are only +invoked from an appropriate ``raise`` statement or a call to ``abort`` in another +of the blueprint's view functions; they are not invoked by, e.g., an invalid URL +access. This is because the blueprint does not "own" a certain URL space, so +the application instance has no way of knowing which blueprint errorhandler it +should run if given an invalid URL. If you would like to execute different +handling strategies for these errors based on URL prefixes, they may be defined +at the application level using the ``request`` proxy object:: + + @app.errorhandler(404) + @app.errorhandler(405) + def _handle_api_error(ex): + if request.path.startswith('/api/'): + return jsonify_error(ex) + else: + return ex + More information on error handling see :ref:`errorpages`. diff -Nru flask-0.12.2/docs/changelog.rst flask-1.0.2/docs/changelog.rst --- flask-0.12.2/docs/changelog.rst 2015-03-11 15:05:50.000000000 +0000 +++ flask-1.0.2/docs/changelog.rst 2018-05-02 00:50:32.000000000 +0000 @@ -1 +1 @@ -.. include:: ../CHANGES +.. include:: ../CHANGES.rst diff -Nru flask-0.12.2/docs/cli.rst flask-1.0.2/docs/cli.rst --- flask-0.12.2/docs/cli.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/cli.rst 2018-05-02 00:50:32.000000000 +0000 @@ -1,89 +1,258 @@ +.. currentmodule:: flask + .. _cli: Command Line Interface ====================== -.. versionadded:: 0.11 +Installing Flask installs the ``flask`` script, a `Click`_ command line +interface, in your virtualenv. Executed from the terminal, this script gives +access to built-in, extension, and application-defined commands. The ``--help`` +option will give more information about any commands and options. -.. currentmodule:: flask +.. _Click: http://click.pocoo.org/ -One of the nice new features in Flask 0.11 is the built-in integration of -the `click `_ command line interface. This -enables a wide range of new features for the Flask ecosystem and your own -applications. - -Basic Usage ------------ - -After installation of Flask you will now find a :command:`flask` script -installed into your virtualenv. If you don't want to install Flask or you -have a special use-case you can also use ``python -m flask`` to accomplish -exactly the same. - -The way this script works is by providing access to all the commands on -your Flask application's :attr:`Flask.cli` instance as well as some -built-in commands that are always there. Flask extensions can also -register more commands there if they desire so. - -For the :command:`flask` script to work, an application needs to be -discovered. This is achieved by exporting the ``FLASK_APP`` environment -variable. It can be either set to an import path or to a filename of a -Python module that contains a Flask application. - -In that imported file the name of the app needs to be called ``app`` or -optionally be specified after a colon. For instance -``mymodule:application`` would tell it to use the `application` object in -the :file:`mymodule.py` file. -Given a :file:`hello.py` file with the application in it named ``app`` -this is how it can be run. +Application Discovery +--------------------- -Environment variables (On Windows use ``set`` instead of ``export``):: +The ``flask`` command is installed by Flask, not your application; it must be +told where to find your application in order to use it. The ``FLASK_APP`` +environment variable is used to specify how to load the application. + +Unix Bash (Linux, Mac, etc.):: + + $ export FLASK_APP=hello + $ flask run + +Windows CMD:: + + > set FLASK_APP=hello + > flask run + +Windows PowerShell:: + + > $env:FLASK_APP = "hello" + > flask run + +While ``FLASK_APP`` supports a variety of options for specifying your +application, most use cases should be simple. Here are the typical values: + +(nothing) + The file :file:`wsgi.py` is imported, automatically detecting an app + (``app``). This provides an easy way to create an app from a factory with + extra arguments. + +``FLASK_APP=hello`` + The name is imported, automatically detecting an app (``app``) or factory + (``create_app``). + +---- + +``FLASK_APP`` has three parts: an optional path that sets the current working +directory, a Python file or dotted import path, and an optional variable +name of the instance or factory. If the name is a factory, it can optionally +be followed by arguments in parentheses. The following values demonstrate these +parts: + +``FLASK_APP=src/hello`` + Sets the current working directory to ``src`` then imports ``hello``. + +``FLASK_APP=hello.web`` + Imports the path ``hello.web``. + +``FLASK_APP=hello:app2`` + Uses the ``app2`` Flask instance in ``hello``. + +``FLASK_APP="hello:create_app('dev')"`` + The ``create_app`` factory in ``hello`` is called with the string ``'dev'`` + as the argument. + +If ``FLASK_APP`` is not set, the command will look for a file called +:file:`wsgi.py` or :file:`app.py` and try to detect an application instance or +factory. + +Within the given import, the command looks for an application instance named +``app`` or ``application``, then any application instance. If no instance is +found, the command looks for a factory function named ``create_app`` or +``make_app`` that returns an instance. + +When calling an application factory, if the factory takes an argument named +``info``, then the :class:`~cli.ScriptInfo` instance is passed as a keyword +argument. If parentheses follow the factory name, their contents are parsed +as Python literals and passes as arguments to the function. This means that +strings must still be in quotes. + + +Run the Development Server +-------------------------- + +The :func:`run ` command will start the development server. It +replaces the :meth:`Flask.run` method in most cases. :: + + $ flask run + * Serving Flask app "hello" + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + +.. warning:: Do not use this command to run your application in production. + Only use the development server during development. The development server + is provided for convenience, but is not designed to be particularly secure, + stable, or efficient. See :ref:`deployment` for how to run in production. + + +Open a Shell +------------ + +To explore the data in your application, you can start an interactive Python +shell with the :func:`shell ` command. An application +context will be active, and the app instance will be imported. :: + + $ flask shell + Python 3.6.2 (default, Jul 20 2017, 03:52:27) + [GCC 7.1.1 20170630] on linux + App: example + Instance: /home/user/Projects/hello/instance + >>> + +Use :meth:`~Flask.shell_context_processor` to add other automatic imports. + + +Environments +------------ + +.. versionadded:: 1.0 + +The environment in which the Flask app runs is set by the +:envvar:`FLASK_ENV` environment variable. If not set it defaults to +``production``. The other recognized environment is ``development``. +Flask and extensions may choose to enable behaviors based on the +environment. + +If the env is set to ``development``, the ``flask`` command will enable +debug mode and ``flask run`` will enable the interactive debugger and +reloader. + +:: + + $ FLASK_ENV=development flask run + * Serving Flask app "hello" + * Environment: development + * Debug mode: on + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + * Restarting with inotify reloader + * Debugger is active! + * Debugger PIN: 223-456-919 + + +Debug Mode +---------- + +Debug mode will be enabled when :envvar:`FLASK_ENV` is ``development``, +as described above. If you want to control debug mode separately, use +:envvar:`FLASK_DEBUG`. The value ``1`` enables it, ``0`` disables it. + + +.. _dotenv: + +Environment Variables From dotenv +--------------------------------- + +Rather than setting ``FLASK_APP`` each time you open a new terminal, you can +use Flask's dotenv support to set environment variables automatically. + +If `python-dotenv`_ is installed, running the ``flask`` command will set +environment variables defined in the files :file:`.env` and :file:`.flaskenv`. +This can be used to avoid having to set ``FLASK_APP`` manually every time you +open a new terminal, and to set configuration using environment variables +similar to how some deployment services work. + +Variables set on the command line are used over those set in :file:`.env`, +which are used over those set in :file:`.flaskenv`. :file:`.flaskenv` should be +used for public variables, such as ``FLASK_APP``, while :file:`.env` should not +be committed to your repository so that it can set private variables. + +Directories are scanned upwards from the directory you call ``flask`` +from to locate the files. The current working directory will be set to the +location of the file, with the assumption that that is the top level project +directory. + +The files are only loaded by the ``flask`` command or calling +:meth:`~Flask.run`. If you would like to load these files when running in +production, you should call :func:`~cli.load_dotenv` manually. + +.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + +Setting Command Options +~~~~~~~~~~~~~~~~~~~~~~~ + +Click is configured to load default values for command options from +environment variables. The variables use the pattern +``FLASK_COMMAND_OPTION``. For example, to set the port for the run +command, instead of ``flask run --port 8000``: + +.. code-block:: none + + export FLASK_RUN_PORT=8000 + flask run + * Running on http://127.0.0.1:8000/ + +These can be added to the ``.flaskenv`` file just like ``FLASK_APP`` to +control default command options. + + +Disable dotenv +~~~~~~~~~~~~~~ + +The ``flask`` command will show a message if it detects dotenv files but +python-dotenv is not installed. + +.. code-block:: none - export FLASK_APP=hello flask run + * Tip: There are .env files present. Do "pip install python-dotenv" to use them. + +You can tell Flask not to load dotenv files even when python-dotenv is +installed by setting the ``FLASK_SKIP_DOTENV`` environment variable. +This can be useful if you want to load them manually, or if you're using +a project runner that loads them already. Keep in mind that the +environment variables must be set before the app loads or it won't +configure as expected. -Or with a filename:: +.. code-block:: none - export FLASK_APP=/path/to/hello.py + export FLASK_SKIP_DOTENV=1 flask run -Virtualenv Integration ----------------------- -If you are constantly working with a virtualenv you can also put the -``export FLASK_APP`` into your ``activate`` script by adding it to the -bottom of the file. That way every time you activate your virtualenv you -automatically also activate the correct application name. +Environment Variables From virtualenv +------------------------------------- -Debug Flag ----------- +If you do not want to install dotenv support, you can still set environment +variables by adding them to the end of the virtualenv's :file:`activate` +script. Activating the virtualenv will set the variables. -The :command:`flask` script can also be instructed to enable the debug -mode of the application automatically by exporting ``FLASK_DEBUG``. If -set to ``1`` debug is enabled or ``0`` disables it:: +Unix Bash, :file:`venv/bin/activate`:: - export FLASK_DEBUG=1 + export FLASK_APP=hello -Running a Shell ---------------- +Windows CMD, :file:`venv\\Scripts\\activate.bat`:: -To run an interactive Python shell you can use the ``shell`` command:: + set FLASK_APP=hello - flask shell +It is preferred to use dotenv support over this, since :file:`.flaskenv` can be +committed to the repository so that it works automatically wherever the project +is checked out. -This will start up an interactive Python shell, setup the correct -application context and setup the local variables in the shell. This is -done by invoking the :meth:`Flask.make_shell_context` method of the -application. By default you have access to your ``app`` and :data:`g`. Custom Commands --------------- -If you want to add more commands to the shell script you can do this -easily. Flask uses `click`_ for the command interface which makes -creating custom commands very easy. For instance if you want a shell -command to initialize the database you can do this:: +The ``flask`` command is implemented using `Click`_. See that project's +documentation for full information about writing commands. + +This example adds the command ``create_user`` that takes the argument +``name``. :: import click from flask import Flask @@ -91,158 +260,207 @@ app = Flask(__name__) @app.cli.command() - def initdb(): - """Initialize the database.""" - click.echo('Init the db') + @click.argument('name') + def create_user(name): + ... + +:: -The command will then show up on the command line:: + flask create_user admin + +This example adds the same command, but as ``user create``, a command in a +group. This is useful if you want to organize multiple related commands. :: + + import click + from flask import Flask + from flask.cli import AppGroup + + app = Flask(__name__) + user_cli = AppGroup('user') + + @user_cli.command('create') + @click.argument('name') + def create_user(name): + ... + + app.cli.add_command(user_cli) + +:: + + flask user create demo + +See :ref:`testing-cli` for an overview of how to test your custom +commands. - $ flask initdb - Init the db Application Context -------------------- +~~~~~~~~~~~~~~~~~~~ + +Commands added using the Flask app's :attr:`~Flask.cli` +:meth:`~cli.AppGroup.command` decorator will be executed with an application +context pushed, so your command and extensions have access to the app and its +configuration. If you create a command using the Click :func:`~click.command` +decorator instead of the Flask decorator, you can use +:func:`~cli.with_appcontext` to get the same behavior. :: + + import click + from flask.cli import with_appcontext + + @click.command + @with_appcontext + def do_work(): + ... -Most commands operate on the application so it makes a lot of sense if -they have the application context setup. Because of this, if you register -a callback on ``app.cli`` with the :meth:`~flask.cli.AppGroup.command` the -callback will automatically be wrapped through :func:`cli.with_appcontext` -which informs the cli system to ensure that an application context is set -up. This behavior is not available if a command is added later with -:func:`~click.Group.add_command` or through other means. + app.cli.add_command(do_work) -It can also be disabled by passing ``with_appcontext=False`` to the -decorator:: +If you're sure a command doesn't need the context, you can disable it:: @app.cli.command(with_appcontext=False) - def example(): - pass + def do_work(): + ... -Factory Functions ------------------ -In case you are using factory functions to create your application (see -:ref:`app-factories`) you will discover that the :command:`flask` command -cannot work with them directly. Flask won't be able to figure out how to -instantiate your application properly by itself. Because of this reason -the recommendation is to create a separate file that instantiates -applications. This is not the only way to make this work. Another is the -:ref:`custom-scripts` support. +Plugins +------- -For instance if you have a factory function that creates an application -from a filename you could make a separate file that creates such an -application from an environment variable. +Flask will automatically load commands specified in the ``flask.commands`` +`entry point`_. This is useful for extensions that want to add commands when +they are installed. Entry points are specified in :file:`setup.py` :: + + from setuptools import setup -This could be a file named :file:`autoapp.py` with these contents:: + setup( + name='flask-my-extension', + ..., + entry_points={ + 'flask.commands': [ + 'my-command=flask_my_extension.commands:cli' + ], + }, + ) - import os - from yourapplication import create_app - app = create_app(os.environ['YOURAPPLICATION_CONFIG']) -Once this has happened you can make the :command:`flask` command automatically -pick it up:: +.. _entry point: https://packaging.python.org/tutorials/distributing-packages/#entry-points - export YOURAPPLICATION_CONFIG=/path/to/config.cfg - export FLASK_APP=/path/to/autoapp.py +Inside :file:`flask_my_extension/commands.py` you can then export a Click +object:: + + import click + + @click.command() + def cli(): + ... + +Once that package is installed in the same virtualenv as your Flask project, +you can run ``flask my-command`` to invoke the command. -From this point onwards :command:`flask` will find your application. .. _custom-scripts: Custom Scripts -------------- -While the most common way is to use the :command:`flask` command, you can -also make your own "driver scripts". Since Flask uses click for the -scripts there is no reason you cannot hook these scripts into any click -application. There is one big caveat and that is, that commands -registered to :attr:`Flask.cli` will expect to be (indirectly at least) -launched from a :class:`flask.cli.FlaskGroup` click group. This is -necessary so that the commands know which Flask application they have to -work with. - -To understand why you might want custom scripts you need to understand how -click finds and executes the Flask application. If you use the -:command:`flask` script you specify the application to work with on the -command line or environment variable as an import name. This is simple -but it has some limitations. Primarily it does not work with application -factory functions (see :ref:`app-factories`). - -With a custom script you don't have this problem as you can fully -customize how the application will be created. This is very useful if you -write reusable applications that you want to ship to users and they should -be presented with a custom management script. - -To explain all of this, here is an example :file:`manage.py` script that -manages a hypothetical wiki application. We will go through the details -afterwards:: +When you are using the app factory pattern, it may be more convenient to define +your own Click script. Instead of using ``FLASK_APP`` and letting Flask load +your application, you can create your own Click object and export it as a +`console script`_ entry point. + +Create an instance of :class:`~cli.FlaskGroup` and pass it the factory:: - import os import click + from flask import Flask from flask.cli import FlaskGroup - def create_wiki_app(info): - from yourwiki import create_app - return create_app( - config=os.environ.get('WIKI_CONFIG', 'wikiconfig.py')) + def create_app(): + app = Flask('wiki') + # other setup + return app - @click.group(cls=FlaskGroup, create_app=create_wiki_app) + @click.group(cls=FlaskGroup, create_app=create_app) def cli(): - """This is a management script for the wiki application.""" - - if __name__ == '__main__': - cli() - -That's a lot of code for not much, so let's go through all parts step by -step. - -1. First we import the ``click`` library as well as the click extensions - from the ``flask.cli`` package. Primarily we are here interested - in the :class:`~flask.cli.FlaskGroup` click group. -2. The next thing we do is defining a function that is invoked with the - script info object (:class:`~flask.cli.ScriptInfo`) from Flask and its - purpose is to fully import and create the application. This can - either directly import an application object or create it (see - :ref:`app-factories`). In this case we load the config from an - environment variable. -3. Next step is to create a :class:`FlaskGroup`. In this case we just - make an empty function with a help doc string that just does nothing - and then pass the ``create_wiki_app`` function as a factory function. - - Whenever click now needs to operate on a Flask application it will - call that function with the script info and ask for it to be created. -4. All is rounded up by invoking the script. - -CLI Plugins ------------ - -Flask extensions can always patch the :attr:`Flask.cli` instance with more -commands if they want. However there is a second way to add CLI plugins -to Flask which is through ``setuptools``. If you make a Python package that -should export a Flask command line plugin you can ship a :file:`setup.py` file -that declares an entrypoint that points to a click command: + """Management script for the Wiki application.""" -Example :file:`setup.py`:: +Define the entry point in :file:`setup.py`:: from setuptools import setup setup( name='flask-my-extension', - ... - entry_points=''' - [flask.commands] - my-command=mypackage.commands:cli - ''', + ..., + entry_points={ + 'console_scripts': [ + 'wiki=wiki:cli' + ], + }, ) -Inside :file:`mypackage/commands.py` you can then export a Click object:: +Install the application in the virtualenv in editable mode and the custom +script is available. Note that you don't need to set ``FLASK_APP``. :: - import click + $ pip install -e . + $ wiki run - @click.command() - def cli(): - """This is an example command.""" +.. admonition:: Errors in Custom Scripts + + When using a custom script, if you introduce an error in your + module-level code, the reloader will fail because it can no longer + load the entry point. + + The ``flask`` command, being separate from your code, does not have + this issue and is recommended in most cases. + +.. _console script: https://packaging.python.org/tutorials/distributing-packages/#console-scripts + + +PyCharm Integration +------------------- -Once that package is installed in the same virtualenv as Flask itself you -can run ``flask my-command`` to invoke your command. This is useful to -provide extra functionality that Flask itself cannot ship. +Prior to PyCharm 2018.1, the Flask CLI features weren't yet fully +integrated into PyCharm. We have to do a few tweaks to get them working +smoothly. These instructions should be similar for any other IDE you +might want to use. + +In PyCharm, with your project open, click on *Run* from the menu bar and +go to *Edit Configurations*. You'll be greeted by a screen similar to +this: + +.. image:: _static/pycharm-runconfig.png + :align: center + :class: screenshot + :alt: screenshot of pycharm's run configuration settings + +There's quite a few options to change, but once we've done it for one +command, we can easily copy the entire configuration and make a single +tweak to give us access to other commands, including any custom ones you +may implement yourself. + +Click the + (*Add New Configuration*) button and select *Python*. Give +the configuration a good descriptive name such as "Run Flask Server". +For the ``flask run`` command, check "Single instance only" since you +can't run the server more than once at the same time. + +Select *Module name* from the dropdown (**A**) then input ``flask``. + +The *Parameters* field (**B**) is set to the CLI command to execute +(with any arguments). In this example we use ``run``, which will run +the development server. + +You can skip this next step if you're using :ref:`dotenv`. We need to +add an environment variable (**C**) to identify our application. Click +on the browse button and add an entry with ``FLASK_APP`` on the left and +the Python import or file on the right (``hello`` for example). + +Next we need to set the working directory (**D**) to be the folder where +our application resides. + +If you have installed your project as a package in your virtualenv, you +may untick the *PYTHONPATH* options (**E**). This will more accurately +match how you deploy the app later. + +Click *Apply* to save the configuration, or *OK* to save and close the +window. Select the configuration in the main PyCharm window and click +the play button next to it to run the server. + +Now that we have a configuration which runs ``flask run`` from within +PyCharm, we can copy that configuration and alter the *Script* argument +to run a different CLI command, e.g. ``flask shell``. diff -Nru flask-0.12.2/docs/config.rst flask-1.0.2/docs/config.rst --- flask-0.12.2/docs/config.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/config.rst 2018-05-02 00:50:32.000000000 +0000 @@ -3,8 +3,6 @@ Configuration Handling ====================== -.. versionadded:: 0.3 - Applications need some kind of configuration. There are different settings you might want to change depending on the application environment like toggling the debug mode, setting the secret key, and other such @@ -22,6 +20,7 @@ values and also where extensions can put their configuration values. But this is also where you can have your own configuration. + Configuration Basics -------------------- @@ -29,193 +28,326 @@ can be modified just like any dictionary:: app = Flask(__name__) - app.config['DEBUG'] = True + app.config['TESTING'] = True Certain configuration values are also forwarded to the :attr:`~flask.Flask` object so you can read and write them from there:: - app.debug = True + app.testing = True To update multiple keys at once you can use the :meth:`dict.update` method:: app.config.update( - DEBUG=True, - SECRET_KEY='...' + TESTING=True, + SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/' ) + +Environment and Debug Features +------------------------------ + +The :data:`ENV` and :data:`DEBUG` config values are special because they +may behave inconsistently if changed after the app has begun setting up. +In order to set the environment and debug mode reliably, Flask uses +environment variables. + +The environment is used to indicate to Flask, extensions, and other +programs, like Sentry, what context Flask is running in. It is +controlled with the :envvar:`FLASK_ENV` environment variable and +defaults to ``production``. + +Setting :envvar:`FLASK_ENV` to ``development`` will enable debug mode. +``flask run`` will use the interactive debugger and reloader by default +in debug mode. To control this separately from the environment, use the +:envvar:`FLASK_DEBUG` flag. + +.. versionchanged:: 1.0 + Added :envvar:`FLASK_ENV` to control the environment separately + from debug mode. The development environment enables debug mode. + +To switch Flask to the development environment and enable debug mode, +set :envvar:`FLASK_ENV`:: + + $ export FLASK_ENV=development + $ flask run + +(On Windows, use ``set`` instead of ``export``.) + +Using the environment variables as described above is recommended. While +it is possible to set :data:`ENV` and :data:`DEBUG` in your config or +code, this is strongly discouraged. They can't be read early by the +``flask`` command, and some systems or extensions may have already +configured themselves based on a previous value. + + Builtin Configuration Values ---------------------------- The following configuration values are used internally by Flask: -.. tabularcolumns:: |p{6.5cm}|p{8.5cm}| +.. py:data:: ENV + + What environment the app is running in. Flask and extensions may + enable behaviors based on the environment, such as enabling debug + mode. The :attr:`~flask.Flask.env` attribute maps to this config + key. This is set by the :envvar:`FLASK_ENV` environment variable and + may not behave as expected if set in code. + + **Do not enable development when deploying in production.** + + Default: ``'production'`` + + .. versionadded:: 1.0 + +.. py:data:: DEBUG + + Whether debug mode is enabled. When using ``flask run`` to start the + development server, an interactive debugger will be shown for + unhandled exceptions, and the server will be reloaded when code + changes. The :attr:`~flask.Flask.debug` attribute maps to this + config key. This is enabled when :data:`ENV` is ``'development'`` + and is overridden by the ``FLASK_DEBUG`` environment variable. It + may not behave as expected if set in code. + + **Do not enable debug mode when deploying in production.** + + Default: ``True`` if :data:`ENV` is ``'development'``, or ``False`` + otherwise. + +.. py:data:: TESTING + + Enable testing mode. Exceptions are propagated rather than handled by the + the app's error handlers. Extensions may also change their behavior to + facilitate easier testing. You should enable this in your own tests. + + Default: ``False`` + +.. py:data:: PROPAGATE_EXCEPTIONS + + Exceptions are re-raised rather than being handled by the app's error + handlers. If not set, this is implicitly true if ``TESTING`` or ``DEBUG`` + is enabled. + + Default: ``None`` + +.. py:data:: PRESERVE_CONTEXT_ON_EXCEPTION + + Don't pop the request context when an exception occurs. If not set, this + is true if ``DEBUG`` is true. This allows debuggers to introspect the + request data on errors, and should normally not need to be set directly. + + Default: ``None`` + +.. py:data:: TRAP_HTTP_EXCEPTIONS + + If there is no handler for an ``HTTPException``-type exception, re-raise it + to be handled by the interactive debugger instead of returning it as a + simple error response. + + Default: ``False`` + +.. py:data:: TRAP_BAD_REQUEST_ERRORS + + Trying to access a key that doesn't exist from request dicts like ``args`` + and ``form`` will return a 400 Bad Request error page. Enable this to treat + the error as an unhandled exception instead so that you get the interactive + debugger. This is a more specific version of ``TRAP_HTTP_EXCEPTIONS``. If + unset, it is enabled in debug mode. + + Default: ``None`` + +.. py:data:: SECRET_KEY + + A secret key that will be used for securely signing the session cookie + and can be used for any other security related needs by extensions or your + application. It should be a long random string of bytes, although unicode + is accepted too. For example, copy the output of this to your config:: + + python -c 'import os; print(os.urandom(16))' + b'_5#y2L"F4Q8z\n\xec]/' + + **Do not reveal the secret key when posting questions or committing code.** + + Default: ``None`` + +.. py:data:: SESSION_COOKIE_NAME + + The name of the session cookie. Can be changed in case you already have a + cookie with the same name. + + Default: ``'session'`` + +.. py:data:: SESSION_COOKIE_DOMAIN + + The domain match rule that the session cookie will be valid for. If not + set, the cookie will be valid for all subdomains of :data:`SERVER_NAME`. + If ``False``, the cookie's domain will not be set. + + Default: ``None`` + +.. py:data:: SESSION_COOKIE_PATH + + The path that the session cookie will be valid for. If not set, the cookie + will be valid underneath ``APPLICATION_ROOT`` or ``/`` if that is not set. + + Default: ``None`` + +.. py:data:: SESSION_COOKIE_HTTPONLY + + Browsers will not allow JavaScript access to cookies marked as "HTTP only" + for security. + + Default: ``True`` + +.. py:data:: SESSION_COOKIE_SECURE + + Browsers will only send cookies with requests over HTTPS if the cookie is + marked "secure". The application must be served over HTTPS for this to make + sense. + + Default: ``False`` + +.. py:data:: SESSION_COOKIE_SAMESITE + + Restrict how cookies are sent with requests from external sites. Can + be set to ``'Lax'`` (recommended) or ``'Strict'``. + See :ref:`security-cookie`. + + Default: ``None`` + + .. versionadded:: 1.0 + +.. py:data:: PERMANENT_SESSION_LIFETIME + + If ``session.permanent`` is true, the cookie's expiration will be set this + number of seconds in the future. Can either be a + :class:`datetime.timedelta` or an ``int``. + + Flask's default cookie implementation validates that the cryptographic + signature is not older than this value. + + Default: ``timedelta(days=31)`` (``2678400`` seconds) + +.. py:data:: SESSION_REFRESH_EACH_REQUEST + + Control whether the cookie is sent with every response when + ``session.permanent`` is true. Sending the cookie every time (the default) + can more reliably keep the session from expiring, but uses more bandwidth. + Non-permanent sessions are not affected. + + Default: ``True`` -================================= ========================================= -``DEBUG`` enable/disable debug mode -``TESTING`` enable/disable testing mode -``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the - propagation of exceptions. If not set or - explicitly set to ``None`` this is - implicitly true if either ``TESTING`` or - ``DEBUG`` is true. -``PRESERVE_CONTEXT_ON_EXCEPTION`` By default if the application is in - debug mode the request context is not - popped on exceptions to enable debuggers - to introspect the data. This can be - disabled by this key. You can also use - this setting to force-enable it for non - debug execution which might be useful to - debug production applications (but also - very risky). -``SECRET_KEY`` the secret key -``SESSION_COOKIE_NAME`` the name of the session cookie -``SESSION_COOKIE_DOMAIN`` the domain for the session cookie. If - this is not set, the cookie will be - valid for all subdomains of - ``SERVER_NAME``. -``SESSION_COOKIE_PATH`` the path for the session cookie. If - this is not set the cookie will be valid - for all of ``APPLICATION_ROOT`` or if - that is not set for ``'/'``. -``SESSION_COOKIE_HTTPONLY`` controls if the cookie should be set - with the httponly flag. Defaults to - ``True``. -``SESSION_COOKIE_SECURE`` controls if the cookie should be set - with the secure flag. Defaults to - ``False``. -``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as - :class:`datetime.timedelta` object. - Starting with Flask 0.8 this can also be - an integer representing seconds. -``SESSION_REFRESH_EACH_REQUEST`` this flag controls how permanent - sessions are refreshed. If set to ``True`` - (which is the default) then the cookie - is refreshed each request which - automatically bumps the lifetime. If - set to ``False`` a `set-cookie` header is - only sent if the session is modified. - Non permanent sessions are not affected - by this. -``USE_X_SENDFILE`` enable/disable x-sendfile -``LOGGER_NAME`` the name of the logger -``LOGGER_HANDLER_POLICY`` the policy of the default logging - handler. The default is ``'always'`` - which means that the default logging - handler is always active. ``'debug'`` - will only activate logging in debug - mode, ``'production'`` will only log in - production and ``'never'`` disables it - entirely. -``SERVER_NAME`` the name and port number of the server. - Required for subdomain support (e.g.: - ``'myapp.dev:5000'``) Note that - localhost does not support subdomains so - setting this to “localhost” does not - help. Setting a ``SERVER_NAME`` also - by default enables URL generation - without a request context but with an - application context. -``APPLICATION_ROOT`` If the application does not occupy - a whole domain or subdomain this can - be set to the path where the application - is configured to live. This is for - session cookie as path value. If - domains are used, this should be - ``None``. -``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will - reject incoming requests with a - content length greater than this by - returning a 413 status code. -``SEND_FILE_MAX_AGE_DEFAULT`` Default cache control max age to use with - :meth:`~flask.Flask.send_static_file` (the - default static file handler) and - :func:`~flask.send_file`, as - :class:`datetime.timedelta` or as seconds. - Override this value on a per-file - basis using the - :meth:`~flask.Flask.get_send_file_max_age` - hook on :class:`~flask.Flask` or - :class:`~flask.Blueprint`, - respectively. Defaults to 43200 (12 hours). -``TRAP_HTTP_EXCEPTIONS`` If this is set to ``True`` Flask will - not execute the error handlers of HTTP - exceptions but instead treat the - exception like any other and bubble it - through the exception stack. This is - helpful for hairy debugging situations - where you have to find out where an HTTP - exception is coming from. -``TRAP_BAD_REQUEST_ERRORS`` Werkzeug's internal data structures that - deal with request specific data will - raise special key errors that are also - bad request exceptions. Likewise many - operations can implicitly fail with a - BadRequest exception for consistency. - Since it's nice for debugging to know - why exactly it failed this flag can be - used to debug those situations. If this - config is set to ``True`` you will get - a regular traceback instead. -``PREFERRED_URL_SCHEME`` The URL scheme that should be used for - URL generation if no URL scheme is - available. This defaults to ``http``. -``JSON_AS_ASCII`` By default Flask serialize object to - ascii-encoded JSON. If this is set to - ``False`` Flask will not encode to ASCII - and output strings as-is and return - unicode strings. ``jsonify`` will - automatically encode it in ``utf-8`` - then for transport for instance. -``JSON_SORT_KEYS`` By default Flask will serialize JSON - objects in a way that the keys are - ordered. This is done in order to - ensure that independent of the hash seed - of the dictionary the return value will - be consistent to not trash external HTTP - caches. You can override the default - behavior by changing this variable. - This is not recommended but might give - you a performance improvement on the - cost of cacheability. -``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` (the default) - jsonify responses will be pretty printed - if they are not requested by an - XMLHttpRequest object (controlled by - the ``X-Requested-With`` header) -``JSONIFY_MIMETYPE`` MIME type used for jsonify responses. -``TEMPLATES_AUTO_RELOAD`` Whether to check for modifications of - the template source and reload it - automatically. By default the value is - ``None`` which means that Flask checks - original file only in debug mode. -``EXPLAIN_TEMPLATE_LOADING`` If this is enabled then every attempt to - load a template will write an info - message to the logger explaining the - attempts to locate the template. This - can be useful to figure out why - templates cannot be found or wrong - templates appear to be loaded. -================================= ========================================= - -.. admonition:: More on ``SERVER_NAME`` - - The ``SERVER_NAME`` key is used for the subdomain support. Because - Flask cannot guess the subdomain part without the knowledge of the - actual server name, this is required if you want to work with - subdomains. This is also used for the session cookie. - - Please keep in mind that not only Flask has the problem of not knowing - what subdomains are, your web browser does as well. Most modern web - browsers will not allow cross-subdomain cookies to be set on a - server name without dots in it. So if your server name is - ``'localhost'`` you will not be able to set a cookie for - ``'localhost'`` and every subdomain of it. Please choose a different - server name in that case, like ``'myapplication.local'`` and add - this name + the subdomains you want to use into your host config - or setup a local `bind`_. +.. py:data:: USE_X_SENDFILE -.. _bind: https://www.isc.org/downloads/bind/ + When serving files, set the ``X-Sendfile`` header instead of serving the + data with Flask. Some web servers, such as Apache, recognize this and serve + the data more efficiently. This only makes sense when using such a server. + + Default: ``False`` + +.. py:data:: SEND_FILE_MAX_AGE_DEFAULT + + When serving files, set the cache control max age to this number of + seconds. Can either be a :class:`datetime.timedelta` or an ``int``. + Override this value on a per-file basis using + :meth:`~flask.Flask.get_send_file_max_age` on the application or blueprint. + + Default: ``timedelta(hours=12)`` (``43200`` seconds) + +.. py:data:: SERVER_NAME + + Inform the application what host and port it is bound to. Required + for subdomain route matching support. + + If set, will be used for the session cookie domain if + :data:`SESSION_COOKIE_DOMAIN` is not set. Modern web browsers will + not allow setting cookies for domains without a dot. To use a domain + locally, add any names that should route to the app to your + ``hosts`` file. :: + + 127.0.0.1 localhost.dev + + If set, ``url_for`` can generate external URLs with only an application + context instead of a request context. + + Default: ``None`` + +.. py:data:: APPLICATION_ROOT + + Inform the application what path it is mounted under by the application / + web server. + + Will be used for the session cookie path if ``SESSION_COOKIE_PATH`` is not + set. + + Default: ``'/'`` + +.. py:data:: PREFERRED_URL_SCHEME + + Use this scheme for generating external URLs when not in a request context. + + Default: ``'http'`` + +.. py:data:: MAX_CONTENT_LENGTH + + Don't read more than this many bytes from the incoming request data. If not + set and the request does not specify a ``CONTENT_LENGTH``, no data will be + read for security. + + Default: ``None`` + +.. py:data:: JSON_AS_ASCII + + Serialize objects to ASCII-encoded JSON. If this is disabled, the JSON + will be returned as a Unicode string, or encoded as ``UTF-8`` by + ``jsonify``. This has security implications when rendering the JSON in + to JavaScript in templates, and should typically remain enabled. + + Default: ``True`` + +.. py:data:: JSON_SORT_KEYS + + Sort the keys of JSON objects alphabetically. This is useful for caching + because it ensures the data is serialized the same way no matter what + Python's hash seed is. While not recommended, you can disable this for a + possible performance improvement at the cost of caching. + + Default: ``True`` + +.. py:data:: JSONIFY_PRETTYPRINT_REGULAR + + ``jsonify`` responses will be output with newlines, spaces, and indentation + for easier reading by humans. Always enabled in debug mode. + + Default: ``False`` + +.. py:data:: JSONIFY_MIMETYPE + + The mimetype of ``jsonify`` responses. + + Default: ``'application/json'`` + +.. py:data:: TEMPLATES_AUTO_RELOAD + + Reload templates when they are changed. If not set, it will be enabled in + debug mode. + + Default: ``None`` + +.. py:data:: EXPLAIN_TEMPLATE_LOADING + + Log debugging information tracing how a template file was loaded. This can + be useful to figure out why a template was not loaded or the wrong file + appears to be loaded. + + Default: ``False`` + +.. py:data:: MAX_COOKIE_SIZE + + Warn if cookie headers are larger than this many bytes. Defaults to + ``4093``. Larger cookies may be silently ignored by browsers. Set to + ``0`` to disable the warning. .. versionadded:: 0.4 ``LOGGER_NAME`` @@ -245,6 +377,19 @@ ``SESSION_REFRESH_EACH_REQUEST``, ``TEMPLATES_AUTO_RELOAD``, ``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING`` +.. versionchanged:: 1.0 + ``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See + :ref:`logging` for information about configuration. + + Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment + variable. + + Added :data:`SESSION_COOKIE_SAMESITE` to control the session + cookie's ``SameSite`` option. + + Added :data:`MAX_COOKIE_SIZE` to control a warning from Werkzeug. + + Configuring from Files ---------------------- @@ -284,7 +429,7 @@ # Example configuration DEBUG = False - SECRET_KEY = '?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' + SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/' Make sure to load the configuration very early on, so that extensions have the ability to access the configuration when starting up. There are other @@ -292,6 +437,54 @@ complete reference, read the :class:`~flask.Config` object's documentation. +Configuring from Environment Variables +-------------------------------------- + +In addition to pointing to configuration files using environment variables, you +may find it useful (or necessary) to control your configuration values directly +from the environment. + +Environment variables can be set on Linux or OS X with the export command in +the shell before starting the server:: + + $ export SECRET_KEY='5f352379324c22463451387a0aec5d2f' + $ export DEBUG=False + $ python run-app.py + * Running on http://127.0.0.1:5000/ + * Restarting with reloader... + +On Windows systems use the `set` builtin instead:: + + >set SECRET_KEY='5f352379324c22463451387a0aec5d2f' + >set DEBUG=False + +While this approach is straightforward to use, it is important to remember that +environment variables are strings -- they are not automatically deserialized +into Python types. + +Here is an example of a configuration file that uses environment variables:: + + # Example configuration + import os + + ENVIRONMENT_DEBUG = os.environ.get("DEBUG", default=False) + if ENVIRONMENT_DEBUG.lower() in ("f", "false"): + ENVIRONMENT_DEBUG = False + + DEBUG = ENVIRONMENT_DEBUG + SECRET_KEY = os.environ.get("SECRET_KEY", default=None) + if not SECRET_KEY: + raise ValueError("No secret key set for Flask application") + + +Notice that any value besides an empty string will be interpreted as a boolean +``True`` value in Python, which requires care if an environment explicitly sets +values intended to be ``False``. + +Make sure to load the configuration very early on, so that extensions have the +ability to access the configuration when starting up. There are other methods +on the config object as well to load from individual files. For a complete +reference, read the :class:`~flask.Config` class documentation. Configuration Best Practices ---------------------------- @@ -344,7 +537,7 @@ class Config(object): DEBUG = False TESTING = False - DATABASE_URI = 'sqlite://:memory:' + DATABASE_URI = 'sqlite:///:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' diff -Nru flask-0.12.2/docs/conf.py flask-1.0.2/docs/conf.py --- flask-0.12.2/docs/conf.py 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/conf.py 2018-05-02 00:50:32.000000000 +0000 @@ -1,284 +1,103 @@ # -*- coding: utf-8 -*- -# -# Flask documentation build configuration file, created by -# sphinx-quickstart on Tue Apr 6 15:24:58 2010. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. from __future__ import print_function -from datetime import datetime -import os -import sys -import pkg_resources - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append(os.path.join(os.path.dirname(__file__), '_themes')) -sys.path.append(os.path.dirname(__file__)) -# -- General configuration ----------------------------------------------------- +import inspect +import re -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +from pallets_sphinx_themes import DocVersion, ProjectLink, get_version -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'flaskdocext' -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +# Project -------------------------------------------------------------- -# The suffix of source filenames. -source_suffix = '.rst' +project = 'Flask' +copyright = '2010 Pallets Team' +author = 'Pallets Team' +release, version = get_version('Flask') -# The encoding of source files. -#source_encoding = 'utf-8-sig' +# General -------------------------------------------------------------- -# The master toctree document. master_doc = 'index' -# General information about the project. -project = u'Flask' -copyright = u'2010 - {0}, Armin Ronacher'.format(datetime.utcnow().year) - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -try: - release = pkg_resources.get_distribution('Flask').version -except pkg_resources.DistributionNotFound: - print('Flask must be installed to build the documentation.') - print('Install from source using `pip install -e .` in a virtualenv.') - sys.exit(1) - -if 'dev' in release: - release = ''.join(release.partition('dev')[:2]) - -version = '.'.join(release.split('.')[:2]) - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = ['_themes'] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. Do not set, template magic! -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -html_favicon = '_static/flask-favicon.ico' +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinxcontrib.log_cabinet', +] -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +intersphinx_mapping = { + 'python': ('https://docs.python.org/3/', None), + 'werkzeug': ('http://werkzeug.pocoo.org/docs/', None), + 'click': ('http://click.pocoo.org/', None), + 'jinja': ('http://jinja.pocoo.org/docs/', None), + 'itsdangerous': ('https://pythonhosted.org/itsdangerous', None), + 'sqlalchemy': ('https://docs.sqlalchemy.org/en/latest/', None), + 'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None), + 'blinker': ('https://pythonhosted.org/blinker/', None), +} -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True +# HTML ----------------------------------------------------------------- -# Custom sidebar templates, maps document names to template names. +html_theme = 'flask' +html_context = { + 'project_links': [ + ProjectLink('Donate to Pallets', 'https://psfmember.org/civicrm/contribute/transact?reset=1&id=20'), + ProjectLink('Flask Website', 'https://palletsprojects.com/p/flask/'), + ProjectLink('PyPI releases', 'https://pypi.org/project/Flask/'), + ProjectLink('Source Code', 'https://github.com/pallets/flask/'), + ProjectLink( + 'Issue Tracker', 'https://github.com/pallets/flask/issues/'), + ], + 'versions': [ + DocVersion('dev', 'Development', 'unstable'), + DocVersion('1.0', 'Flask 1.0', 'stable'), + DocVersion('0.12', 'Flask 0.12'), + ], + 'canonical_url': 'http://flask.pocoo.org/docs/{}/'.format(version), + 'carbon_ads_args': 'zoneid=1673&serve=C6AILKT&placement=pocooorg', +} html_sidebars = { 'index': [ - 'sidebarintro.html', - 'sourcelink.html', - 'searchbox.html' + 'project.html', + 'versions.html', + 'carbon_ads.html', + 'searchbox.html', ], '**': [ - 'sidebarlogo.html', 'localtoc.html', 'relations.html', - 'sourcelink.html', - 'searchbox.html' + 'versions.html', + 'carbon_ads.html', + 'searchbox.html', ] } +html_static_path = ['_static'] +html_favicon = '_static/flask-favicon.ico' +html_logo = '_static/flask.png' +html_additional_pages = { + '404': '404.html', +} +html_show_sourcelink = False -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -html_use_modindex = False - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -html_show_sphinx = False - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'Flaskdoc' - - -# -- Options for LaTeX output -------------------------------------------------- +# LaTeX ---------------------------------------------------------------- -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('latexindex', 'Flask.tex', u'Flask Documentation', u'Armin Ronacher', 'manual'), + (master_doc, 'Flask.tex', 'Flask Documentation', 'Pallets Team', 'manual'), ] - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. latex_use_modindex = False - latex_elements = { - 'fontpkg': r'\usepackage{mathpazo}', 'papersize': 'a4paper', 'pointsize': '12pt', - 'preamble': r'\usepackage{flaskstyle}' + 'fontpkg': r'\usepackage{mathpazo}', + 'preamble': r'\usepackage{flaskstyle}', } latex_use_parts = True - latex_additional_files = ['flaskstyle.sty', 'logo.pdf'] +# linkcheck ------------------------------------------------------------ -# -- Options for Epub output --------------------------------------------------- - -# Bibliographic Dublin Core info. -#epub_title = '' -#epub_author = '' -#epub_publisher = '' -#epub_copyright = '' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -#epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -#epub_identifier = '' - -# A unique identification for the text. -#epub_uid = '' - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -#epub_exclude_files = [] +linkcheck_anchors = False -# The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 +# Local Extensions ----------------------------------------------------- -intersphinx_mapping = { - 'python': ('https://docs.python.org/3/', None), - 'werkzeug': ('http://werkzeug.pocoo.org/docs/', None), - 'click': ('http://click.pocoo.org/', None), - 'jinja': ('http://jinja.pocoo.org/docs/', None), - 'sqlalchemy': ('http://docs.sqlalchemy.org/en/latest/', None), - 'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None), - 'blinker': ('https://pythonhosted.org/blinker/', None) -} - -try: - __import__('flask_theme_support') - pygments_style = 'flask_theme_support.FlaskyStyle' - html_theme = 'flask' - html_theme_options = { - 'touch_icon': 'touch-icon.png' - } -except ImportError: - print('-' * 74) - print('Warning: Flask themes unavailable. Building with default theme') - print('If you want the Flask themes, run this command and build again:') - print() - print(' git submodule update --init') - print('-' * 74) - - -# unwrap decorators def unwrap_decorators(): import sphinx.util.inspect as inspect import functools @@ -295,5 +114,62 @@ return rv functools.update_wrapper = update_wrapper + unwrap_decorators() del unwrap_decorators + + +_internal_mark_re = re.compile(r'^\s*:internal:\s*$(?m)', re.M) + + +def skip_internal(app, what, name, obj, skip, options): + docstring = inspect.getdoc(obj) or '' + + if skip or _internal_mark_re.search(docstring) is not None: + return True + + +def cut_module_meta(app, what, name, obj, options, lines): + """Remove metadata from autodoc output.""" + if what != 'module': + return + + lines[:] = [ + line for line in lines + if not line.startswith((':copyright:', ':license:')) + ] + + +def github_link( + name, rawtext, text, lineno, inliner, options=None, content=None +): + app = inliner.document.settings.env.app + release = app.config.release + base_url = 'https://github.com/pallets/flask/tree/' + + if text.endswith('>'): + words, text = text[:-1].rsplit('<', 1) + words = words.strip() + else: + words = None + + if release.endswith('dev'): + url = '{0}master/{1}'.format(base_url, text) + else: + url = '{0}{1}/{2}'.format(base_url, release, text) + + if words is None: + words = url + + from docutils.nodes import reference + from docutils.parsers.rst.roles import set_classes + options = options or {} + set_classes(options) + node = reference(rawtext, words, refuri=url, **options) + return [node], [] + + +def setup(app): + app.connect('autodoc-skip-member', skip_internal) + app.connect('autodoc-process-docstring', cut_module_meta) + app.add_role('gh', github_link) diff -Nru flask-0.12.2/docs/contents.rst.inc flask-1.0.2/docs/contents.rst.inc --- flask-0.12.2/docs/contents.rst.inc 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/docs/contents.rst.inc 2018-05-02 00:50:32.000000000 +0000 @@ -16,6 +16,7 @@ templating testing errorhandling + logging config signals views @@ -55,7 +56,7 @@ unicode extensiondev styleguide - python3 upgrading changelog license + contributing diff -Nru flask-0.12.2/docs/contributing.rst flask-1.0.2/docs/contributing.rst --- flask-0.12.2/docs/contributing.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/contributing.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1 @@ +.. include:: ../CONTRIBUTING.rst diff -Nru flask-0.12.2/docs/deploying/fastcgi.rst flask-1.0.2/docs/deploying/fastcgi.rst --- flask-0.12.2/docs/deploying/fastcgi.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/deploying/fastcgi.rst 2018-05-02 00:50:32.000000000 +0000 @@ -111,7 +111,7 @@ #!/usr/bin/python #: optional path to your local python site-packages folder import sys - sys.path.insert(0, '/lib/python2.6/site-packages') + sys.path.insert(0, '/lib/python/site-packages') from flup.server.fcgi import WSGIServer from yourapplication import app @@ -144,7 +144,7 @@ ) alias.url = ( - "/static/" => "/path/to/your/static" + "/static/" => "/path/to/your/static/" ) url.rewrite-once = ( @@ -159,7 +159,7 @@ Make sure to apply it only if you are mounting the application the URL root. Also, see the Lighty docs for more information on `FastCGI and Python -`_ (note that +`_ (note that explicitly passing a socket to run() is no longer necessary). Configuring nginx @@ -234,7 +234,7 @@ web server. - Different python interpreters being used. -.. _nginx: http://nginx.org/ -.. _lighttpd: http://www.lighttpd.net/ +.. _nginx: https://nginx.org/ +.. _lighttpd: https://www.lighttpd.net/ .. _cherokee: http://cherokee-project.com/ -.. _flup: https://pypi.python.org/pypi/flup +.. _flup: https://pypi.org/project/flup/ diff -Nru flask-0.12.2/docs/deploying/index.rst flask-1.0.2/docs/deploying/index.rst --- flask-0.12.2/docs/deploying/index.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/deploying/index.rst 2018-05-02 00:50:32.000000000 +0000 @@ -4,9 +4,8 @@ ================== While lightweight and easy to use, **Flask's built-in server is not suitable -for production** as it doesn't scale well and by default serves only one -request at a time. Some of the options available for properly running Flask in -production are documented here. +for production** as it doesn't scale well. Some of the options available for +properly running Flask in production are documented here. If you want to deploy your Flask application to a WSGI server not listed here, look up the server documentation about how to use a WSGI app with it. Just @@ -20,8 +19,8 @@ - `Deploying Flask on Heroku `_ - `Deploying Flask on OpenShift `_ - `Deploying Flask on Webfaction `_ -- `Deploying Flask on Google App Engine `_ -- `Deploying Flask on AWS Elastic Beanstalk `_ +- `Deploying Flask on Google App Engine `_ +- `Deploying Flask on AWS Elastic Beanstalk `_ - `Sharing your Localhost Server with Localtunnel `_ - `Deploying on Azure (IIS) `_ - `Deploying on PythonAnywhere `_ @@ -32,8 +31,8 @@ .. toctree:: :maxdepth: 2 - mod_wsgi wsgi-standalone uwsgi + mod_wsgi fastcgi cgi diff -Nru flask-0.12.2/docs/deploying/mod_wsgi.rst flask-1.0.2/docs/deploying/mod_wsgi.rst --- flask-0.12.2/docs/deploying/mod_wsgi.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/deploying/mod_wsgi.rst 2018-05-02 00:50:32.000000000 +0000 @@ -13,7 +13,7 @@ not called because this will always start a local WSGI server which we do not want if we deploy that application to mod_wsgi. -.. _Apache: http://httpd.apache.org/ +.. _Apache: https://httpd.apache.org/ Installing `mod_wsgi` --------------------- @@ -114,7 +114,7 @@ Note: There have been some changes in access control configuration for `Apache 2.4`_. -.. _Apache 2.4: http://httpd.apache.org/docs/trunk/upgrading.html +.. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html Most notably, the syntax for directory permissions has changed from httpd 2.2 @@ -133,9 +133,9 @@ For more information consult the `mod_wsgi documentation`_. .. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi -.. _installation instructions: http://modwsgi.readthedocs.io/en/develop/installation.html -.. _virtual python: https://pypi.python.org/pypi/virtualenv -.. _mod_wsgi documentation: http://modwsgi.readthedocs.io/en/develop/index.html +.. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html +.. _virtual python: https://pypi.org/project/virtualenv/ +.. _mod_wsgi documentation: https://modwsgi.readthedocs.io/en/develop/index.html Troubleshooting --------------- diff -Nru flask-0.12.2/docs/deploying/uwsgi.rst flask-1.0.2/docs/deploying/uwsgi.rst --- flask-0.12.2/docs/deploying/uwsgi.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/deploying/uwsgi.rst 2018-05-02 00:50:32.000000000 +0000 @@ -66,7 +66,7 @@ uwsgi_pass unix:/tmp/yourapplication.sock; } -.. _nginx: http://nginx.org/ -.. _lighttpd: http://www.lighttpd.net/ +.. _nginx: https://nginx.org/ +.. _lighttpd: https://www.lighttpd.net/ .. _cherokee: http://cherokee-project.com/ .. _uwsgi: http://projects.unbit.it/uwsgi/ diff -Nru flask-0.12.2/docs/deploying/wsgi-standalone.rst flask-1.0.2/docs/deploying/wsgi-standalone.rst --- flask-0.12.2/docs/deploying/wsgi-standalone.rst 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/docs/deploying/wsgi-standalone.rst 2018-05-02 00:50:32.000000000 +0000 @@ -27,6 +27,22 @@ .. _eventlet: http://eventlet.net/ .. _greenlet: https://greenlet.readthedocs.io/en/latest/ +uWSGI +-------- + +`uWSGI`_ is a fast application server written in C. It is very configurable +which makes it more complicated to setup than gunicorn. + +Running `uWSGI HTTP Router`_:: + + uwsgi --http 127.0.0.1:5000 --module myproject:app + +For a more optimized setup, see `configuring uWSGI and NGINX`_. + +.. _uWSGI: http://uwsgi-docs.readthedocs.io/en/latest/ +.. _uWSGI HTTP Router: http://uwsgi-docs.readthedocs.io/en/latest/HTTP.html#the-uwsgi-http-https-router +.. _configuring uWSGI and NGINX: uwsgi.html#starting-your-app-with-uwsgi + Gevent ------- @@ -62,7 +78,7 @@ example, to run a Twisted Web server in the foreground, on port 8080, with an application from ``myproject``:: - twistd -n web --port 8080 --wsgi myproject.app + twistd -n web --port tcp:8080 --wsgi myproject.app .. _Twisted: https://twistedmatrix.com/ .. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb diff -Nru flask-0.12.2/docs/errorhandling.rst flask-1.0.2/docs/errorhandling.rst --- flask-0.12.2/docs/errorhandling.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/errorhandling.rst 2018-05-02 00:50:32.000000000 +0000 @@ -34,7 +34,7 @@ Sending error mails, even if just for critical ones, can become overwhelming if enough users are hitting the error and log files are typically never looked at. This is why we recommend using `Sentry -`_ for dealing with application errors. It's +`_ for dealing with application errors. It's available as an Open Source project `on GitHub `__ and is also available as a `hosted version `_ which you can try for free. Sentry @@ -42,9 +42,9 @@ variables for debugging, and sends you mails based on new errors or frequency thresholds. -To use Sentry you need to install the `raven` client:: +To use Sentry you need to install the `raven` client with extra `flask` dependencies:: - $ pip install raven + $ pip install raven[flask] And then add this to your Flask app:: @@ -76,257 +76,78 @@ You might want to show custom error pages to the user when an error occurs. This can be done by registering error handlers. -Error handlers are normal :ref:`views` but instead of being registered for -routes, they are registered for exceptions that are raised while trying to -do something else. +An error handler is a normal view function that return a response, but instead +of being registered for a route, it is registered for an exception or HTTP +status code that would is raised while trying to handle a request. Registering ``````````` -Register error handlers using :meth:`~flask.Flask.errorhandler` or -:meth:`~flask.Flask.register_error_handler`:: +Register handlers by decorating a function with +:meth:`~flask.Flask.errorhandler`. Or use +:meth:`~flask.Flask.register_error_handler` to register the function later. +Remember to set the error code when returning the response. :: @app.errorhandler(werkzeug.exceptions.BadRequest) def handle_bad_request(e): - return 'bad request!' - - app.register_error_handler(400, lambda e: 'bad request!') + return 'bad request!', 400 -Those two ways are equivalent, but the first one is more clear and leaves -you with a function to call on your whim (and in tests). Note that -:exc:`werkzeug.exceptions.HTTPException` subclasses like -:exc:`~werkzeug.exceptions.BadRequest` from the example and their HTTP codes -are interchangeable when handed to the registration methods or decorator -(``BadRequest.code == 400``). - -You are however not limited to :exc:`~werkzeug.exceptions.HTTPException` -or HTTP status codes but can register a handler for every exception class you -like. + # or, without the decorator + app.register_error_handler(400, handle_bad_request) -.. versionchanged:: 0.11 +:exc:`werkzeug.exceptions.HTTPException` subclasses like +:exc:`~werkzeug.exceptions.BadRequest` and their HTTP codes are interchangeable +when registering handlers. (``BadRequest.code == 400``) - Errorhandlers are now prioritized by specificity of the exception classes - they are registered for instead of the order they are registered in. +Non-standard HTTP codes cannot be registered by code because they are not known +by Werkzeug. Instead, define a subclass of +:class:`~werkzeug.exceptions.HTTPException` with the appropriate code and +register and raise that exception class. :: + + class InsufficientStorage(werkzeug.exceptions.HTTPException): + code = 507 + description = 'Not enough storage space.' + + app.register_error_handler(InsuffcientStorage, handle_507) + + raise InsufficientStorage() + +Handlers can be registered for any exception class, not just +:exc:`~werkzeug.exceptions.HTTPException` subclasses or HTTP status +codes. Handlers can be registered for a specific class, or for all subclasses +of a parent class. Handling ```````` -Once an exception instance is raised, its class hierarchy is traversed, -and searched for in the exception classes for which handlers are registered. -The most specific handler is selected. +When an exception is caught by Flask while handling a request, it is first +looked up by code. If no handler is registered for the code, it is looked up +by its class hierarchy; the most specific handler is chosen. If no handler is +registered, :class:`~werkzeug.exceptions.HTTPException` subclasses show a +generic message about their code, while other exceptions are converted to a +generic 500 Internal Server Error. -E.g. if an instance of :exc:`ConnectionRefusedError` is raised, and a handler +For example, if an instance of :exc:`ConnectionRefusedError` is raised, and a handler is registered for :exc:`ConnectionError` and :exc:`ConnectionRefusedError`, -the more specific :exc:`ConnectionRefusedError` handler is called on the -exception instance, and its response is shown to the user. +the more specific :exc:`ConnectionRefusedError` handler is called with the +exception instance to generate the response. + +Handlers registered on the blueprint take precedence over those registered +globally on the application, assuming a blueprint is handling the request that +raises the exception. However, the blueprint cannot handle 404 routing errors +because the 404 occurs at the routing level before the blueprint can be +determined. + +.. versionchanged:: 0.11 + + Handlers are prioritized by specificity of the exception classes they are + registered for instead of the order they are registered in. -Error Mails ------------ +Logging +------- -If the application runs in production mode (which it will do on your -server) you might not see any log messages. The reason for that is that -Flask by default will just report to the WSGI error stream or stderr -(depending on what's available). Where this ends up is sometimes hard to -find. Often it's in your webserver's log files. - -I can pretty much promise you however that if you only use a logfile for -the application errors you will never look at it except for debugging an -issue when a user reported it for you. What you probably want instead is -a mail the second the exception happened. Then you get an alert and you -can do something about it. - -Flask uses the Python builtin logging system, and it can actually send -you mails for errors which is probably what you want. Here is how you can -configure the Flask logger to send you mails for exceptions:: - - ADMINS = ['yourname@example.com'] - if not app.debug: - import logging - from logging.handlers import SMTPHandler - mail_handler = SMTPHandler('127.0.0.1', - 'server-error@example.com', - ADMINS, 'YourApplication Failed') - mail_handler.setLevel(logging.ERROR) - app.logger.addHandler(mail_handler) - -So what just happened? We created a new -:class:`~logging.handlers.SMTPHandler` that will send mails with the mail -server listening on ``127.0.0.1`` to all the `ADMINS` from the address -*server-error@example.com* with the subject "YourApplication Failed". If -your mail server requires credentials, these can also be provided. For -that check out the documentation for the -:class:`~logging.handlers.SMTPHandler`. - -We also tell the handler to only send errors and more critical messages. -Because we certainly don't want to get a mail for warnings or other -useless logs that might happen during request handling. - -Before you run that in production, please also look at :ref:`logformat` to -put more information into that error mail. That will save you from a lot -of frustration. - - -Logging to a File ------------------ - -Even if you get mails, you probably also want to log warnings. It's a -good idea to keep as much information around that might be required to -debug a problem. By default as of Flask 0.11, errors are logged to your -webserver's log automatically. Warnings however are not. Please note -that Flask itself will not issue any warnings in the core system, so it's -your responsibility to warn in the code if something seems odd. - -There are a couple of handlers provided by the logging system out of the -box but not all of them are useful for basic error logging. The most -interesting are probably the following: - -- :class:`~logging.FileHandler` - logs messages to a file on the - filesystem. -- :class:`~logging.handlers.RotatingFileHandler` - logs messages to a file - on the filesystem and will rotate after a certain number of messages. -- :class:`~logging.handlers.NTEventLogHandler` - will log to the system - event log of a Windows system. If you are deploying on a Windows box, - this is what you want to use. -- :class:`~logging.handlers.SysLogHandler` - sends logs to a UNIX - syslog. - -Once you picked your log handler, do like you did with the SMTP handler -above, just make sure to use a lower setting (I would recommend -`WARNING`):: - - if not app.debug: - import logging - from themodule import TheHandlerYouWant - file_handler = TheHandlerYouWant(...) - file_handler.setLevel(logging.WARNING) - app.logger.addHandler(file_handler) - -.. _logformat: - -Controlling the Log Format --------------------------- - -By default a handler will only write the message string into a file or -send you that message as mail. A log record stores more information, -and it makes a lot of sense to configure your logger to also contain that -information so that you have a better idea of why that error happened, and -more importantly, where it did. - -A formatter can be instantiated with a format string. Note that -tracebacks are appended to the log entry automatically. You don't have to -do that in the log formatter format string. - -Here are some example setups: - -Email -````` - -:: - - from logging import Formatter - mail_handler.setFormatter(Formatter(''' - Message type: %(levelname)s - Location: %(pathname)s:%(lineno)d - Module: %(module)s - Function: %(funcName)s - Time: %(asctime)s - - Message: - - %(message)s - ''')) - -File logging -```````````` - -:: - - from logging import Formatter - file_handler.setFormatter(Formatter( - '%(asctime)s %(levelname)s: %(message)s ' - '[in %(pathname)s:%(lineno)d]' - )) - - -Complex Log Formatting -`````````````````````` - -Here is a list of useful formatting variables for the format string. Note -that this list is not complete, consult the official documentation of the -:mod:`logging` package for a full list. - -.. tabularcolumns:: |p{3cm}|p{12cm}| - -+------------------+----------------------------------------------------+ -| Format | Description | -+==================+====================================================+ -| ``%(levelname)s``| Text logging level for the message | -| | (``'DEBUG'``, ``'INFO'``, ``'WARNING'``, | -| | ``'ERROR'``, ``'CRITICAL'``). | -+------------------+----------------------------------------------------+ -| ``%(pathname)s`` | Full pathname of the source file where the | -| | logging call was issued (if available). | -+------------------+----------------------------------------------------+ -| ``%(filename)s`` | Filename portion of pathname. | -+------------------+----------------------------------------------------+ -| ``%(module)s`` | Module (name portion of filename). | -+------------------+----------------------------------------------------+ -| ``%(funcName)s`` | Name of function containing the logging call. | -+------------------+----------------------------------------------------+ -| ``%(lineno)d`` | Source line number where the logging call was | -| | issued (if available). | -+------------------+----------------------------------------------------+ -| ``%(asctime)s`` | Human-readable time when the | -| | :class:`~logging.LogRecord` was created. | -| | By default this is of the form | -| | ``"2003-07-08 16:49:45,896"`` (the numbers after | -| | the comma are millisecond portion of the time). | -| | This can be changed by subclassing the formatter | -| | and overriding the | -| | :meth:`~logging.Formatter.formatTime` method. | -+------------------+----------------------------------------------------+ -| ``%(message)s`` | The logged message, computed as ``msg % args`` | -+------------------+----------------------------------------------------+ - -If you want to further customize the formatting, you can subclass the -formatter. The formatter has three interesting methods: - -:meth:`~logging.Formatter.format`: - handles the actual formatting. It is passed a - :class:`~logging.LogRecord` object and has to return the formatted - string. -:meth:`~logging.Formatter.formatTime`: - called for `asctime` formatting. If you want a different time format - you can override this method. -:meth:`~logging.Formatter.formatException` - called for exception formatting. It is passed an :attr:`~sys.exc_info` - tuple and has to return a string. The default is usually fine, you - don't have to override it. - -For more information, head over to the official documentation. - - -Other Libraries ---------------- - -So far we only configured the logger your application created itself. -Other libraries might log themselves as well. For example, SQLAlchemy uses -logging heavily in its core. While there is a method to configure all -loggers at once in the :mod:`logging` package, I would not recommend using -it. There might be a situation in which you want to have multiple -separate applications running side by side in the same Python interpreter -and then it becomes impossible to have different logging setups for those. - -Instead, I would recommend figuring out which loggers you are interested -in, getting the loggers with the :func:`~logging.getLogger` function and -iterating over them to attach handlers:: - - from logging import getLogger - loggers = [app.logger, getLogger('sqlalchemy'), - getLogger('otherlibrary')] - for logger in loggers: - logger.addHandler(mail_handler) - logger.addHandler(file_handler) +See :ref:`logging` for information on how to log exceptions, such as by +emailing them to admins. Debugging Application Errors diff -Nru flask-0.12.2/docs/extensiondev.rst flask-1.0.2/docs/extensiondev.rst --- flask-0.12.2/docs/extensiondev.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/extensiondev.rst 2018-05-02 00:50:32.000000000 +0000 @@ -29,12 +29,6 @@ This is how users can then register dependencies to your extension in their :file:`setup.py` files. -Flask sets up a redirect package called :data:`flask.ext` where users -should import the extensions from. If you for instance have a package -called ``flask_something`` users would import it as -``flask.ext.something``. This is done to transition from the old -namespace packages. See :ref:`ext-import-transition` for more details. - But what do extensions look like themselves? An extension has to ensure that it works with multiple Flask application instances at once. This is a requirement because many people will use patterns like the @@ -48,7 +42,7 @@ virtualenv without having to download the library by hand. Flask extensions must be licensed under a BSD, MIT or more liberal license -to be able to be enlisted in the Flask Extension Registry. Keep in mind +in order to be listed in the Flask Extension Registry. Keep in mind that the Flask Extension Registry is a moderated place and libraries will be reviewed upfront if they behave as required. @@ -154,10 +148,10 @@ we will use the class-based approach because it will provide users with an object that handles opening and closing database connections. -What's important about classes is that they encourage to be shared around -on module level. In that case, the object itself must not under any +When designing your classes, it's important to make them easily reusable +at the module level. This means the object itself must not under any circumstances store any application specific state and must be shareable -between different application. +between different applications. The Extension Code ------------------ @@ -165,19 +159,10 @@ Here's the contents of the `flask_sqlite3.py` for copy/paste:: import sqlite3 - from flask import current_app - - # Find the stack on which we want to store the database connection. - # Starting with Flask 0.9, the _app_ctx_stack is the correct one, - # before that we need to use the _request_ctx_stack. - try: - from flask import _app_ctx_stack as stack - except ImportError: - from flask import _request_ctx_stack as stack + from flask import current_app, _app_ctx_stack class SQLite3(object): - def __init__(self, app=None): self.app = app if app is not None: @@ -185,24 +170,19 @@ def init_app(self, app): app.config.setdefault('SQLITE3_DATABASE', ':memory:') - # Use the newstyle teardown_appcontext if it's available, - # otherwise fall back to the request context - if hasattr(app, 'teardown_appcontext'): - app.teardown_appcontext(self.teardown) - else: - app.teardown_request(self.teardown) + app.teardown_appcontext(self.teardown) def connect(self): return sqlite3.connect(current_app.config['SQLITE3_DATABASE']) def teardown(self, exception): - ctx = stack.top + ctx = _app_ctx_stack.top if hasattr(ctx, 'sqlite3_db'): ctx.sqlite3_db.close() @property def connection(self): - ctx = stack.top + ctx = _app_ctx_stack.top if ctx is not None: if not hasattr(ctx, 'sqlite3_db'): ctx.sqlite3_db = self.connect() @@ -218,9 +198,7 @@ factory pattern for creating applications. The ``init_app`` will set the configuration for the database, defaulting to an in memory database if no configuration is supplied. In addition, the ``init_app`` method attaches - the ``teardown`` handler. It will try to use the newstyle app context - handler and if it does not exist, falls back to the request context - one. + the ``teardown`` handler. 3. Next, we define a ``connect`` method that opens a database connection. 4. Finally, we add a ``connection`` property that on first access opens the database connection and stores it on the context. This is also @@ -230,9 +208,7 @@ Note here that we're attaching our database connection to the top application context via ``_app_ctx_stack.top``. Extensions should use the top context for storing their own information with a sufficiently - complex name. Note that we're falling back to the - ``_request_ctx_stack.top`` if the application is using an older - version of Flask that does not support it. + complex name. So why did we decide on a class-based approach here? Because using our extension looks something like this:: @@ -251,9 +227,8 @@ cur = db.connection.cursor() cur.execute(...) -Likewise if you are outside of a request but you are using Flask 0.9 or -later with the app context support, you can use the database in the same -way:: +Likewise if you are outside of a request you can use the database by +pushing an app context:: with app.app_context(): cur = db.connection.cursor() @@ -297,34 +272,6 @@ this pattern, the *same* connection to the sqlite3 database is accessible to anything that needs it for the duration of the request. -If the :data:`~flask._app_ctx_stack` does not exist because the user uses -an old version of Flask, it is recommended to fall back to -:data:`~flask._request_ctx_stack` which is bound to a request. - -Teardown Behavior ------------------ - -*This is only relevant if you want to support Flask 0.6 and older* - -Due to the change in Flask 0.7 regarding functions that are run at the end -of the request your extension will have to be extra careful there if it -wants to continue to support older versions of Flask. The following -pattern is a good way to support both:: - - def close_connection(response): - ctx = _request_ctx_stack.top - ctx.sqlite3_db.close() - return response - - if hasattr(app, 'teardown_request'): - app.teardown_request(close_connection) - else: - app.after_request(close_connection) - -Strictly speaking the above code is wrong, because teardown functions are -passed the exception and typically don't return anything. However because -the return value is discarded this will just work assuming that the code -in between does not touch the passed parameter. Learn from Others ----------------- @@ -334,10 +281,10 @@ out existing extensions on the `Flask Extension Registry`_. If you feel lost there is still the `mailinglist`_ and the `IRC channel`_ to get some ideas for nice looking APIs. Especially if you do something nobody before -you did, it might be a very good idea to get some more input. This not -only to get an idea about what people might want to have from an -extension, but also to avoid having multiple developers working on pretty -much the same side by side. +you did, it might be a very good idea to get some more input. This not only +generates useful feedback on what people might want from an extension, but +also avoids having multiple developers working in isolation on pretty much the +same problem. Remember: good API design is hard, so introduce your project on the mailinglist, and let other developers give you a helping hand with @@ -370,10 +317,10 @@ 3. APIs of approved extensions will be checked for the following characteristics: - - an approved extension has to support multiple applications - running in the same Python process. - - it must be possible to use the factory pattern for creating - applications. + - an approved extension has to support multiple applications + running in the same Python process. + - it must be possible to use the factory pattern for creating + applications. 4. The license must be BSD/MIT/WTFPL licensed. 5. The naming scheme for official extensions is *Flask-ExtensionName* or @@ -381,38 +328,18 @@ 6. Approved extensions must define all their dependencies in the :file:`setup.py` file unless a dependency cannot be met because it is not available on PyPI. -7. The extension must have documentation that uses one of the two Flask - themes for Sphinx documentation. +7. The documentation must use the ``flask`` theme from the `Official + Pallets Themes`_. 8. The setup.py description (and thus the PyPI description) has to link to the documentation, website (if there is one) and there must be a link to automatically install the development version (``PackageName==dev``). -9. The ``zip_safe`` flag in the setup script must be set to ``False``, - even if the extension would be safe for zipping. -10. An extension currently has to support Python 2.6 as well as - Python 2.7 - - -.. _ext-import-transition: - -Extension Import Transition ---------------------------- - -In early versions of Flask we recommended using namespace packages for Flask -extensions, of the form ``flaskext.foo``. This turned out to be problematic in -practice because it meant that multiple ``flaskext`` packages coexist. -Consequently we have recommended to name extensions ``flask_foo`` over -``flaskext.foo`` for a long time. - -Flask 0.8 introduced a redirect import system as a compatibility aid for app -developers: Importing ``flask.ext.foo`` would try ``flask_foo`` and -``flaskext.foo`` in that order. - -As of Flask 0.11, most Flask extensions have transitioned to the new naming -schema. The ``flask.ext.foo`` compatibility alias is still in Flask 0.11 but is -now deprecated -- you should use ``flask_foo``. +9. The ``zip_safe`` flag in the setup script must be set to ``False``, + even if the extension would be safe for zipping. +10. An extension currently has to support Python 3.4 and newer and 2.7. -.. _OAuth extension: http://pythonhosted.org/Flask-OAuth/ +.. _OAuth extension: https://pythonhosted.org/Flask-OAuth/ .. _mailinglist: http://flask.pocoo.org/mailinglist/ .. _IRC channel: http://flask.pocoo.org/community/irc/ +.. _Official Pallets Themes: https://pypi.org/project/pallets-sphinx-themes/ diff -Nru flask-0.12.2/docs/extensions.rst flask-1.0.2/docs/extensions.rst --- flask-0.12.2/docs/extensions.rst 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/docs/extensions.rst 2018-05-02 00:50:32.000000000 +0000 @@ -1,58 +1,53 @@ .. _extensions: -Flask Extensions -================ +Extensions +========== + +Extensions are extra packages that add functionality to a Flask +application. For example, an extension might add support for sending +email or connecting to a database. Some extensions add entire new +frameworks to help build certain types of applications, like a ReST API. -Flask extensions extend the functionality of Flask in various different -ways. For instance they add support for databases and other common tasks. Finding Extensions ------------------ -Flask extensions are listed on the `Flask Extension Registry`_ and can be -downloaded with :command:`easy_install` or :command:`pip`. If you add a Flask extension -as dependency to your :file:`requirements.txt` or :file:`setup.py` file they are -usually installed with a simple command or when your application installs. +Flask extensions are usually named "Flask-Foo" or "Foo-Flask". Many +extensions are listed in the `Extension Registry`_, which can be updated +by extension developers. You can also search PyPI for packages tagged +with `Framework :: Flask `_. + Using Extensions ---------------- -Extensions typically have documentation that goes along that shows how to -use it. There are no general rules in how extensions are supposed to -behave but they are imported from common locations. If you have an -extension called ``Flask-Foo`` or ``Foo-Flask`` it should be always -importable from ``flask_foo``:: +Consult each extension's documentation for installation, configuration, +and usage instructions. Generally, extensions pull their own +configuration from :attr:`app.config ` and are +passed an application instance during initialization. For example, +an extension caled "Flask-Foo" might be used like this:: + + from flask_foo import Foo + + foo = Foo() + + app = Flask(__name__) + app.config.update( + FOO_BAR='baz', + FOO_SPAM='eggs', + ) + + foo.init_app(app) - import flask_foo Building Extensions ------------------- -While `Flask Extension Registry`_ contains many Flask extensions, you may not find -an extension that fits your need. If this is the case, you can always create your own. -Consider reading :ref:`extension-dev` to develop your own Flask extension. - -Flask Before 0.8 ----------------- - -If you are using Flask 0.7 or earlier the :data:`flask.ext` package will not -exist, instead you have to import from ``flaskext.foo`` or ``flask_foo`` -depending on how the extension is distributed. If you want to develop an -application that supports Flask 0.7 or earlier you should still import -from the :data:`flask.ext` package. We provide you with a compatibility -module that provides this package for older versions of Flask. You can -download it from GitHub: `flaskext_compat.py`_ - -And here is how you can use it:: - - import flaskext_compat - flaskext_compat.activate() - - from flask.ext import foo - -Once the ``flaskext_compat`` module is activated the :data:`flask.ext` will -exist and you can start importing from there. +While the `Extension Registry`_ contains many Flask extensions, you may +not find an extension that fits your need. If this is the case, you can +create your own. Read :ref:`extension-dev` to develop your own Flask +extension. -.. _Flask Extension Registry: http://flask.pocoo.org/extensions/ -.. _flaskext_compat.py: https://raw.githubusercontent.com/pallets/flask/master/scripts/flaskext_compat.py +.. _Extension Registry: http://flask.pocoo.org/extensions/ +.. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask diff -Nru flask-0.12.2/docs/flaskdocext.py flask-1.0.2/docs/flaskdocext.py --- flask-0.12.2/docs/flaskdocext.py 2014-11-01 15:48:02.000000000 +0000 +++ flask-1.0.2/docs/flaskdocext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -import re -import inspect - - -_internal_mark_re = re.compile(r'^\s*:internal:\s*$(?m)') - - -def skip_member(app, what, name, obj, skip, options): - docstring = inspect.getdoc(obj) - if skip: - return True - return _internal_mark_re.search(docstring or '') is not None - - -def setup(app): - app.connect('autodoc-skip-member', skip_member) diff -Nru flask-0.12.2/docs/flaskext.py flask-1.0.2/docs/flaskext.py --- flask-0.12.2/docs/flaskext.py 2013-06-27 13:01:37.000000000 +0000 +++ flask-1.0.2/docs/flaskext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -# flasky extensions. flasky pygments style based on tango style -from pygments.style import Style -from pygments.token import Keyword, Name, Comment, String, Error, \ - Number, Operator, Generic, Whitespace, Punctuation, Other, Literal - - -class FlaskyStyle(Style): - background_color = "#f8f8f8" - default_style = "" - - styles = { - # No corresponding class for the following: - #Text: "", # class: '' - Whitespace: "underline #f8f8f8", # class: 'w' - Error: "#a40000 border:#ef2929", # class: 'err' - Other: "#000000", # class 'x' - - Comment: "italic #8f5902", # class: 'c' - Comment.Preproc: "noitalic", # class: 'cp' - - Keyword: "bold #004461", # class: 'k' - Keyword.Constant: "bold #004461", # class: 'kc' - Keyword.Declaration: "bold #004461", # class: 'kd' - Keyword.Namespace: "bold #004461", # class: 'kn' - Keyword.Pseudo: "bold #004461", # class: 'kp' - Keyword.Reserved: "bold #004461", # class: 'kr' - Keyword.Type: "bold #004461", # class: 'kt' - - Operator: "#582800", # class: 'o' - Operator.Word: "bold #004461", # class: 'ow' - like keywords - - Punctuation: "bold #000000", # class: 'p' - - # because special names such as Name.Class, Name.Function, etc. - # are not recognized as such later in the parsing, we choose them - # to look the same as ordinary variables. - Name: "#000000", # class: 'n' - Name.Attribute: "#c4a000", # class: 'na' - to be revised - Name.Builtin: "#004461", # class: 'nb' - Name.Builtin.Pseudo: "#3465a4", # class: 'bp' - Name.Class: "#000000", # class: 'nc' - to be revised - Name.Constant: "#000000", # class: 'no' - to be revised - Name.Decorator: "#888", # class: 'nd' - to be revised - Name.Entity: "#ce5c00", # class: 'ni' - Name.Exception: "bold #cc0000", # class: 'ne' - Name.Function: "#000000", # class: 'nf' - Name.Property: "#000000", # class: 'py' - Name.Label: "#f57900", # class: 'nl' - Name.Namespace: "#000000", # class: 'nn' - to be revised - Name.Other: "#000000", # class: 'nx' - Name.Tag: "bold #004461", # class: 'nt' - like a keyword - Name.Variable: "#000000", # class: 'nv' - to be revised - Name.Variable.Class: "#000000", # class: 'vc' - to be revised - Name.Variable.Global: "#000000", # class: 'vg' - to be revised - Name.Variable.Instance: "#000000", # class: 'vi' - to be revised - - Number: "#990000", # class: 'm' - - Literal: "#000000", # class: 'l' - Literal.Date: "#000000", # class: 'ld' - - String: "#4e9a06", # class: 's' - String.Backtick: "#4e9a06", # class: 'sb' - String.Char: "#4e9a06", # class: 'sc' - String.Doc: "italic #8f5902", # class: 'sd' - like a comment - String.Double: "#4e9a06", # class: 's2' - String.Escape: "#4e9a06", # class: 'se' - String.Heredoc: "#4e9a06", # class: 'sh' - String.Interpol: "#4e9a06", # class: 'si' - String.Other: "#4e9a06", # class: 'sx' - String.Regex: "#4e9a06", # class: 'sr' - String.Single: "#4e9a06", # class: 's1' - String.Symbol: "#4e9a06", # class: 'ss' - - Generic: "#000000", # class: 'g' - Generic.Deleted: "#a40000", # class: 'gd' - Generic.Emph: "italic #000000", # class: 'ge' - Generic.Error: "#ef2929", # class: 'gr' - Generic.Heading: "bold #000080", # class: 'gh' - Generic.Inserted: "#00A000", # class: 'gi' - Generic.Output: "#888", # class: 'go' - Generic.Prompt: "#745334", # class: 'gp' - Generic.Strong: "bold #000000", # class: 'gs' - Generic.Subheading: "bold #800080", # class: 'gu' - Generic.Traceback: "bold #a40000", # class: 'gt' - } diff -Nru flask-0.12.2/docs/.gitignore flask-1.0.2/docs/.gitignore --- flask-0.12.2/docs/.gitignore 2013-06-27 13:01:37.000000000 +0000 +++ flask-1.0.2/docs/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -_build diff -Nru flask-0.12.2/docs/index.rst flask-1.0.2/docs/index.rst --- flask-0.12.2/docs/index.rst 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/docs/index.rst 2018-05-02 00:50:32.000000000 +0000 @@ -4,28 +4,23 @@ ================ .. image:: _static/logo-full.png - :alt: Flask: web development, one drop at a time - :class: floatingflask + :alt: Flask: web development, one drop at a time + :align: right -Welcome to Flask's documentation. This documentation is divided into -different parts. I recommend that you get started with -:ref:`installation` and then head over to the :ref:`quickstart`. -Besides the quickstart, there is also a more detailed :ref:`tutorial` that -shows how to create a complete (albeit small) application with Flask. If -you'd rather dive into the internals of Flask, check out -the :ref:`api` documentation. Common patterns are described in the -:ref:`patterns` section. - -Flask depends on two external libraries: the `Jinja2`_ template -engine and the `Werkzeug`_ WSGI toolkit. These libraries are not documented -here. If you want to dive into their documentation, check out the -following links: +Welcome to Flask's documentation. Get started with :ref:`installation` +and then get an overview with the :ref:`quickstart`. There is also a +more detailed :ref:`tutorial` that shows how to create a small but +complete application with Flask. Common patterns are described in the +:ref:`patterns` section. The rest of the docs describe each component of +Flask in detail, with a full reference in the :ref:`api` section. -- `Jinja2 Documentation `_ -- `Werkzeug Documentation `_ +Flask depends on the `Jinja`_ template engine and the `Werkzeug`_ WSGI +toolkit. The documentation for these libraries can be found at: +- `Jinja documentation `_ +- `Werkzeug documentation `_ -.. _Jinja2: http://jinja.pocoo.org/ -.. _Werkzeug: http://werkzeug.pocoo.org/ +.. _Jinja: https://www.palletsprojects.com/p/jinja/ +.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/ .. include:: contents.rst.inc diff -Nru flask-0.12.2/docs/installation.rst flask-1.0.2/docs/installation.rst --- flask-0.12.2/docs/installation.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/installation.rst 2018-05-02 00:50:32.000000000 +0000 @@ -3,163 +3,181 @@ Installation ============ -Flask depends on some external libraries, like `Werkzeug -`_ and `Jinja2 `_. -Werkzeug is a toolkit for WSGI, the standard Python interface between web -applications and a variety of servers for both development and deployment. -Jinja2 renders templates. +Python Version +-------------- -So how do you get all that on your computer quickly? There are many ways you -could do that, but the most kick-ass method is virtualenv, so let's have a look -at that first. +We recommend using the latest version of Python 3. Flask supports Python 3.4 +and newer, Python 2.7, and PyPy. -You will need Python 2.6 or newer to get started, so be sure to have an -up-to-date Python 2.x installation. For using Flask with Python 3 have a -look at :ref:`python3-support`. +Dependencies +------------ -.. _virtualenv: +These distributions will be installed automatically when installing Flask. -virtualenv ----------- +* `Werkzeug`_ implements WSGI, the standard Python interface between + applications and servers. +* `Jinja`_ is a template language that renders the pages your application + serves. +* `MarkupSafe`_ comes with Jinja. It escapes untrusted input when rendering + templates to avoid injection attacks. +* `ItsDangerous`_ securely signs data to ensure its integrity. This is used + to protect Flask's session cookie. +* `Click`_ is a framework for writing command line applications. It provides + the ``flask`` command and allows adding custom management commands. -Virtualenv is probably what you want to use during development, and if you have -shell access to your production machines, you'll probably want to use it there, -too. +.. _Werkzeug: http://werkzeug.pocoo.org/ +.. _Jinja: http://jinja.pocoo.org/ +.. _MarkupSafe: https://pypi.org/project/MarkupSafe/ +.. _ItsDangerous: https://pythonhosted.org/itsdangerous/ +.. _Click: http://click.pocoo.org/ -What problem does virtualenv solve? If you like Python as much as I do, -chances are you want to use it for other projects besides Flask-based web -applications. But the more projects you have, the more likely it is that you -will be working with different versions of Python itself, or at least different -versions of Python libraries. Let's face it: quite often libraries break -backwards compatibility, and it's unlikely that any serious application will -have zero dependencies. So what do you do if two or more of your projects have -conflicting dependencies? +Optional dependencies +~~~~~~~~~~~~~~~~~~~~~ -Virtualenv to the rescue! Virtualenv enables multiple side-by-side -installations of Python, one for each project. It doesn't actually install -separate copies of Python, but it does provide a clever way to keep different -project environments isolated. Let's see how virtualenv works. +These distributions will not be installed automatically. Flask will detect and +use them if you install them. -If you are on Mac OS X or Linux, chances are that the following -command will work for you:: +* `Blinker`_ provides support for :ref:`signals`. +* `SimpleJSON`_ is a fast JSON implementation that is compatible with + Python's ``json`` module. It is preferred for JSON operations if it is + installed. +* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask`` + commands. +* `Watchdog`_ provides a faster, more efficient reloader for the development + server. - $ sudo pip install virtualenv +.. _Blinker: https://pythonhosted.org/blinker/ +.. _SimpleJSON: https://simplejson.readthedocs.io/ +.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme +.. _watchdog: https://pythonhosted.org/watchdog/ -It will probably install virtualenv on your system. Maybe it's even -in your package manager. If you use Ubuntu, try:: +Virtual environments +-------------------- - $ sudo apt-get install python-virtualenv +Use a virtual environment to manage the dependencies for your project, both in +development and in production. -If you are on Windows and don't have the ``easy_install`` command, you must -install it first. Check the :ref:`windows-easy-install` section for more -information about how to do that. Once you have it installed, run the same -commands as above, but without the ``sudo`` prefix. +What problem does a virtual environment solve? The more Python projects you +have, the more likely it is that you need to work with different versions of +Python libraries, or even Python itself. Newer versions of libraries for one +project can break compatibility in another project. -Once you have virtualenv installed, just fire up a shell and create -your own environment. I usually create a project folder and a :file:`venv` -folder within:: +Virtual environments are independent groups of Python libraries, one for each +project. Packages installed for one project will not affect other projects or +the operating system's packages. - $ mkdir myproject - $ cd myproject - $ virtualenv venv - New python executable in venv/bin/python - Installing setuptools, pip............done. +Python 3 comes bundled with the :mod:`venv` module to create virtual +environments. If you're using a modern version of Python, you can continue on +to the next section. -Now, whenever you want to work on a project, you only have to activate the -corresponding environment. On OS X and Linux, do the following:: +If you're using Python 2, see :ref:`install-install-virtualenv` first. - $ . venv/bin/activate +.. _install-create-env: -If you are a Windows user, the following command is for you:: +Create an environment +~~~~~~~~~~~~~~~~~~~~~ - $ venv\Scripts\activate +Create a project folder and a :file:`venv` folder within: -Either way, you should now be using your virtualenv (notice how the prompt of -your shell has changed to show the active environment). +.. code-block:: sh -And if you want to go back to the real world, use the following command:: + mkdir myproject + cd myproject + python3 -m venv venv - $ deactivate +On Windows: -After doing this, the prompt of your shell should be as familiar as before. +.. code-block:: bat -Now, let's move on. Enter the following command to get Flask activated in your -virtualenv:: + py -3 -m venv venv - $ pip install Flask +If you needed to install virtualenv because you are on an older version of +Python, use the following command instead: -A few seconds later and you are good to go. +.. code-block:: sh + virtualenv venv -System-Wide Installation ------------------------- +On Windows: -This is possible as well, though I do not recommend it. Just run -``pip`` with root privileges:: +.. code-block:: bat - $ sudo pip install Flask + \Python27\Scripts\virtualenv.exe venv -(On Windows systems, run it in a command-prompt window with administrator -privileges, and leave out ``sudo``.) +.. _install-activate-env: +Activate the environment +~~~~~~~~~~~~~~~~~~~~~~~~ -Living on the Edge ------------------- +Before you work on your project, activate the corresponding environment: + +.. code-block:: sh + + . venv/bin/activate + +On Windows: + +.. code-block:: bat + + venv\Scripts\activate + +Your shell prompt will change to show the name of the activated environment. + +Install Flask +------------- -If you want to work with the latest version of Flask, there are two ways: you -can either let ``pip`` pull in the development version, or you can tell -it to operate on a git checkout. Either way, virtualenv is recommended. +Within the activated environment, use the following command to install Flask: -Get the git checkout in a new virtualenv and run in development mode:: +.. code-block:: sh - $ git clone http://github.com/pallets/flask.git - Initialized empty Git repository in ~/dev/flask/.git/ - $ cd flask - $ virtualenv venv - New python executable in venv/bin/python - Installing setuptools, pip............done. - $ . venv/bin/activate - $ python setup.py develop - ... - Finished processing dependencies for Flask + pip install Flask + +Living on the edge +~~~~~~~~~~~~~~~~~~ + +If you want to work with the latest Flask code before it's released, install or +update the code from the master branch: + +.. code-block:: sh + + pip install -U https://github.com/pallets/flask/archive/master.tar.gz + +.. _install-install-virtualenv: + +Install virtualenv +------------------ -This will pull in the dependencies and activate the git head as the current -version inside the virtualenv. Then all you have to do is run ``git pull -origin`` to update to the latest version. +If you are using Python 2, the venv module is not available. Instead, +install `virtualenv`_. -.. _windows-easy-install: +On Linux, virtualenv is provided by your package manager: -`pip` and `setuptools` on Windows ---------------------------------- +.. code-block:: sh -Sometimes getting the standard "Python packaging tools" like ``pip``, ``setuptools`` -and ``virtualenv`` can be a little trickier, but nothing very hard. The crucial -package you will need is pip - this will let you install -anything else (like virtualenv). Fortunately there is a "bootstrap script" -you can run to install. + # Debian, Ubuntu + sudo apt-get install python-virtualenv -If you don't currently have ``pip``, then `get-pip.py` will install it for you. + # CentOS, Fedora + sudo yum install python-virtualenv -`get-pip.py`_ + # Arch + sudo pacman -S python-virtualenv -It should be double-clickable once you download it. If you already have ``pip``, -you can upgrade them by running:: +If you are on Mac OS X or Windows, download `get-pip.py`_, then: - > pip install --upgrade pip setuptools +.. code-block:: sh -Most often, once you pull up a command prompt you want to be able to type ``pip`` -and ``python`` which will run those things, but this might not automatically happen -on Windows, because it doesn't know where those executables are (give either a try!). + sudo python2 Downloads/get-pip.py + sudo python2 -m pip install virtualenv -To fix this, you should be able to navigate to your Python install directory -(e.g :file:`C:\Python27`), then go to :file:`Tools`, then :file:`Scripts`, then find the -:file:`win_add2path.py` file and run that. Open a **new** Command Prompt and -check that you can now just type ``python`` to bring up the interpreter. +On Windows, as an administrator: -Finally, to install `virtualenv`_, you can simply run:: +.. code-block:: bat - > pip install virtualenv + \Python27\python.exe Downloads\get-pip.py + \Python27\python.exe -m pip install virtualenv -Then you can be off on your way following the installation instructions above. +Now you can continue to :ref:`install-create-env`. +.. _virtualenv: https://virtualenv.pypa.io/ .. _get-pip.py: https://bootstrap.pypa.io/get-pip.py diff -Nru flask-0.12.2/docs/logging.rst flask-1.0.2/docs/logging.rst --- flask-0.12.2/docs/logging.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/logging.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,175 @@ +.. _logging: + +Logging +======= + +Flask uses standard Python :mod:`logging`. All Flask-related messages are +logged under the ``'flask'`` logger namespace. +:meth:`Flask.logger ` returns the logger named +``'flask.app'``, and can be used to log messages for your application. :: + + @app.route('/login', methods=['POST']) + def login(): + user = get_user(request.form['username']) + + if user.check_password(request.form['password']): + login_user(user) + app.logger.info('%s logged in successfully', user.username) + return redirect(url_for('index')) + else: + app.logger.info('%s failed to log in', user.username) + abort(401) + + +Basic Configuration +------------------- + +When you want to configure logging for your project, you should do it as soon +as possible when the program starts. If :meth:`app.logger ` +is accessed before logging is configured, it will add a default handler. If +possible, configure logging before creating the application object. + +This example uses :func:`~logging.config.dictConfig` to create a logging +configuration similar to Flask's default, except for all logs:: + + from logging.config import dictConfig + + dictConfig({ + 'version': 1, + 'formatters': {'default': { + 'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s', + }}, + 'handlers': {'wsgi': { + 'class': 'logging.StreamHandler', + 'stream': 'ext://flask.logging.wsgi_errors_stream', + 'formatter': 'default' + }}, + 'root': { + 'level': 'INFO', + 'handlers': ['wsgi'] + } + }) + + app = Flask(__name__) + + +Default Configuration +````````````````````` + +If you do not configure logging yourself, Flask will add a +:class:`~logging.StreamHandler` to :meth:`app.logger ` +automatically. During requests, it will write to the stream specified by the +WSGI server in ``environ['wsgi.errors']`` (which is usually +:data:`sys.stderr`). Outside a request, it will log to :data:`sys.stderr`. + + +Removing the Default Handler +```````````````````````````` + +If you configured logging after accessing +:meth:`app.logger `, and need to remove the default +handler, you can import and remove it:: + + from flask.logging import default_handler + + app.logger.removeHandler(default_handler) + + +Email Errors to Admins +---------------------- + +When running the application on a remote server for production, you probably +won't be looking at the log messages very often. The WSGI server will probably +send log messages to a file, and you'll only check that file if a user tells +you something went wrong. + +To be proactive about discovering and fixing bugs, you can configure a +:class:`logging.handlers.SMTPHandler` to send an email when errors and higher +are logged. :: + + import logging + from logging.handlers import SMTPHandler + + mail_handler = SMTPHandler( + mailhost='127.0.0.1', + fromaddr='server-error@example.com', + toaddrs=['admin@example.com'], + subject='Application Error' + ) + mail_handler.setLevel(logging.ERROR) + mail_handler.setFormatter(logging.Formatter( + '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' + )) + + if not app.debug: + app.logger.addHandler(mail_handler) + +This requires that you have an SMTP server set up on the same server. See the +Python docs for more information about configuring the handler. + + +Injecting Request Information +----------------------------- + +Seeing more information about the request, such as the IP address, may help +debugging some errors. You can subclass :class:`logging.Formatter` to inject +your own fields that can be used in messages. You can change the formatter for +Flask's default handler, the mail handler defined above, or any other +handler. :: + + from flask import request + from flask.logging import default_handler + + class RequestFormatter(logging.Formatter): + def format(self, record): + record.url = request.url + record.remote_addr = request.remote_addr + return super().format(record) + + formatter = RequestFormatter( + '[%(asctime)s] %(remote_addr)s requested %(url)s\n' + '%(levelname)s in %(module)s: %(message)s' + ) + default_handler.setFormatter(formatter) + mail_handler.setFormatter(formatter) + + +Other Libraries +--------------- + +Other libraries may use logging extensively, and you want to see relevant +messages from those logs too. The simplest way to do this is to add handlers +to the root logger instead of only the app logger. :: + + from flask.logging import default_handler + + root = logging.getLogger() + root.addHandler(default_handler) + root.addHandler(mail_handler) + +Depending on your project, it may be more useful to configure each logger you +care about separately, instead of configuring only the root logger. :: + + for logger in ( + app.logger, + logging.getLogger('sqlalchemy'), + logging.getLogger('other_package'), + ): + logger.addHandler(default_handler) + logger.addHandler(mail_handler) + + +Werkzeug +```````` + +Werkzeug logs basic request/response information to the ``'werkzeug'`` logger. +If the root logger has no handlers configured, Werkzeug adds a +:class:`~logging.StreamHandler` to its logger. + + +Flask Extensions +```````````````` + +Depending on the situation, an extension may choose to log to +:meth:`app.logger ` or its own named logger. Consult each +extension's documentation for details. diff -Nru flask-0.12.2/docs/make.bat flask-1.0.2/docs/make.bat --- flask-0.12.2/docs/make.bat 2013-06-27 13:01:37.000000000 +0000 +++ flask-1.0.2/docs/make.bat 2018-05-02 00:50:32.000000000 +0000 @@ -1,139 +1,36 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Flask.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Flask.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% _build/devhelp - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build +set SPHINXPROJ=Flask + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff -Nru flask-0.12.2/docs/Makefile flask-1.0.2/docs/Makefile --- flask-0.12.2/docs/Makefile 2013-06-27 13:01:37.000000000 +0000 +++ flask-1.0.2/docs/Makefile 2018-04-19 13:10:25.000000000 +0000 @@ -1,118 +1,20 @@ -# Makefile for Sphinx documentation +# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build -PAPER = +SPHINXPROJ = Flask +SOURCEDIR = . BUILDDIR = _build -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp epub latex changes linkcheck doctest - +# Put it first so that "make" without argument is like "make help". help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask" - @echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/Flask" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -latexpdf: latex - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex - @echo "Running LaTeX files through pdflatex..." - make -C _build/latex all-pdf - @echo "pdflatex finished; the PDF files are in _build/latex." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." +.PHONY: help Makefile -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff -Nru flask-0.12.2/docs/patterns/appfactories.rst flask-1.0.2/docs/patterns/appfactories.rst --- flask-0.12.2/docs/patterns/appfactories.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/appfactories.rst 2018-05-02 00:50:32.000000000 +0000 @@ -60,7 +60,7 @@ It's preferable to create your extensions and app factories so that the extension object does not initially get bound to the application. -Using `Flask-SQLAlchemy `_, +Using `Flask-SQLAlchemy `_, as an example, you should not do something along those lines:: def create_app(config_filename): @@ -89,28 +89,29 @@ Using Applications ------------------ -So to use such an application you then have to create the application -first in a separate file otherwise the :command:`flask` command won't be able -to find it. Here an example :file:`exampleapp.py` file that creates such -an application:: +To run such an application, you can use the :command:`flask` command:: - from yourapplication import create_app - app = create_app('/path/to/config.cfg') - -It can then be used with the :command:`flask` command:: + export FLASK_APP=myapp + flask run + +Flask will automatically detect the factory (``create_app`` or ``make_app``) +in ``myapp``. You can also pass arguments to the factory like this:: - export FLASK_APP=exampleapp + export FLASK_APP="myapp:create_app('dev')" flask run + +Then the ``create_app`` factory in ``myapp`` is called with the string +``'dev'`` as the argument. See :doc:`/cli` for more detail. Factory Improvements -------------------- -The factory function from above is not very clever so far, you can improve -it. The following changes are straightforward and possible: +The factory function above is not very clever, but you can improve it. +The following changes are straightforward to implement: -1. make it possible to pass in configuration values for unittests so that - you don't have to create config files on the filesystem -2. call a function from a blueprint when the application is setting up so +1. Make it possible to pass in configuration values for unit tests so that + you don't have to create config files on the filesystem. +2. Call a function from a blueprint when the application is setting up so that you have a place to modify attributes of the application (like - hooking in before / after request handlers etc.) -3. Add in WSGI middlewares when the application is creating if necessary. + hooking in before/after request handlers etc.) +3. Add in WSGI middlewares when the application is being created if necessary. diff -Nru flask-0.12.2/docs/patterns/caching.rst flask-1.0.2/docs/patterns/caching.rst --- flask-0.12.2/docs/patterns/caching.rst 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/docs/patterns/caching.rst 2018-05-02 00:50:32.000000000 +0000 @@ -27,7 +27,7 @@ cache = SimpleCache() If you want to use memcached, make sure to have one of the memcache modules -supported (you get them from `PyPI `_) and a +supported (you get them from `PyPI `_) and a memcached server running somewhere. This is how you connect to such an memcached server then:: diff -Nru flask-0.12.2/docs/patterns/celery.rst flask-1.0.2/docs/patterns/celery.rst --- flask-0.12.2/docs/patterns/celery.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/celery.rst 2018-05-02 00:50:32.000000000 +0000 @@ -1,24 +1,27 @@ -Celery Based Background Tasks -============================= +Celery Background Tasks +======================= -Celery is a task queue for Python with batteries included. It used to -have a Flask integration but it became unnecessary after some -restructuring of the internals of Celery with Version 3. This guide fills -in the blanks in how to properly use Celery with Flask but assumes that -you generally already read the `First Steps with Celery -`_ -guide in the official Celery documentation. +If your application has a long running task, such as processing some uploaded +data or sending email, you don't want to wait for it to finish during a +request. Instead, use a task queue to send the necessary data to another +process that will run the task in the background while the request returns +immediately. + +Celery is a powerful task queue that can be used for simple background tasks +as well as complex multi-stage programs and schedules. This guide will show you +how to configure Celery using Flask, but assumes you've already read the +`First Steps with Celery `_ +guide in the Celery documentation. -Installing Celery ------------------ +Install +------- -Celery is on the Python Package Index (PyPI), so it can be installed with -standard Python tools like :command:`pip` or :command:`easy_install`:: +Celery is a separate Python package. Install it from PyPI using pip:: $ pip install celery -Configuring Celery ------------------- +Configure +--------- The first thing you need is a Celery instance, this is called the celery application. It serves the same purpose as the :class:`~flask.Flask` @@ -36,15 +39,18 @@ from celery import Celery def make_celery(app): - celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'], - broker=app.config['CELERY_BROKER_URL']) + celery = Celery( + app.import_name, + backend=app.config['CELERY_RESULT_BACKEND'], + broker=app.config['CELERY_BROKER_URL'] + ) celery.conf.update(app.config) - TaskBase = celery.Task - class ContextTask(TaskBase): - abstract = True + + class ContextTask(celery.Task): def __call__(self, *args, **kwargs): with app.app_context(): - return TaskBase.__call__(self, *args, **kwargs) + return self.run(*args, **kwargs) + celery.Task = ContextTask return celery @@ -53,11 +59,12 @@ the Flask config and then creates a subclass of the task that wraps the task execution in an application context. -Minimal Example +An example task --------------- -With what we have above this is the minimal example of using Celery with -Flask:: +Let's write a task that adds two numbers together and returns the result. We +configure Celery's broker and backend to use Redis, create a ``celery`` +application using the factor from above, and then use it to define the task. :: from flask import Flask @@ -68,26 +75,27 @@ ) celery = make_celery(flask_app) - @celery.task() def add_together(a, b): return a + b -This task can now be called in the background: +This task can now be called in the background:: ->>> result = add_together.delay(23, 42) ->>> result.wait() -65 - -Running the Celery Worker -------------------------- - -Now if you jumped in and already executed the above code you will be -disappointed to learn that your ``.wait()`` will never actually return. -That's because you also need to run celery. You can do that by running -celery as a worker:: + result = add_together.delay(23, 42) + result.wait() # 65 + +Run a worker +------------ + +If you jumped in and already executed the above code you will be +disappointed to learn that ``.wait()`` will never actually return. +That's because you also need to run a Celery worker to receive and execute the +task. :: $ celery -A your_application.celery worker The ``your_application`` string has to point to your application's package -or module that creates the `celery` object. +or module that creates the ``celery`` object. + +Now that the worker is running, ``wait`` will return the result once the task +is finished. diff -Nru flask-0.12.2/docs/patterns/deferredcallbacks.rst flask-1.0.2/docs/patterns/deferredcallbacks.rst --- flask-0.12.2/docs/patterns/deferredcallbacks.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/deferredcallbacks.rst 2018-05-02 00:50:32.000000000 +0000 @@ -3,71 +3,43 @@ Deferred Request Callbacks ========================== -One of the design principles of Flask is that response objects are created -and passed down a chain of potential callbacks that can modify them or -replace them. When the request handling starts, there is no response -object yet. It is created as necessary either by a view function or by -some other component in the system. - -But what happens if you want to modify the response at a point where the -response does not exist yet? A common example for that would be a -before-request function that wants to set a cookie on the response object. - -One way is to avoid the situation. Very often that is possible. For -instance you can try to move that logic into an after-request callback -instead. Sometimes however moving that code there is just not a very -pleasant experience or makes code look very awkward. - -As an alternative possibility you can attach a bunch of callback functions -to the :data:`~flask.g` object and call them at the end of the request. -This way you can defer code execution from anywhere in the application. - - -The Decorator -------------- - -The following decorator is the key. It registers a function on a list on -the :data:`~flask.g` object:: - - from flask import g - - def after_this_request(f): - if not hasattr(g, 'after_request_callbacks'): - g.after_request_callbacks = [] - g.after_request_callbacks.append(f) - return f - - -Calling the Deferred --------------------- - -Now you can use the `after_this_request` decorator to mark a function to -be called at the end of the request. But we still need to call them. For -this the following function needs to be registered as -:meth:`~flask.Flask.after_request` callback:: - - @app.after_request - def call_after_request_callbacks(response): - for callback in getattr(g, 'after_request_callbacks', ()): - callback(response) - return response - - -A Practical Example -------------------- +One of the design principles of Flask is that response objects are created and +passed down a chain of potential callbacks that can modify them or replace +them. When the request handling starts, there is no response object yet. It is +created as necessary either by a view function or by some other component in +the system. + +What happens if you want to modify the response at a point where the response +does not exist yet? A common example for that would be a +:meth:`~flask.Flask.before_request` callback that wants to set a cookie on the +response object. + +One way is to avoid the situation. Very often that is possible. For instance +you can try to move that logic into a :meth:`~flask.Flask.after_request` +callback instead. However, sometimes moving code there makes it more +more complicated or awkward to reason about. + +As an alternative, you can use :func:`~flask.after_this_request` to register +callbacks that will execute after only the current request. This way you can +defer code execution from anywhere in the application, based on the current +request. At any time during a request, we can register a function to be called at the -end of the request. For example you can remember the current language of the -user in a cookie in the before-request function:: +end of the request. For example you can remember the current language of the +user in a cookie in a :meth:`~flask.Flask.before_request` callback:: - from flask import request + from flask import request, after_this_request @app.before_request def detect_user_language(): language = request.cookies.get('user_lang') + if language is None: language = guess_language_from_request() + + # when the response exists, set a cookie with the language @after_this_request def remember_language(response): response.set_cookie('user_lang', language) + g.language = language diff -Nru flask-0.12.2/docs/patterns/distribute.rst flask-1.0.2/docs/patterns/distribute.rst --- flask-0.12.2/docs/patterns/distribute.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/distribute.rst 2018-05-02 00:50:32.000000000 +0000 @@ -88,15 +88,15 @@ Tagging Builds -------------- -It is useful to distinguish between release and development builds. Add a -:file:`setup.cfg` file to configure these options. +It is useful to distinguish between release and development builds. Add a +:file:`setup.cfg` file to configure these options. :: [egg_info] tag_build = .dev tag_date = 1 [aliases] - release = egg_info -RDb '' + release = egg_info -Db '' Running ``python setup.py sdist`` will create a development package with ".dev" and the current date appended: ``flaskr-1.0.dev20160314.tar.gz``. @@ -173,5 +173,5 @@ the code without having to run ``install`` again after each change. -.. _pip: https://pypi.python.org/pypi/pip -.. _Setuptools: https://pythonhosted.org/setuptools +.. _pip: https://pypi.org/project/pip/ +.. _Setuptools: https://pypi.org/project/setuptools/ diff -Nru flask-0.12.2/docs/patterns/errorpages.rst flask-1.0.2/docs/patterns/errorpages.rst --- flask-0.12.2/docs/patterns/errorpages.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/errorpages.rst 2018-05-02 00:50:32.000000000 +0000 @@ -47,37 +47,53 @@ Error Handlers -------------- -An error handler is a function, just like a view function, but it is -called when an error happens and is passed that error. The error is most -likely a :exc:`~werkzeug.exceptions.HTTPException`, but in one case it -can be a different error: a handler for internal server errors will be -passed other exception instances as well if they are uncaught. +An error handler is a function that returns a response when a type of error is +raised, similar to how a view is a function that returns a response when a +request URL is matched. It is passed the instance of the error being handled, +which is most likely a :exc:`~werkzeug.exceptions.HTTPException`. An error +handler for "500 Internal Server Error" will be passed uncaught exceptions in +addition to explicit 500 errors. An error handler is registered with the :meth:`~flask.Flask.errorhandler` -decorator and the error code of the exception. Keep in mind that Flask -will *not* set the error code for you, so make sure to also provide the -HTTP status code when returning a response. +decorator or the :meth:`~flask.Flask.register_error_handler` method. A handler +can be registered for a status code, like 404, or for an exception class. -Please note that if you add an error handler for "500 Internal Server -Error", Flask will not trigger it if it's running in Debug mode. +The status code of the response will not be set to the handler's code. Make +sure to provide the appropriate HTTP status code when returning a response from +a handler. -Here an example implementation for a "404 Page Not Found" exception:: +A handler for "500 Internal Server Error" will not be used when running in +debug mode. Instead, the interactive debugger will be shown. + +Here is an example implementation for a "404 Page Not Found" exception:: from flask import render_template @app.errorhandler(404) def page_not_found(e): + # note that we set the 404 status explicitly return render_template('404.html'), 404 +When using the :ref:`application factory pattern `:: + + from flask import Flask, render_template + + def page_not_found(e): + return render_template('404.html'), 404 + + def create_app(config_filename): + app = Flask(__name__) + app.register_error_handler(404, page_not_found) + return app + An example template might be this: .. sourcecode:: html+jinja - {% extends "layout.html" %} - {% block title %}Page Not Found{% endblock %} - {% block body %} -

Page Not Found

-

What you were looking for is just not there. -

go somewhere nice - {% endblock %} - + {% extends "layout.html" %} + {% block title %}Page Not Found{% endblock %} + {% block body %} +

Page Not Found

+

What you were looking for is just not there. +

go somewhere nice + {% endblock %} diff -Nru flask-0.12.2/docs/patterns/favicon.rst flask-1.0.2/docs/patterns/favicon.rst --- flask-0.12.2/docs/patterns/favicon.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/favicon.rst 2018-05-02 00:50:32.000000000 +0000 @@ -49,5 +49,5 @@ See also -------- -* The `Favicon `_ article on +* The `Favicon `_ article on Wikipedia diff -Nru flask-0.12.2/docs/patterns/fileuploads.rst flask-1.0.2/docs/patterns/fileuploads.rst --- flask-0.12.2/docs/patterns/fileuploads.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/fileuploads.rst 2018-05-02 00:50:32.000000000 +0000 @@ -21,7 +21,7 @@ bootstrapping code for our application:: import os - from flask import Flask, request, redirect, url_for + from flask import Flask, flash, request, redirect, url_for from werkzeug.utils import secure_filename UPLOAD_FOLDER = '/path/to/the/uploads' @@ -58,7 +58,7 @@ return redirect(request.url) file = request.files['file'] # if user does not select file, browser also - # submit a empty part without filename + # submit an empty part without filename if file.filename == '': flash('No selected file') return redirect(request.url) @@ -72,8 +72,8 @@ Upload new File

Upload new File

-

- + +

''' @@ -149,10 +149,16 @@ app = Flask(__name__) app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 -The code above will limited the maximum allowed payload to 16 megabytes. -If a larger file is transmitted, Flask will raise an +The code above will limit the maximum allowed payload to 16 megabytes. +If a larger file is transmitted, Flask will raise a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception. +.. admonition:: Connection Reset Issue + + When using the local development server, you may get a connection + reset error instead of a 413 response. You will get the correct + status response when running the app with a production WSGI server. + This feature was added in Flask 0.6 but can be achieved in older versions as well by subclassing the request object. For more information on that consult the Werkzeug documentation on file handling. @@ -181,4 +187,4 @@ blacklisting of extensions and more. .. _jQuery: https://jquery.com/ -.. _Flask-Uploads: http://pythonhosted.org/Flask-Uploads/ +.. _Flask-Uploads: https://pythonhosted.org/Flask-Uploads/ diff -Nru flask-0.12.2/docs/patterns/flashing.rst flask-1.0.2/docs/patterns/flashing.rst --- flask-0.12.2/docs/patterns/flashing.rst 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/docs/patterns/flashing.rst 2018-05-02 00:50:32.000000000 +0000 @@ -22,7 +22,7 @@ request, url_for app = Flask(__name__) - app.secret_key = 'some_secret' + app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' @app.route('/') def index(): diff -Nru flask-0.12.2/docs/patterns/jquery.rst flask-1.0.2/docs/patterns/jquery.rst --- flask-0.12.2/docs/patterns/jquery.rst 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/docs/patterns/jquery.rst 2018-05-02 00:50:32.000000000 +0000 @@ -162,7 +162,6 @@ argument. Note that we can use the `$SCRIPT_ROOT` variable here that we set earlier. -If you don't get the whole picture, download the `sourcecode -for this example -`_ -from GitHub. +Check out the :gh:`example source ` for a full +application demonstrating the code on this page, as well as the same +thing using ``XMLHttpRequest`` and ``fetch``. diff -Nru flask-0.12.2/docs/patterns/packages.rst flask-1.0.2/docs/patterns/packages.rst --- flask-0.12.2/docs/patterns/packages.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/packages.rst 2018-05-02 00:50:32.000000000 +0000 @@ -17,6 +17,9 @@ login.html ... +The :ref:`tutorial ` is structured this way, see the +:gh:`example code `. + Simple Packages --------------- @@ -55,21 +58,21 @@ ], ) -In order to run the application you need to export an environment variable -that tells Flask where to find the application instance:: +In order to run the application you need to export an environment variable +that tells Flask where to find the application instance:: export FLASK_APP=yourapplication -If you are outside of the project directory make sure to provide the exact -path to your application directory. Similiarly you can turn on "debug -mode" with this environment variable:: +If you are outside of the project directory make sure to provide the exact +path to your application directory. Similarly you can turn on the +development features like this:: - export FLASK_DEBUG=true + export FLASK_ENV=development -In order to install and run the application you need to issue the following +In order to install and run the application you need to issue the following commands:: - pip install -e . + pip install -e . flask run What did we gain from this? Now we can restructure the application a bit diff -Nru flask-0.12.2/docs/patterns/sqlalchemy.rst flask-1.0.2/docs/patterns/sqlalchemy.rst --- flask-0.12.2/docs/patterns/sqlalchemy.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/sqlalchemy.rst 2018-05-02 00:50:32.000000000 +0000 @@ -20,9 +20,9 @@ if you want to get started quickly. You can download `Flask-SQLAlchemy`_ from `PyPI -`_. +`_. -.. _Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/ +.. _Flask-SQLAlchemy: http://flask-sqlalchemy.pocoo.org/ Declarative @@ -108,9 +108,9 @@ >>> User.query.filter(User.name == 'admin').first() -.. _SQLAlchemy: http://www.sqlalchemy.org/ +.. _SQLAlchemy: https://www.sqlalchemy.org/ .. _declarative: - http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/ + https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/ Manual Object Relational Mapping -------------------------------- @@ -215,4 +215,4 @@ (1, u'admin', u'admin@localhost') For more information about SQLAlchemy, head over to the -`website `_. +`website `_. diff -Nru flask-0.12.2/docs/patterns/sqlite3.rst flask-1.0.2/docs/patterns/sqlite3.rst --- flask-0.12.2/docs/patterns/sqlite3.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/sqlite3.rst 2018-05-02 00:50:32.000000000 +0000 @@ -3,8 +3,8 @@ Using SQLite 3 with Flask ========================= -In Flask you can easily implement the opening of database connections on -demand and closing them when the context dies (usually at the end of the +In Flask you can easily implement the opening of database connections on +demand and closing them when the context dies (usually at the end of the request). Here is a simple example of how you can use SQLite 3 with Flask:: @@ -67,11 +67,11 @@ Easy Querying ------------- -Now in each request handling function you can access `g.db` to get the +Now in each request handling function you can access `get_db()` to get the current open database connection. To simplify working with SQLite, a row factory function is useful. It is executed for every result returned from the database to convert the result. For instance, in order to get -dictionaries instead of tuples, this could be inserted into the ``get_db`` +dictionaries instead of tuples, this could be inserted into the ``get_db`` function we created above:: def make_dicts(cursor, row): @@ -102,15 +102,15 @@ Additionally, it is a good idea to provide a query function that combines getting the cursor, executing and fetching the results:: - + def query_db(query, args=(), one=False): cur = get_db().execute(query, args) rv = cur.fetchall() cur.close() return (rv[0] if rv else None) if one else rv -This handy little function, in combination with a row factory, makes -working with the database much more pleasant than it is by just using the +This handy little function, in combination with a row factory, makes +working with the database much more pleasant than it is by just using the raw cursor and connection objects. Here is how you can use it:: @@ -131,7 +131,7 @@ statement and pass in the arguments as a list. Never directly add them to the SQL statement with string formatting because this makes it possible to attack the application using `SQL Injections -`_. +`_. Initial Schemas --------------- diff -Nru flask-0.12.2/docs/patterns/wtforms.rst flask-1.0.2/docs/patterns/wtforms.rst --- flask-0.12.2/docs/patterns/wtforms.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/patterns/wtforms.rst 2018-05-02 00:50:32.000000000 +0000 @@ -17,9 +17,9 @@ The `Flask-WTF`_ extension expands on this pattern and adds a few little helpers that make working with forms and Flask more fun. You can get it from `PyPI - `_. + `_. -.. _Flask-WTF: http://pythonhosted.org/Flask-WTF/ +.. _Flask-WTF: https://flask-wtf.readthedocs.io/en/stable/ The Forms --------- diff -Nru flask-0.12.2/docs/python3.rst flask-1.0.2/docs/python3.rst --- flask-0.12.2/docs/python3.rst 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/docs/python3.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -.. _python3-support: - -Python 3 Support -================ - -Flask, its dependencies, and most Flask extensions support Python 3. -You should start using Python 3 for your next project, -but there are a few things to be aware of. - -You need to use Python 3.3 or higher. 3.2 and older are *not* supported. - -You should use the latest versions of all Flask-related packages. -Flask 0.10 and Werkzeug 0.9 were the first versions to introduce Python 3 support. - -Python 3 changed how unicode and bytes are handled, which complicates how low -level code handles HTTP data. This mainly affects WSGI middleware interacting -with the WSGI ``environ`` data. Werkzeug wraps that information in high-level -helpers, so encoding issues should not affect you. - -The majority of the upgrade work is in the lower-level libraries like -Flask and Werkzeug, not the high-level application code. -For example, all of the examples in the Flask repository work on both Python 2 and 3 -and did not require a single line of code changed. diff -Nru flask-0.12.2/docs/quickstart.rst flask-1.0.2/docs/quickstart.rst --- flask-0.12.2/docs/quickstart.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/quickstart.rst 2018-05-02 00:50:32.000000000 +0000 @@ -50,7 +50,14 @@ $ flask run * Running on http://127.0.0.1:5000/ -If you are on Windows you need to use ``set`` instead of ``export``. +If you are on Windows, the environment variable syntax depends on command line +interpreter. On Command Prompt:: + + C:\path\to\app>set FLASK_APP=hello.py + +And on PowerShell:: + + PS C:\path\to\app> $env:FLASK_APP = "hello.py" Alternatively you can use :command:`python -m flask`:: @@ -102,9 +109,9 @@ Invalid Import Name ``````````````````` -The ``FLASK_APP`` environment variable is the name of the module to import at -:command:`flask run`. In case that module is incorrectly named you will get an -import error upon start (or if debug is enabled when you navigate to the +The ``FLASK_APP`` environment variable is the name of the module to import at +:command:`flask run`. In case that module is incorrectly named you will get an +import error upon start (or if debug is enabled when you navigate to the application). It will tell you what it tried to import and why it failed. The most common reason is a typo or because you did not actually create an @@ -123,13 +130,14 @@ support the server will reload itself on code changes, and it will also provide you with a helpful debugger if things go wrong. -To enable debug mode you can export the ``FLASK_DEBUG`` environment variable +To enable all development features (including debug mode) you can export +the ``FLASK_ENV`` environment variable and set it to ``development`` before running the server:: - $ export FLASK_DEBUG=1 + $ export FLASK_ENV=development $ flask run -(On Windows you need to use ``set`` instead of ``export``). +(On Windows you need to use ``set`` instead of ``export``.) This does the following things: @@ -137,6 +145,9 @@ 2. it activates the automatic reloader 3. it enables the debug mode on the Flask application. +You can also control debug mode separately from the environment by +exporting ``FLASK_DEBUG=1``. + There are more parameters that are explained in the :ref:`server` docs. .. admonition:: Attention @@ -153,20 +164,22 @@ :class: screenshot :alt: screenshot of debugger in action +More information on using the debugger can be found in the `Werkzeug +documentation`_. + +.. _Werkzeug documentation: http://werkzeug.pocoo.org/docs/debug/#using-the-debugger + Have another debugger in mind? See :ref:`working-with-debuggers`. Routing ------- -Modern web applications have beautiful URLs. This helps people remember -the URLs, which is especially handy for applications that are used from -mobile devices with slower network connections. If the user can directly -go to the desired page without having to hit the index page it is more -likely they will like the page and come back next time. +Modern web applications use meaningful URLs to help users. Users are more +likely to like a page and come back if the page uses a meaningful URL they can +remember and use to directly visit a page. -As you have seen above, the :meth:`~flask.Flask.route` decorator is used to -bind a function to a URL. Here are some basic examples:: +Use the :meth:`~flask.Flask.route` decorator to bind a function to a URL. :: @app.route('/') def index(): @@ -176,16 +189,16 @@ def hello(): return 'Hello, World' -But there is more to it! You can make certain parts of the URL dynamic and -attach multiple rules to a function. +You can do more! You can make parts of the URL dynamic and attach multiple +rules to a function. Variable Rules `````````````` -To add variable parts to a URL you can mark these special sections as -````. Such a part is then passed as a keyword argument to your -function. Optionally a converter can be used by specifying a rule with -````. Here are some nice examples:: +You can add variable sections to a URL by marking sections with +````. Your function then receives the ```` +as a keyword argument. Optionally, you can use a converter to specify the type +of the argument like ````. :: @app.route('/user/') def show_user_profile(username): @@ -197,47 +210,44 @@ # show the post with the given id, the id is an integer return 'Post %d' % post_id -The following converters exist: - -=========== =============================================== -`string` accepts any text without a slash (the default) -`int` accepts integers -`float` like ``int`` but for floating point values -`path` like the default but also accepts slashes -`any` matches one of the items provided -`uuid` accepts UUID strings -=========== =============================================== - -.. admonition:: Unique URLs / Redirection Behavior - - Flask's URL rules are based on Werkzeug's routing module. The idea - behind that module is to ensure beautiful and unique URLs based on - precedents laid down by Apache and earlier HTTP servers. - - Take these two rules:: - - @app.route('/projects/') - def projects(): - return 'The project page' - - @app.route('/about') - def about(): - return 'The about page' - - Though they look rather similar, they differ in their use of the trailing - slash in the URL *definition*. In the first case, the canonical URL for the - ``projects`` endpoint has a trailing slash. In that sense, it is similar to - a folder on a filesystem. Accessing it without a trailing slash will cause - Flask to redirect to the canonical URL with the trailing slash. - - In the second case, however, the URL is defined without a trailing slash, - rather like the pathname of a file on UNIX-like systems. Accessing the URL - with a trailing slash will produce a 404 "Not Found" error. - - This behavior allows relative URLs to continue working even if the trailing - slash is omitted, consistent with how Apache and other servers work. Also, - the URLs will stay unique, which helps search engines avoid indexing the - same page twice. + @app.route('/path/') + def show_subpath(subpath): + # show the subpath after /path/ + return 'Subpath %s' % subpath + +Converter types: + +========== ========================================== +``string`` (default) accepts any text without a slash +``int`` accepts positive integers +``float`` accepts positive floating point values +``path`` like ``string`` but also accepts slashes +``uuid`` accepts UUID strings +========== ========================================== + +Unique URLs / Redirection Behavior +`````````````````````````````````` + +The following two rules differ in their use of a trailing slash. :: + + @app.route('/projects/') + def projects(): + return 'The project page' + + @app.route('/about') + def about(): + return 'The about page' + +The canonical URL for the ``projects`` endpoint has a trailing slash. +It's similar to a folder in a file system. If you access the URL without +a trailing slash, Flask redirects you to the canonical URL with the +trailing slash. + +The canonical URL for the ``about`` endpoint does not have a trailing +slash. It's similar to the pathname of a file. Accessing the URL with a +trailing slash produces a 404 "Not Found" error. This helps keep URLs +unique for these resources, which helps search engines avoid indexing +the same page twice. .. _url-building: @@ -245,129 +255,80 @@ URL Building ```````````` -If it can match URLs, can Flask also generate them? Of course it can. To -build a URL to a specific function you can use the :func:`~flask.url_for` -function. It accepts the name of the function as first argument and a number -of keyword arguments, each corresponding to the variable part of the URL rule. -Unknown variable parts are appended to the URL as query parameters. Here are -some examples:: - - >>> from flask import Flask, url_for - >>> app = Flask(__name__) - >>> @app.route('/') - ... def index(): pass - ... - >>> @app.route('/login') - ... def login(): pass - ... - >>> @app.route('/user/') - ... def profile(username): pass - ... - >>> with app.test_request_context(): - ... print url_for('index') - ... print url_for('login') - ... print url_for('login', next='/') - ... print url_for('profile', username='John Doe') - ... - / - /login - /login?next=/ - /user/John%20Doe - -(This also uses the :meth:`~flask.Flask.test_request_context` method, explained -below. It tells Flask to behave as though it is handling a request, even -though we are interacting with it through a Python shell. Have a look at the -explanation below. :ref:`context-locals`). +To build a URL to a specific function, use the :func:`~flask.url_for` function. +It accepts the name of the function as its first argument and any number of +keyword arguments, each corresponding to a variable part of the URL rule. +Unknown variable parts are appended to the URL as query parameters. Why would you want to build URLs using the URL reversing function :func:`~flask.url_for` instead of hard-coding them into your templates? -There are three good reasons for this: -1. Reversing is often more descriptive than hard-coding the URLs. More - importantly, it allows you to change URLs in one go, without having to - remember to change URLs all over the place. -2. URL building will handle escaping of special characters and Unicode - data transparently for you, so you don't have to deal with them. -3. If your application is placed outside the URL root - say, in - ``/myapplication`` instead of ``/`` - :func:`~flask.url_for` will handle - that properly for you. +1. Reversing is often more descriptive than hard-coding the URLs. +2. You can change your URLs in one go instead of needing to remember to + manually change hard-coded URLs. +3. URL building handles escaping of special characters and Unicode data + transparently. +4. The generated paths are always absolute, avoiding unexpected behavior + of relative paths in browsers. +5. If your application is placed outside the URL root, for example, in + ``/myapplication`` instead of ``/``, :func:`~flask.url_for` properly + handles that for you. + +For example, here we use the :meth:`~flask.Flask.test_request_context` method +to try out :func:`~flask.url_for`. :meth:`~flask.Flask.test_request_context` +tells Flask to behave as though it's handling a request even while we use a +Python shell. See :ref:`context-locals`. :: + + from flask import Flask, url_for + + app = Flask(__name__) + + @app.route('/') + def index(): + return 'index' + + @app.route('/login') + def login(): + return 'login' + + @app.route('/user/') + def profile(username): + return '{}\'s profile'.format(username) + with app.test_request_context(): + print(url_for('index')) + print(url_for('login')) + print(url_for('login', next='/')) + print(url_for('profile', username='John Doe')) + + / + /login + /login?next=/ + /user/John%20Doe HTTP Methods ```````````` -HTTP (the protocol web applications are speaking) knows different methods for -accessing URLs. By default, a route only answers to ``GET`` requests, but that -can be changed by providing the ``methods`` argument to the -:meth:`~flask.Flask.route` decorator. Here are some examples:: +Web applications use different HTTP methods when accessing URLs. You should +familiarize yourself with the HTTP methods as you work with Flask. By default, +a route only answers to ``GET`` requests. You can use the ``methods`` argument +of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods. +:: from flask import request @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': - do_the_login() + return do_the_login() else: - show_the_login_form() + return show_the_login_form() -If ``GET`` is present, ``HEAD`` will be added automatically for you. You -don't have to deal with that. It will also make sure that ``HEAD`` requests -are handled as the `HTTP RFC`_ (the document describing the HTTP -protocol) demands, so you can completely ignore that part of the HTTP -specification. Likewise, as of Flask 0.6, ``OPTIONS`` is implemented for you -automatically as well. - -You have no idea what an HTTP method is? Worry not, here is a quick -introduction to HTTP methods and why they matter: - -The HTTP method (also often called "the verb") tells the server what the -client wants to *do* with the requested page. The following methods are -very common: - -``GET`` - The browser tells the server to just *get* the information stored on - that page and send it. This is probably the most common method. - -``HEAD`` - The browser tells the server to get the information, but it is only - interested in the *headers*, not the content of the page. An - application is supposed to handle that as if a ``GET`` request was - received but to not deliver the actual content. In Flask you don't - have to deal with that at all, the underlying Werkzeug library handles - that for you. - -``POST`` - The browser tells the server that it wants to *post* some new - information to that URL and that the server must ensure the data is - stored and only stored once. This is how HTML forms usually - transmit data to the server. - -``PUT`` - Similar to ``POST`` but the server might trigger the store procedure - multiple times by overwriting the old values more than once. Now you - might be asking why this is useful, but there are some good reasons - to do it this way. Consider that the connection is lost during - transmission: in this situation a system between the browser and the - server might receive the request safely a second time without breaking - things. With ``POST`` that would not be possible because it must only - be triggered once. - -``DELETE`` - Remove the information at the given location. - -``OPTIONS`` - Provides a quick way for a client to figure out which methods are - supported by this URL. Starting with Flask 0.6, this is implemented - for you automatically. - -Now the interesting part is that in HTML4 and XHTML1, the only methods a -form can submit to the server are ``GET`` and ``POST``. But with JavaScript -and future HTML standards you can use the other methods as well. Furthermore -HTTP has become quite popular lately and browsers are no longer the only -clients that are using HTTP. For instance, many revision control systems -use it. +If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method +and handles ``HEAD`` requests according to the `HTTP RFC`_. Likewise, +``OPTIONS`` is automatically implemented for you. -.. _HTTP RFC: http://www.ietf.org/rfc/rfc2068.txt +.. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt Static Files ------------ @@ -538,16 +499,16 @@ `````````````````` The request object is documented in the API section and we will not cover -it here in detail (see :class:`~flask.request`). Here is a broad overview of +it here in detail (see :class:`~flask.Request`). Here is a broad overview of some of the most common operations. First of all you have to import it from the ``flask`` module:: from flask import request The current request method is available by using the -:attr:`~flask.request.method` attribute. To access form data (data +:attr:`~flask.Request.method` attribute. To access form data (data transmitted in a ``POST`` or ``PUT`` request) you can use the -:attr:`~flask.request.form` attribute. Here is a full example of the two +:attr:`~flask.Request.form` attribute. Here is a full example of the two attributes mentioned above:: @app.route('/login', methods=['POST', 'GET']) @@ -570,7 +531,7 @@ deal with that problem. To access parameters submitted in the URL (``?key=value``) you can use the -:attr:`~flask.request.args` attribute:: +:attr:`~flask.Request.args` attribute:: searchword = request.args.get('key', '') @@ -579,7 +540,7 @@ bad request page in that case is not user friendly. For a full list of methods and attributes of the request object, head over -to the :class:`~flask.request` documentation. +to the :class:`~flask.Request` documentation. File Uploads @@ -768,6 +729,9 @@ app = Flask(__name__) + # Set the secret key to some random bytes. Keep this really secret! + app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' + @app.route('/') def index(): if 'username' in session: @@ -792,24 +756,18 @@ session.pop('username', None) return redirect(url_for('index')) - # set the secret key. keep this really secret: - app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' - The :func:`~flask.escape` mentioned here does escaping for you if you are not using the template engine (as in this example). .. admonition:: How to generate good secret keys - The problem with random is that it's hard to judge what is truly random. And - a secret key should be as random as possible. Your operating system - has ways to generate pretty random stuff based on a cryptographic - random generator which can be used to get such a key:: - - >>> import os - >>> os.urandom(24) - '\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O>> redirect_url() -Traceback (most recent call last): - File "", line 1, in -AttributeError: 'NoneType' object has no attribute 'request' -That makes a lot of sense because we currently do not have a request we -could access. So we have to make a request and bind it to the current -context. The :attr:`~flask.Flask.test_request_context` method can create -us a :class:`~flask.ctx.RequestContext`: +Manually Push a Context +----------------------- ->>> ctx = app.test_request_context('/?next=http://example.com/') +If you try to access :data:`request`, or anything that uses it, outside +a request context, you'll get this error message: -This context can be used in two ways. Either with the ``with`` statement -or by calling the :meth:`~flask.ctx.RequestContext.push` and -:meth:`~flask.ctx.RequestContext.pop` methods: +.. code-block:: pytb ->>> ctx.push() + RuntimeError: Working outside of request context. -From that point onwards you can work with the request object: + 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. ->>> redirect_url() -u'http://example.com/' +This should typically only happen when testing code that expects an +active request. One option is to use the +:meth:`test client ` to simulate a full request. Or +you can use :meth:`~Flask.test_request_context` in a ``with`` block, and +everything that runs in the block will have access to :data:`request`, +populated with your test data. :: -Until you call `pop`: + def generate_report(year): + format = request.args.get('format') + ... ->>> ctx.pop() + with app.test_request_context( + '/make_report/2017', data={'format': 'short'}): + generate_report() -Because the request context is internally maintained as a stack you can -push and pop multiple times. This is very handy to implement things like -internal redirects. +If you see that error somewhere else in your code not related to +testing, it most likely indicates that you should move that code into a +view function. + +For information on how to use the request context from the interactive +Python shell, see :doc:`/shell`. -For more information of how to utilize the request context from the -interactive Python shell, head over to the :ref:`shell` chapter. How the Context Works --------------------- -If you look into how the Flask WSGI application internally works, you will -find a piece of code that looks very much like this:: +The :meth:`Flask.wsgi_app` method is called to handle each request. It +manages the contexts during the request. Internally, the request and +application contexts work as stacks, :data:`_request_ctx_stack` and +:data:`_app_ctx_stack`. When contexts are pushed onto the stack, the +proxies that depend on them are available and point at information from +the top context on the stack. + +When the request starts, a :class:`~ctx.RequestContext` is created and +pushed, which creates and pushes an :class:`~ctx.AppContext` first if +a context for that application is not already the top context. While +these contexts are pushed, the :data:`current_app`, :data:`g`, +:data:`request`, and :data:`session` proxies are available to the +original thread handling the request. + +Because the contexts are stacks, other contexts may be pushed to change +the proxies during a request. While this is not a common pattern, it +can be used in advanced applications to, for example, do internal +redirects or chain different applications together. + +After the request is dispatched and a response is generated and sent, +the request context is popped, which then pops the application context. +Immediately before they are popped, the :meth:`~Flask.teardown_request` +and :meth:`~Flask.teardown_appcontext` functions are are executed. These +execute even if an unhandled exception occurred during dispatch. - def wsgi_app(self, environ): - with self.request_context(environ): - try: - response = self.full_dispatch_request() - except Exception as e: - response = self.make_response(self.handle_exception(e)) - return response(environ, start_response) - -The method :meth:`~Flask.request_context` returns a new -:class:`~flask.ctx.RequestContext` object and uses it in combination with -the ``with`` statement to bind the context. Everything that is called from -the same thread from this point onwards until the end of the ``with`` -statement will have access to the request globals (:data:`flask.request` -and others). - -The request context internally works like a stack: The topmost level on -the stack is the current active request. -:meth:`~flask.ctx.RequestContext.push` adds the context to the stack on -the very top, :meth:`~flask.ctx.RequestContext.pop` removes it from the -stack again. On popping the application's -:func:`~flask.Flask.teardown_request` functions are also executed. - -Another thing of note is that the request context will automatically also -create an :ref:`application context ` when it's pushed and -there is no application context for that application so far. .. _callbacks-and-errors: Callbacks and Errors -------------------- -What happens if an error occurs in Flask during request processing? This -particular behavior changed in 0.7 because we wanted to make it easier to -understand what is actually happening. The new behavior is quite simple: - -1. Before each request, :meth:`~flask.Flask.before_request` functions are - executed. If one of these functions return a response, the other - functions are no longer called. In any case however the return value - is treated as a replacement for the view's return value. - -2. If the :meth:`~flask.Flask.before_request` functions did not return a - response, the regular request handling kicks in and the view function - that was matched has the chance to return a response. - -3. The return value of the view is then converted into an actual response - object and handed over to the :meth:`~flask.Flask.after_request` - functions which have the chance to replace it or modify it in place. - -4. At the end of the request the :meth:`~flask.Flask.teardown_request` - functions are executed. This always happens, even in case of an - unhandled exception down the road or if a before-request handler was - not executed yet or at all (for example in test environments sometimes - you might want to not execute before-request callbacks). - -Now what happens on errors? In production mode if an exception is not -caught, the 500 internal server handler is called. In development mode -however the exception is not further processed and bubbles up to the WSGI -server. That way things like the interactive debugger can provide helpful -debug information. - -An important change in 0.7 is that the internal server error is now no -longer post processed by the after request callbacks and after request -callbacks are no longer guaranteed to be executed. This way the internal -dispatching code looks cleaner and is easier to customize and understand. +Flask dispatches a request in multiple stages which can affect the +request, response, and how errors are handled. The contexts are active +during all of these stages. + +A :class:`Blueprint` can add handlers for these events that are specific +to the blueprint. The handlers for a blueprint will run if the blueprint +owns the route that matches the request. + +#. Before each request, :meth:`~Flask.before_request` functions are + called. If one of these functions return a value, the other + functions are skipped. The return value is treated as the response + and the view function is not called. + +#. If the :meth:`~Flask.before_request` functions did not return a + response, the view function for the matched route is called and + returns a response. + +#. The return value of the view is converted into an actual response + object and passed to the :meth:`~Flask.after_request` + functions. Each function returns a modified or new response object. + +#. After the response is returned, the contexts are popped, which calls + the :meth:`~Flask.teardown_request` and + :meth:`~Flask.teardown_appcontext` functions. These functions are + called even if an unhandled exception was raised at any point above. + +If an exception is raised before the teardown functions, Flask tries to +match it with an :meth:`~Flask.errorhandler` function to handle the +exception and return a response. If no error handler is found, or the +handler itself raises an exception, Flask returns a generic +``500 Internal Server Error`` response. The teardown functions are still +called, and are passed the exception object. + +If debug mode is enabled, unhandled exceptions are not converted to a +``500`` response and instead are propagated to the WSGI server. This +allows the development server to present the interactive debugger with +the traceback. -The new teardown functions are supposed to be used as a replacement for -things that absolutely need to happen at the end of request. Teardown Callbacks ------------------- +~~~~~~~~~~~~~~~~~~ + +The teardown callbacks are independent of the request dispatch, and are +instead called by the contexts when they are popped. The functions are +called even if there is an unhandled exception during dispatch, and for +manually pushed contexts. This means there is no guarantee that any +other parts of the request dispatch have run first. Be sure to write +these functions in a way that does not depend on other callbacks and +will not fail. + +During testing, it can be useful to defer popping the contexts after the +request ends, so that their data can be accessed in the test function. +Using the :meth:`~Flask.test_client` as a ``with`` block to preserve the +contexts until the with block exits. + +.. code-block:: python + + from flask import Flask, request + + app = Flask(__name__) + + @app.route('/') + def hello(): + print('during view') + return 'Hello, World!' + + @app.teardown_request + def show_teardown(exception): + print('after with block') + + with app.test_request_context(): + print('during with block') + + # teardown functions are called after the context with block exits + + with app.test_client(): + client.get('/') + # the contexts are not popped even though the request ended + print(request.path) + + # the contexts are popped and teardown functions are called after + # the client with block exists + + +Signals +~~~~~~~ + +If :data:`~signals.signals_available` is true, the following signals are +sent: + +#. :data:`request_started` is sent before the + :meth:`~Flask.before_request` functions are called. + +#. :data:`request_finished` is sent after the + :meth:`~Flask.after_request` functions are called. + +#. :data:`got_request_exception` is sent when an exception begins to + be handled, but before an :meth:`~Flask.errorhandler` is looked up or + called. + +#. :data:`request_tearing_down` is sent after the + :meth:`~Flask.teardown_request` functions are called. + + +Context Preservation on Error +----------------------------- + +At the end of a request, the request context is popped and all data +associated with it is destroyed. If an error occurs during development, +it is useful to delay destroying the data for debugging purposes. + +When the development server is running in development mode (the +``FLASK_ENV`` environment variable is set to ``'development'``), the +error and data will be preserved and shown in the interactive debugger. + +This behavior can be controlled with the +:data:`PRESERVE_CONTEXT_ON_EXCEPTION` config. As described above, it +defaults to ``True`` in the development environment. + +Do not enable :data:`PRESERVE_CONTEXT_ON_EXCEPTION` in production, as it +will cause your application to leak memory on exceptions. -The teardown callbacks are special callbacks in that they are executed at -a different point. Strictly speaking they are independent of the actual -request handling as they are bound to the lifecycle of the -:class:`~flask.ctx.RequestContext` object. When the request context is -popped, the :meth:`~flask.Flask.teardown_request` functions are called. - -This is important to know if the life of the request context is prolonged -by using the test client in a with statement or when using the request -context from the command line:: - - with app.test_client() as client: - resp = client.get('/foo') - # the teardown functions are still not called at that point - # even though the response ended and you have the response - # object in your hand - - # only when the code reaches this point the teardown functions - # are called. Alternatively the same thing happens if another - # request was triggered from the test client - -It's easy to see the behavior from the command line: - ->>> app = Flask(__name__) ->>> @app.teardown_request -... def teardown_request(exception=None): -... print 'this runs after request' -... ->>> ctx = app.test_request_context() ->>> ctx.push() ->>> ctx.pop() -this runs after request ->>> - -Keep in mind that teardown callbacks are always executed, even if -before-request callbacks were not executed yet but an exception happened. -Certain parts of the test system might also temporarily create a request -context without calling the before-request handlers. Make sure to write -your teardown-request handlers in a way that they will never fail. .. _notes-on-proxies: Notes On Proxies ---------------- -Some of the objects provided by Flask are proxies to other objects. The -reason behind this is that these proxies are shared between threads and -they have to dispatch to the actual object bound to a thread behind the -scenes as necessary. +Some of the objects provided by Flask are proxies to other objects. The +proxies are accessed in the same way for each worker thread, but +point to the unique object bound to each worker behind the scenes as +described on this page. Most of the time you don't have to care about that, but there are some exceptions where it is good to know that this object is an actual proxy: -- The proxy objects do not fake their inherited types, so if you want to - perform actual instance checks, you have to do that on the instance - that is being proxied (see `_get_current_object` below). -- if the object reference is important (so for example for sending - :ref:`signals`) +- The proxy objects cannot fake their type as the actual object types. + If you want to perform instance checks, you have to do that on the + object being proxied. +- If the specific object reference is important, for example for + sending :ref:`signals` or passing data to a background thread. -If you need to get access to the underlying object that is proxied, you -can use the :meth:`~werkzeug.local.LocalProxy._get_current_object` method:: +If you need to access the underlying object that is proxied, use the +:meth:`~werkzeug.local.LocalProxy._get_current_object` method:: app = current_app._get_current_object() my_signal.send(app) - -Context Preservation on Error ------------------------------ - -If an error occurs or not, at the end of the request the request context -is popped and all data associated with it is destroyed. During -development however that can be problematic as you might want to have the -information around for a longer time in case an exception occurred. In -Flask 0.6 and earlier in debug mode, if an exception occurred, the -request context was not popped so that the interactive debugger can still -provide you with important information. - -Starting with Flask 0.7 you have finer control over that behavior by -setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By -default it's linked to the setting of ``DEBUG``. If the application is in -debug mode the context is preserved, in production mode it's not. - -Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` in production mode -as it will cause your application to leak memory on exceptions. However -it can be useful during development to get the same error preserving -behavior as in development mode when attempting to debug an error that -only occurs under production settings. diff -Nru flask-0.12.2/docs/security.rst flask-1.0.2/docs/security.rst --- flask-0.12.2/docs/security.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/security.rst 2018-05-02 00:50:32.000000000 +0000 @@ -15,7 +15,7 @@ have to properly escape text so that it cannot include arbitrary HTML tags. For more information on that have a look at the Wikipedia article on `Cross-Site Scripting -`_. +`_. Flask configures Jinja2 to automatically escape all values unless explicitly told otherwise. This should rule out all XSS problems caused @@ -38,7 +38,7 @@ .. sourcecode:: html+jinja - the text + Why is this necessary? Because if you would not be doing that, an attacker could easily inject custom JavaScript handlers. For example an @@ -46,15 +46,26 @@ .. sourcecode:: html - onmouseover=alert(document.cookie) + onmouseover=alert(document.cookie) -When the user would then move with the mouse over the link, the cookie +When the user would then move with the mouse over the input, the cookie would be presented to the user in an alert window. But instead of showing the cookie to the user, a good attacker might also execute any other JavaScript code. In combination with CSS injections the attacker might even make the element fill out the entire page so that the user would just have to have the mouse anywhere on the page to trigger the attack. +There is one class of XSS issues that Jinja's escaping does not protect +against. The ``a`` tag's ``href`` attribute can contain a `javascript:` URI, +which the browser will execute when clicked if not secured properly. + +.. sourcecode:: html + + click here + click here + +To prevent this, you'll need to set the :ref:`security-csp` response header. + Cross-Site Request Forgery (CSRF) --------------------------------- @@ -104,3 +115,146 @@ `_, so this behavior was changed and :func:`~flask.jsonify` now supports serializing arrays. + +Security Headers +---------------- + +Browsers recognize various response headers in order to control security. We +recommend reviewing each of the headers below for use in your application. +The `Flask-Talisman`_ extension can be used to manage HTTPS and the security +headers for you. + +.. _Flask-Talisman: https://github.com/GoogleCloudPlatform/flask-talisman + +HTTP Strict Transport Security (HSTS) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tells the browser to convert all HTTP requests to HTTPS, preventing +man-in-the-middle (MITM) attacks. :: + + response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' + +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security + +.. _security-csp: + +Content Security Policy (CSP) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tell the browser where it can load various types of resource from. This header +should be used whenever possible, but requires some work to define the correct +policy for your site. A very strict policy would be:: + + response.headers['Content-Security-Policy'] = "default-src 'self'" + +- https://csp.withgoogle.com/docs/index.html +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + +X-Content-Type-Options +~~~~~~~~~~~~~~~~~~~~~~ + +Forces the browser to honor the response content type instead of trying to +detect it, which can be abused to generate a cross-site scripting (XSS) +attack. :: + + response.headers['X-Content-Type-Options'] = 'nosniff' + +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options + +X-Frame-Options +~~~~~~~~~~~~~~~ + +Prevents external sites from embedding your site in an ``iframe``. This +prevents a class of attacks where clicks in the outer frame can be translated +invisibly to clicks on your page's elements. This is also known as +"clickjacking". :: + + response.headers['X-Frame-Options'] = 'SAMEORIGIN' + +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options + +X-XSS-Protection +~~~~~~~~~~~~~~~~ + +The browser will try to prevent reflected XSS attacks by not loading the page +if the request contains something that looks like JavaScript and the response +contains the same data. :: + + response.headers['X-XSS-Protection'] = '1; mode=block' + +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection + + +.. _security-cookie: + +Set-Cookie options +~~~~~~~~~~~~~~~~~~ + +These options can be added to a ``Set-Cookie`` header to improve their +security. Flask has configuration options to set these on the session cookie. +They can be set on other cookies too. + +- ``Secure`` limits cookies to HTTPS traffic only. +- ``HttpOnly`` protects the contents of cookies from being read with + JavaScript. +- ``SameSite`` restricts how cookies are sent with requests from + external sites. Can be set to ``'Lax'`` (recommended) or ``'Strict'``. + ``Lax`` prevents sending cookies with CSRF-prone requests from + external sites, such as submitting a form. ``Strict`` prevents sending + cookies with all external requests, including following regular links. + +:: + + app.config.update( + SESSION_COOKIE_SECURE=True, + SESSION_COOKIE_HTTPONLY=True, + SESSION_COOKIE_SAMESITE='Lax', + ) + + response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Lax') + +Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after +the given time, or the current time plus the age, respectively. If neither +option is set, the cookie will be removed when the browser is closed. :: + + # cookie expires after 10 minutes + response.set_cookie('snakes', '3', max_age=600) + +For the session cookie, if :attr:`session.permanent ` +is set, then :data:`PERMANENT_SESSION_LIFETIME` is used to set the expiration. +Flask's default cookie implementation validates that the cryptographic +signature is not older than this value. Lowering this value may help mitigate +replay attacks, where intercepted cookies can be sent at a later time. :: + + app.config.update( + PERMANENT_SESSION_LIFETIME=600 + ) + + @app.route('/login', methods=['POST']) + def login(): + ... + session.clear() + session['user_id'] = user.id + session.permanent = True + ... + +Use :class:`itsdangerous.TimedSerializer` to sign and validate other cookie +values (or any values that need secure signatures). + +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie + +.. _samesite_support: https://caniuse.com/#feat=same-site-cookie-attribute + + +HTTP Public Key Pinning (HPKP) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This tells the browser to authenticate with the server using only the specific +certificate key to prevent MITM attacks. + +.. warning:: + Be careful when enabling this, as it is very difficult to undo if you set up + or upgrade your key incorrectly. + +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning diff -Nru flask-0.12.2/docs/server.rst flask-1.0.2/docs/server.rst --- flask-0.12.2/docs/server.rst 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/docs/server.rst 2018-05-02 00:50:32.000000000 +0000 @@ -12,23 +12,33 @@ Command Line ------------ -The :command:`flask` command line script (:ref:`cli`) is strongly recommended for -development because it provides a superior reload experience due to how it -loads the application. The basic usage is like this:: +The :command:`flask` command line script (:ref:`cli`) is strongly +recommended for development because it provides a superior reload +experience due to how it loads the application. The basic usage is like +this:: $ export FLASK_APP=my_application - $ export FLASK_DEBUG=1 + $ export FLASK_ENV=development $ flask run -This will enable the debugger, the reloader and then start the server on +This enables the development environment, including the interactive +debugger and reloader, and then starts the server on *http://localhost:5000/*. The individual features of the server can be controlled by passing more -arguments to the ``run`` option. For instance the reloader can be +arguments to the ``run`` option. For instance the reloader can be disabled:: $ flask run --no-reload +.. note:: + + Prior to Flask 1.0 the :envvar:`FLASK_ENV` environment variable was + not supported and you needed to enable debug mode by exporting + ``FLASK_DEBUG=1``. This can still be used to control debug mode, but + you should prefer setting the development environment as shown + above. + In Code ------- diff -Nru flask-0.12.2/docs/signals.rst flask-1.0.2/docs/signals.rst --- flask-0.12.2/docs/signals.rst 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/docs/signals.rst 2018-05-02 00:50:32.000000000 +0000 @@ -27,7 +27,7 @@ The big advantage of signals over handlers is that you can safely subscribe to them for just a split second. These temporary -subscriptions are helpful for unittesting for example. Say you want to +subscriptions are helpful for unit testing for example. Say you want to know what templates were rendered as part of a request: signals allow you to do exactly that. @@ -45,7 +45,7 @@ unless you really want to listen for signals from all applications. This is especially true if you are developing an extension. -For example, here is a helper context manager that can be used in a unittest +For example, here is a helper context manager that can be used in a unit test to determine which templates were rendered and what variables were passed to the template:: @@ -187,4 +187,4 @@ Take a look at :ref:`core-signals-list` for a list of all builtin signals. -.. _blinker: https://pypi.python.org/pypi/blinker +.. _blinker: https://pypi.org/project/blinker/ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/_static/debugger.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/_static/debugger.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/_static/flask.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/_static/flask.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/_static/flaskr.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/_static/flaskr.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/_static/logo-full.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/_static/logo-full.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/_static/no.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/_static/no.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/_static/pycharm-runconfig.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/_static/pycharm-runconfig.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/_static/touch-icon.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/_static/touch-icon.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/_static/yes.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/_static/yes.png differ diff -Nru flask-0.12.2/docs/styleguide.rst flask-1.0.2/docs/styleguide.rst --- flask-0.12.2/docs/styleguide.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/styleguide.rst 2018-05-02 00:50:32.000000000 +0000 @@ -167,7 +167,7 @@ """ Module header: - The module header consists of an utf-8 encoding declaration (if non + The module header consists of a utf-8 encoding declaration (if non ASCII letters are used, but it is recommended all the time) and a standard docstring:: diff -Nru flask-0.12.2/docs/_templates/sidebarintro.html flask-1.0.2/docs/_templates/sidebarintro.html --- flask-0.12.2/docs/_templates/sidebarintro.html 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/_templates/sidebarintro.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -

About Flask

-

- Flask is a micro webdevelopment framework for Python. You are currently - looking at the documentation of the development version. -

-

Other Formats

-

- You can download the documentation in other formats as well: -

- -

Useful Links

- diff -Nru flask-0.12.2/docs/_templates/sidebarlogo.html flask-1.0.2/docs/_templates/sidebarlogo.html --- flask-0.12.2/docs/_templates/sidebarlogo.html 2015-03-11 15:05:43.000000000 +0000 +++ flask-1.0.2/docs/_templates/sidebarlogo.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ - diff -Nru flask-0.12.2/docs/templating.rst flask-1.0.2/docs/templating.rst --- flask-0.12.2/docs/templating.rst 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/docs/templating.rst 2018-05-02 00:50:32.000000000 +0000 @@ -1,3 +1,5 @@ +.. _templates: + Templates ========= diff -Nru flask-0.12.2/docs/testing.rst flask-1.0.2/docs/testing.rst --- flask-0.12.2/docs/testing.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/testing.rst 2018-05-02 00:50:32.000000000 +0000 @@ -5,116 +5,119 @@ **Something that is untested is broken.** -The origin of this quote is unknown and while it is not entirely correct, it is also -not far from the truth. Untested applications make it hard to +The origin of this quote is unknown and while it is not entirely correct, it +is also not far from the truth. Untested applications make it hard to improve existing code and developers of untested applications tend to become pretty paranoid. If an application has automated tests, you can safely make changes and instantly know if anything breaks. Flask provides a way to test your application by exposing the Werkzeug test :class:`~werkzeug.test.Client` and handling the context locals for you. -You can then use that with your favourite testing solution. In this documentation -we will use the :mod:`unittest` package that comes pre-installed with Python. +You can then use that with your favourite testing solution. + +In this documentation we will use the `pytest`_ package as the base +framework for our tests. You can install it with ``pip``, like so:: + + pip install pytest + +.. _pytest: + https://pytest.org The Application --------------- First, we need an application to test; we will use the application from the :ref:`tutorial`. If you don't have that application yet, get the -sources from `the examples`_. - -.. _the examples: - https://github.com/pallets/flask/tree/master/examples/flaskr/ +source code from :gh:`the examples `. The Testing Skeleton -------------------- -In order to test the application, we add a second module -(:file:`flaskr_tests.py`) and create a unittest skeleton there:: +We begin by adding a tests directory under the application root. Then +create a Python file to store our tests (:file:`test_flaskr.py`). When we +format the filename like ``test_*.py``, it will be auto-discoverable by +pytest. + +Next, we create a `pytest fixture`_ called +:func:`client` that configures +the application for testing and initializes a new database.:: import os - import flaskr - import unittest import tempfile - class FlaskrTestCase(unittest.TestCase): + import pytest + + from flaskr import flaskr + + + @pytest.fixture + def client(): + db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() + flaskr.app.config['TESTING'] = True + client = flaskr.app.test_client() + + with flaskr.app.app_context(): + flaskr.init_db() + + yield client - def setUp(self): - self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.testing = True - self.app = flaskr.app.test_client() - with flaskr.app.app_context(): - flaskr.init_db() - - def tearDown(self): - os.close(self.db_fd) - os.unlink(flaskr.app.config['DATABASE']) - - if __name__ == '__main__': - unittest.main() - -The code in the :meth:`~unittest.TestCase.setUp` method creates a new test -client and initializes a new database. This function is called before -each individual test function is run. To delete the database after the -test, we close the file and remove it from the filesystem in the -:meth:`~unittest.TestCase.tearDown` method. Additionally during setup the -``TESTING`` config flag is activated. What it does is disable the error -catching during request handling so that you get better error reports when -performing test requests against the application. - -This test client will give us a simple interface to the application. We can -trigger test requests to the application, and the client will also keep track -of cookies for us. + os.close(db_fd) + os.unlink(flaskr.app.config['DATABASE']) -Because SQLite3 is filesystem-based we can easily use the tempfile module +This client fixture will be called by each individual test. It gives us a +simple interface to the application, where we can trigger test requests to the +application. The client will also keep track of cookies for us. + +During setup, the ``TESTING`` config flag is activated. What +this does is disable error catching during request handling, so that +you get better error reports when performing test requests against the +application. + +Because SQLite3 is filesystem-based, we can easily use the :mod:`tempfile` module to create a temporary database and initialize it. The :func:`~tempfile.mkstemp` function does two things for us: it returns a low-level file handle and a random file name, the latter we use as database name. We just have to keep the `db_fd` around so that we can use the :func:`os.close` function to close the file. +To delete the database after the test, the fixture closes the file and removes +it from the filesystem. + If we now run the test suite, we should see the following output:: - $ python flaskr_tests.py + $ pytest - ---------------------------------------------------------------------- - Ran 0 tests in 0.000s + ================ test session starts ================ + rootdir: ./flask/examples/flaskr, inifile: setup.cfg + collected 0 items - OK + =========== no tests ran in 0.07 seconds ============ -Even though it did not run any actual tests, we already know that our flaskr +Even though it did not run any actual tests, we already know that our ``flaskr`` application is syntactically valid, otherwise the import would have died with an exception. +.. _pytest fixture: + https://docs.pytest.org/en/latest/fixture.html + The First Test -------------- Now it's time to start testing the functionality of the application. Let's check that the application shows "No entries here so far" if we -access the root of the application (``/``). To do this, we add a new -test method to our class, like this:: +access the root of the application (``/``). To do this, we add a new +test function to :file:`test_flaskr.py`, like this:: - class FlaskrTestCase(unittest.TestCase): + def test_empty_db(client): + """Start with a blank database.""" - def setUp(self): - self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.testing = True - self.app = flaskr.app.test_client() - with flaskr.app.app_context(): - flaskr.init_db() - - def tearDown(self): - os.close(self.db_fd) - os.unlink(flaskr.app.config['DATABASE']) - - def test_empty_db(self): - rv = self.app.get('/') - assert b'No entries here so far' in rv.data + rv = client.get('/') + assert b'No entries here so far' in rv.data Notice that our test functions begin with the word `test`; this allows -:mod:`unittest` to automatically identify the method as a test to run. +`pytest`_ to automatically identify the function as a test to run. -By using `self.app.get` we can send an HTTP ``GET`` request to the application with +By using ``client.get`` we can send an HTTP ``GET`` request to the application with the given path. The return value will be a :class:`~flask.Flask.response_class` object. We can now use the :attr:`~werkzeug.wrappers.BaseResponse.data` attribute to inspect the return value (as string) from the application. In this case, we ensure that @@ -122,12 +125,15 @@ Run it again and you should see one passing test:: - $ python flaskr_tests.py - . - ---------------------------------------------------------------------- - Ran 1 test in 0.034s + $ pytest -v + + ================ test session starts ================ + rootdir: ./flask/examples/flaskr, inifile: setup.cfg + collected 1 items - OK + tests/test_flaskr.py::test_empty_db PASSED + + ============= 1 passed in 0.10 seconds ============== Logging In and Out ------------------ @@ -138,39 +144,47 @@ pages with the required form data (username and password). And because the login and logout pages redirect, we tell the client to `follow_redirects`. -Add the following two methods to your `FlaskrTestCase` class:: +Add the following two functions to your :file:`test_flaskr.py` file:: + + def login(client, username, password): + return client.post('/login', data=dict( + username=username, + password=password + ), follow_redirects=True) - def login(self, username, password): - return self.app.post('/login', data=dict( - username=username, - password=password - ), follow_redirects=True) - def logout(self): - return self.app.get('/logout', follow_redirects=True) + def logout(client): + return client.get('/logout', follow_redirects=True) Now we can easily test that logging in and out works and that it fails with -invalid credentials. Add this new test to the class:: +invalid credentials. Add this new test function:: + + def test_login_logout(client): + """Make sure login and logout works.""" - def test_login_logout(self): - rv = self.login('admin', 'default') - assert b'You were logged in' in rv.data - rv = self.logout() - assert b'You were logged out' in rv.data - rv = self.login('adminx', 'default') - assert b'Invalid username' in rv.data - rv = self.login('admin', 'defaultx') - assert b'Invalid password' in rv.data + rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD']) + assert b'You were logged in' in rv.data + + rv = logout(client) + assert b'You were logged out' in rv.data + + rv = login(client, flaskr.app.config['USERNAME'] + 'x', flaskr.app.config['PASSWORD']) + assert b'Invalid username' in rv.data + + rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'] + 'x') + assert b'Invalid password' in rv.data Test Adding Messages -------------------- -We should also test that adding messages works. Add a new test method +We should also test that adding messages works. Add a new test function like this:: - def test_messages(self): - self.login('admin', 'default') - rv = self.app.post('/add', data=dict( + def test_messages(client): + """Test that messages work.""" + + login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD']) + rv = client.post('/add', data=dict( title='', text='HTML allowed here' ), follow_redirects=True) @@ -183,20 +197,17 @@ Running that should now give us three passing tests:: - $ python flaskr_tests.py - ... - ---------------------------------------------------------------------- - Ran 3 tests in 0.332s - - OK + $ pytest -v -For more complex tests with headers and status codes, check out the -`MiniTwit Example`_ from the sources which contains a larger test -suite. + ================ test session starts ================ + rootdir: ./flask/examples/flaskr, inifile: setup.cfg + collected 3 items + tests/test_flaskr.py::test_empty_db PASSED + tests/test_flaskr.py::test_login_logout PASSED + tests/test_flaskr.py::test_messages PASSED -.. _MiniTwit Example: - https://github.com/pallets/flask/tree/master/examples/minitwit/ + ============= 3 passed in 0.23 seconds ============== Other Testing Tricks @@ -355,3 +366,91 @@ Note that in this case you have to use the ``sess`` object instead of the :data:`flask.session` proxy. The object however itself will provide the same interface. + + +Testing JSON APIs +----------------- + +.. versionadded:: 1.0 + +Flask has great support for JSON, and is a popular choice for building JSON +APIs. Making requests with JSON data and examining JSON data in responses is +very convenient:: + + from flask import request, jsonify + + @app.route('/api/auth') + def auth(): + json_data = request.get_json() + email = json_data['email'] + password = json_data['password'] + return jsonify(token=generate_token(email, password)) + + with app.test_client() as c: + rv = c.post('/api/auth', json={ + 'username': 'flask', 'password': 'secret' + }) + json_data = rv.get_json() + assert verify_token(email, json_data['token']) + +Passing the ``json`` argument in the test client methods sets the request data +to the JSON-serialized object and sets the content type to +``application/json``. You can get the JSON data from the request or response +with ``get_json``. + + +.. _testing-cli: + +Testing CLI Commands +-------------------- + +Click comes with `utilities for testing`_ your CLI commands. A +:class:`~click.testing.CliRunner` runs commands in isolation and +captures the output in a :class:`~click.testing.Result` object. + +Flask provides :meth:`~flask.Flask.test_cli_runner` to create a +:class:`~flask.testing.FlaskCliRunner` that passes the Flask app to the +CLI automatically. Use its :meth:`~flask.testing.FlaskCliRunner.invoke` +method to call commands in the same way they would be called from the +command line. :: + + import click + + @app.cli.command('hello') + @click.option('--name', default='World') + def hello_command(name) + click.echo(f'Hello, {name}!') + + def test_hello(): + runner = app.test_cli_runner() + + # invoke the command directly + result = runner.invoke(hello_command, ['--name', 'Flask']) + assert 'Hello, Flask' in result.output + + # or by name + result = runner.invoke(args=['hello']) + assert 'World' in result.output + +In the example above, invoking the command by name is useful because it +verifies that the command was correctly registered with the app. + +If you want to test how your command parses parameters, without running +the command, use its :meth:`~click.BaseCommand.make_context` method. +This is useful for testing complex validation rules and custom types. :: + + def upper(ctx, param, value): + if value is not None: + return value.upper() + + @app.cli.command('hello') + @click.option('--name', default='World', callback=upper) + def hello_command(name) + click.echo(f'Hello, {name}!') + + def test_hello_params(): + context = hello_command.make_context('hello', ['--name', 'flask']) + assert context.params['name'] == 'FLASK' + +.. _click: http://click.pocoo.org/ +.. _utilities for testing: http://click.pocoo.org/testing diff -Nru flask-0.12.2/docs/tutorial/blog.rst flask-1.0.2/docs/tutorial/blog.rst --- flask-0.12.2/docs/tutorial/blog.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/blog.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,336 @@ +.. currentmodule:: flask + +Blog Blueprint +============== + +You'll use the same techniques you learned about when writing the +authentication blueprint to write the blog blueprint. The blog should +list all posts, allow logged in users to create posts, and allow the +author of a post to edit or delete it. + +As you implement each view, keep the development server running. As you +save your changes, try going to the URL in your browser and testing them +out. + +The Blueprint +------------- + +Define the blueprint and register it in the application factory. + +.. code-block:: python + :caption: ``flaskr/blog.py`` + + from flask import ( + Blueprint, flash, g, redirect, render_template, request, url_for + ) + from werkzeug.exceptions import abort + + from flaskr.auth import login_required + from flaskr.db import get_db + + bp = Blueprint('blog', __name__) + +Import and register the blueprint from the factory using +:meth:`app.register_blueprint() `. Place the +new code at the end of the factory function before returning the app. + +.. code-block:: python + :caption: ``flaskr/__init__.py`` + + def create_app(): + app = ... + # existing code omitted + + from . import blog + app.register_blueprint(blog.bp) + app.add_url_rule('/', endpoint='index') + + return app + + +Unlike the auth blueprint, the blog blueprint does not have a +``url_prefix``. So the ``index`` view will be at ``/``, the ``create`` +view at ``/create``, and so on. The blog is the main feature of Flaskr, +so it makes sense that the blog index will be the main index. + +However, the endpoint for the ``index`` view defined below will be +``blog.index``. Some of the authentication views referred to a plain +``index`` endpoint. :meth:`app.add_url_rule() ` +associates the endpoint name ``'index'`` with the ``/`` url so that +``url_for('index')`` or ``url_for('blog.index')`` will both work, +generating the same ``/`` URL either way. + +In another application you might give the blog blueprint a +``url_prefix`` and define a separate ``index`` view in the application +factory, similar to the ``hello`` view. Then the ``index`` and +``blog.index`` endpoints and URLs would be different. + + +Index +----- + +The index will show all of the posts, most recent first. A ``JOIN`` is +used so that the author information from the ``user`` table is +available in the result. + +.. code-block:: python + :caption: ``flaskr/blog.py`` + + @bp.route('/') + def index(): + db = get_db() + posts = db.execute( + 'SELECT p.id, title, body, created, author_id, username' + ' FROM post p JOIN user u ON p.author_id = u.id' + ' ORDER BY created DESC' + ).fetchall() + return render_template('blog/index.html', posts=posts) + +.. code-block:: html+jinja + :caption: ``flaskr/templates/blog/index.html`` + + {% extends 'base.html' %} + + {% block header %} +

{% block title %}Posts{% endblock %}

+ {% if g.user %} + New + {% endif %} + {% endblock %} + + {% block content %} + {% for post in posts %} +
+
+
+

{{ post['title'] }}

+
by {{ post['username'] }} on {{ post['created'].strftime('%Y-%m-%d') }}
+
+ {% if g.user['id'] == post['author_id'] %} + Edit + {% endif %} +
+

{{ post['body'] }}

+
+ {% if not loop.last %} +
+ {% endif %} + {% endfor %} + {% endblock %} + +When a user is logged in, the ``header`` block adds a link to the +``create`` view. When the user is the author of a post, they'll see an +"Edit" link to the ``update`` view for that post. ``loop.last`` is a +special variable available inside `Jinja for loops`_. It's used to +display a line after each post except the last one, to visually separate +them. + +.. _Jinja for loops: http://jinja.pocoo.org/docs/templates/#for + + +Create +------ + +The ``create`` view works the same as the auth ``register`` view. Either +the form is displayed, or the posted data is validated and the post is +added to the database or an error is shown. + +The ``login_required`` decorator you wrote earlier is used on the blog +views. A user must be logged in to visit these views, otherwise they +will be redirected to the login page. + +.. code-block:: python + :caption: ``flaskr/blog.py`` + + @bp.route('/create', methods=('GET', 'POST')) + @login_required + def create(): + if request.method == 'POST': + title = request.form['title'] + body = request.form['body'] + error = None + + if not title: + error = 'Title is required.' + + if error is not None: + flash(error) + else: + db = get_db() + db.execute( + 'INSERT INTO post (title, body, author_id)' + ' VALUES (?, ?, ?)', + (title, body, g.user['id']) + ) + db.commit() + return redirect(url_for('blog.index')) + + return render_template('blog/create.html') + +.. code-block:: html+jinja + :caption: ``flaskr/templates/blog/create.html`` + + {% extends 'base.html' %} + + {% block header %} +

{% block title %}New Post{% endblock %}

+ {% endblock %} + + {% block content %} +
+ + + + + +
+ {% endblock %} + + +Update +------ + +Both the ``update`` and ``delete`` views will need to fetch a ``post`` +by ``id`` and check if the author matches the logged in user. To avoid +duplicating code, you can write a function to get the ``post`` and call +it from each view. + +.. code-block:: python + :caption: ``flaskr/blog.py`` + + def get_post(id, check_author=True): + post = get_db().execute( + 'SELECT p.id, title, body, created, author_id, username' + ' FROM post p JOIN user u ON p.author_id = u.id' + ' WHERE p.id = ?', + (id,) + ).fetchone() + + if post is None: + abort(404, "Post id {0} doesn't exist.".format(id)) + + if check_author and post['author_id'] != g.user['id']: + abort(403) + + return post + +:func:`abort` will raise a special exception that returns an HTTP status +code. It takes an optional message to show with the error, otherwise a +default message is used. ``404`` means "Not Found", and ``403`` means +"Forbidden". (``401`` means "Unauthorized", but you redirect to the +login page instead of returning that status.) + +The ``check_author`` argument is defined so that the function can be +used to get a ``post`` without checking the author. This would be useful +if you wrote a view to show an individual post on a page, where the user +doesn't matter because they're not modifying the post. + +.. code-block:: python + :caption: ``flaskr/blog.py`` + + @bp.route('//update', methods=('GET', 'POST')) + @login_required + def update(id): + post = get_post(id) + + if request.method == 'POST': + title = request.form['title'] + body = request.form['body'] + error = None + + if not title: + error = 'Title is required.' + + if error is not None: + flash(error) + else: + db = get_db() + db.execute( + 'UPDATE post SET title = ?, body = ?' + ' WHERE id = ?', + (title, body, id) + ) + db.commit() + return redirect(url_for('blog.index')) + + return render_template('blog/update.html', post=post) + +Unlike the views you've written so far, the ``update`` function takes +an argument, ``id``. That corresponds to the ```` in the route. +A real URL will look like ``/1/update``. Flask will capture the ``1``, +ensure it's an :class:`int`, and pass it as the ``id`` argument. If you +don't specify ``int:`` and instead do ````, it will be a string. +To generate a URL to the update page, :func:`url_for` needs to be passed +the ``id`` so it knows what to fill in: +``url_for('blog.update', id=post['id'])``. This is also in the +``index.html`` file above. + +The ``create`` and ``update`` views look very similar. The main +difference is that the ``update`` view uses a ``post`` object and an +``UPDATE`` query instead of an ``INSERT``. With some clever refactoring, +you could use one view and template for both actions, but for the +tutorial it's clearer to keep them separate. + +.. code-block:: html+jinja + :caption: ``flaskr/templates/blog/update.html`` + + {% extends 'base.html' %} + + {% block header %} +

{% block title %}Edit "{{ post['title'] }}"{% endblock %}

+ {% endblock %} + + {% block content %} +
+ + + + + +
+
+
+ +
+ {% endblock %} + +This template has two forms. The first posts the edited data to the +current page (``//update``). The other form contains only a button +and specifies an ``action`` attribute that posts to the delete view +instead. The button uses some JavaScript to show a confirmation dialog +before submitting. + +The pattern ``{{ request.form['title'] or post['title'] }}`` is used to +choose what data appears in the form. When the form hasn't been +submitted, the original ``post`` data appears, but if invalid form data +was posted you want to display that so the user can fix the error, so +``request.form`` is used instead. :data:`request` is another variable +that's automatically available in templates. + + +Delete +------ + +The delete view doesn't have its own template, the delete button is part +of ``update.html`` and posts to the ``//delete`` URL. Since there +is no template, it will only handle the ``POST`` method then redirect +to the ``index`` view. + +.. code-block:: python + :caption: ``flaskr/blog.py`` + + @bp.route('//delete', methods=('POST',)) + @login_required + def delete(id): + get_post(id) + db = get_db() + db.execute('DELETE FROM post WHERE id = ?', (id,)) + db.commit() + return redirect(url_for('blog.index')) + +Congratulations, you've now finished writing your application! Take some +time to try out everything in the browser. However, there's still more +to do before the project is complete. + +Continue to :doc:`install`. diff -Nru flask-0.12.2/docs/tutorial/css.rst flask-1.0.2/docs/tutorial/css.rst --- flask-0.12.2/docs/tutorial/css.rst 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/docs/tutorial/css.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -.. _tutorial-css: - -Step 8: Adding Style -==================== - -Now that everything else works, it's time to add some style to the -application. Just create a stylesheet called :file:`style.css` in the -:file:`static` folder: - -.. sourcecode:: css - - body { font-family: sans-serif; background: #eee; } - a, h1, h2 { color: #377ba8; } - h1, h2 { font-family: 'Georgia', serif; margin: 0; } - h1 { border-bottom: 2px solid #eee; } - h2 { font-size: 1.2em; } - - .page { margin: 2em auto; width: 35em; border: 5px solid #ccc; - padding: 0.8em; background: white; } - .entries { list-style: none; margin: 0; padding: 0; } - .entries li { margin: 0.8em 1.2em; } - .entries li h2 { margin-left: -1em; } - .add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; } - .add-entry dl { font-weight: bold; } - .metanav { text-align: right; font-size: 0.8em; padding: 0.3em; - margin-bottom: 1em; background: #fafafa; } - .flash { background: #cee5F5; padding: 0.5em; - border: 1px solid #aacbe2; } - .error { background: #f0d6d6; padding: 0.5em; } - -Continue with :ref:`tutorial-testing`. diff -Nru flask-0.12.2/docs/tutorial/database.rst flask-1.0.2/docs/tutorial/database.rst --- flask-0.12.2/docs/tutorial/database.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/database.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,213 @@ +.. currentmodule:: flask + +Define and Access the Database +============================== + +The application will use a `SQLite`_ database to store users and posts. +Python comes with built-in support for SQLite in the :mod:`sqlite3` +module. + +SQLite is convenient because it doesn't require setting up a separate +database server and is built-in to Python. However, if concurrent +requests try to write to the database at the same time, they will slow +down as each write happens sequentially. Small applications won't notice +this. Once you become big, you may want to switch to a different +database. + +The tutorial doesn't go into detail about SQL. If you are not familiar +with it, the SQLite docs describe the `language`_. + +.. _SQLite: https://sqlite.org/about.html +.. _language: https://sqlite.org/lang.html + + +Connect to the Database +----------------------- + +The first thing to do when working with a SQLite database (and most +other Python database libraries) is to create a connection to it. Any +queries and operations are performed using the connection, which is +closed after the work is finished. + +In web applications this connection is typically tied to the request. It +is created at some point when handling a request, and closed before the +response is sent. + +.. code-block:: python + :caption: ``flaskr/db.py`` + + import sqlite3 + + import click + from flask import current_app, g + from flask.cli import with_appcontext + + + def get_db(): + if 'db' not in g: + g.db = sqlite3.connect( + current_app.config['DATABASE'], + detect_types=sqlite3.PARSE_DECLTYPES + ) + g.db.row_factory = sqlite3.Row + + return g.db + + + def close_db(e=None): + db = g.pop('db', None) + + if db is not None: + db.close() + +:data:`g` is a special object that is unique for each request. It is +used to store data that might be accessed by multiple functions during +the request. The connection is stored and reused instead of creating a +new connection if ``get_db`` is called a second time in the same +request. + +:data:`current_app` is another special object that points to the Flask +application handling the request. Since you used an application factory, +there is no application object when writing the rest of your code. +``get_db`` will be called when the application has been created and is +handling a request, so :data:`current_app` can be used. + +:func:`sqlite3.connect` establishes a connection to the file pointed at +by the ``DATABASE`` configuration key. This file doesn't have to exist +yet, and won't until you initialize the database later. + +:class:`sqlite3.Row` tells the connection to return rows that behave +like dicts. This allows accessing the columns by name. + +``close_db`` checks if a connection was created by checking if ``g.db`` +was set. If the connection exists, it is closed. Further down you will +tell your application about the ``close_db`` function in the application +factory so that it is called after each request. + + +Create the Tables +----------------- + +In SQLite, data is stored in *tables* and *columns*. These need to be +created before you can store and retrieve data. Flaskr will store users +in the ``user`` table, and posts in the ``post`` table. Create a file +with the SQL commands needed to create empty tables: + +.. code-block:: sql + :caption: ``flaskr/schema.sql`` + + DROP TABLE IF EXISTS user; + DROP TABLE IF EXISTS post; + + CREATE TABLE user ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password TEXT NOT NULL + ); + + CREATE TABLE post ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + author_id INTEGER NOT NULL, + created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + title TEXT NOT NULL, + body TEXT NOT NULL, + FOREIGN KEY (author_id) REFERENCES user (id) + ); + +Add the Python functions that will run these SQL commands to the +``db.py`` file: + +.. code-block:: python + :caption: ``flaskr/db.py`` + + def init_db(): + db = get_db() + + with current_app.open_resource('schema.sql') as f: + db.executescript(f.read().decode('utf8')) + + + @click.command('init-db') + @with_appcontext + def init_db_command(): + """Clear the existing data and create new tables.""" + init_db() + click.echo('Initialized the database.') + +:meth:`open_resource() ` opens a file relative to +the ``flaskr`` package, which is useful since you won't necessarily know +where that location is when deploying the application later. ``get_db`` +returns a database connection, which is used to execute the commands +read from the file. + +:func:`click.command` defines a command line command called ``init-db`` +that calls the ``init_db`` function and shows a success message to the +user. You can read :ref:`cli` to learn more about writing commands. + + +Register with the Application +----------------------------- + +The ``close_db`` and ``init_db_command`` functions need to be registered +with the application instance, otherwise they won't be used by the +application. However, since you're using a factory function, that +instance isn't available when writing the functions. Instead, write a +function that takes an application and does the registration. + +.. code-block:: python + :caption: ``flaskr/db.py`` + + def init_app(app): + app.teardown_appcontext(close_db) + app.cli.add_command(init_db_command) + +:meth:`app.teardown_appcontext() ` tells +Flask to call that function when cleaning up after returning the +response. + +:meth:`app.cli.add_command() ` adds a new +command that can be called with the ``flask`` command. + +Import and call this function from the factory. Place the new code at +the end of the factory function before returning the app. + +.. code-block:: python + :caption: ``flaskr/__init__.py`` + + def create_app(): + app = ... + # existing code omitted + + from . import db + db.init_app(app) + + return app + + +Initialize the Database File +---------------------------- + +Now that ``init-db`` has been registered with the app, it can be called +using the ``flask`` command, similar to the ``run`` command from the +previous page. + +.. note:: + + If you're still running the server from the previous page, you can + either stop the server, or run this command in a new terminal. If + you use a new terminal, remember to change to your project directory + and activate the env as described in :ref:`install-activate-env`. + You'll also need to set ``FLASK_APP`` and ``FLASK_ENV`` as shown on + the previous page. + +Run the ``init-db`` command: + +.. code-block:: none + + flask init-db + Initialized the database. + +There will now be a ``flaskr.sqlite`` file in the ``instance`` folder in +your project. + +Continue to :doc:`views`. diff -Nru flask-0.12.2/docs/tutorial/dbcon.rst flask-1.0.2/docs/tutorial/dbcon.rst --- flask-0.12.2/docs/tutorial/dbcon.rst 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/docs/tutorial/dbcon.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -.. _tutorial-dbcon: - -Step 4: Database Connections ----------------------------- - -You currently have a function for establishing a database connection with -`connect_db`, but by itself, it is not particularly useful. Creating and -closing database connections all the time is very inefficient, so you will -need to keep it around for longer. Because database connections -encapsulate a transaction, you will need to make sure that only one -request at a time uses the connection. An elegant way to do this is by -utilizing the *application context*. - -Flask provides two contexts: the *application context* and the -*request context*. For the time being, all you have to know is that there -are special variables that use these. For instance, the -:data:`~flask.request` variable is the request object associated with -the current request, whereas :data:`~flask.g` is a general purpose -variable associated with the current application context. The tutorial -will cover some more details of this later on. - -For the time being, all you have to know is that you can store information -safely on the :data:`~flask.g` object. - -So when do you put it on there? To do that you can make a helper -function. The first time the function is called, it will create a database -connection for the current context, and successive calls will return the -already established connection:: - - def get_db(): - """Opens a new database connection if there is none yet for the - current application context. - """ - if not hasattr(g, 'sqlite_db'): - g.sqlite_db = connect_db() - return g.sqlite_db - -Now you know how to connect, but how can you properly disconnect? For -that, Flask provides us with the :meth:`~flask.Flask.teardown_appcontext` -decorator. It's executed every time the application context tears down:: - - @app.teardown_appcontext - def close_db(error): - """Closes the database again at the end of the request.""" - if hasattr(g, 'sqlite_db'): - g.sqlite_db.close() - -Functions marked with :meth:`~flask.Flask.teardown_appcontext` are called -every time the app context tears down. What does this mean? -Essentially, the app context is created before the request comes in and is -destroyed (torn down) whenever the request finishes. A teardown can -happen because of two reasons: either everything went well (the error -parameter will be ``None``) or an exception happened, in which case the error -is passed to the teardown function. - -Curious about what these contexts mean? Have a look at the -:ref:`app-context` documentation to learn more. - -Continue to :ref:`tutorial-dbinit`. - -.. hint:: Where do I put this code? - - If you've been following along in this tutorial, you might be wondering - where to put the code from this step and the next. A logical place is to - group these module-level functions together, and put your new - ``get_db`` and ``close_db`` functions below your existing - ``connect_db`` function (following the tutorial line-by-line). - - If you need a moment to find your bearings, take a look at how the `example - source`_ is organized. In Flask, you can put all of your application code - into a single Python module. You don't have to, and if your app :ref:`grows - larger `, it's a good idea not to. - -.. _example source: - https://github.com/pallets/flask/tree/master/examples/flaskr/ diff -Nru flask-0.12.2/docs/tutorial/dbinit.rst flask-1.0.2/docs/tutorial/dbinit.rst --- flask-0.12.2/docs/tutorial/dbinit.rst 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/docs/tutorial/dbinit.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -.. _tutorial-dbinit: - -Step 5: Creating The Database -============================= - -As outlined earlier, Flaskr is a database powered application, and more -precisely, it is an application powered by a relational database system. Such -systems need a schema that tells them how to store that information. -Before starting the server for the first time, it's important to create -that schema. - -Such a schema can be created by piping the ``schema.sql`` file into the -`sqlite3` command as follows:: - - sqlite3 /tmp/flaskr.db < schema.sql - -The downside of this is that it requires the ``sqlite3`` command to be -installed, which is not necessarily the case on every system. This also -requires that you provide the path to the database, which can introduce -errors. It's a good idea to add a function that initializes the database -for you, to the application. - -To do this, you can create a function and hook it into a :command:`flask` -command that initializes the database. For now just take a look at the -code segment below. A good place to add this function, and command, is -just below the `connect_db` function in :file:`flaskr.py`:: - - def init_db(): - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() - - @app.cli.command('initdb') - def initdb_command(): - """Initializes the database.""" - init_db() - print('Initialized the database.') - -The ``app.cli.command()`` decorator registers a new command with the -:command:`flask` script. When the command executes, Flask will automatically -create an application context which is bound to the right application. -Within the function, you can then access :attr:`flask.g` and other things as -you might expect. When the script ends, the application context tears down -and the database connection is released. - -You will want to keep an actual function around that initializes the database, -though, so that we can easily create databases in unit tests later on. (For -more information see :ref:`testing`.) - -The :func:`~flask.Flask.open_resource` method of the application object -is a convenient helper function that will open a resource that the -application provides. This function opens a file from the resource -location (the :file:`flaskr/flaskr` folder) and allows you to read from it. -It is used in this example to execute a script on the database connection. - -The connection object provided by SQLite can give you a cursor object. -On that cursor, there is a method to execute a complete script. Finally, you -only have to commit the changes. SQLite3 and other transactional -databases will not commit unless you explicitly tell it to. - -Now, it is possible to create a database with the :command:`flask` script:: - - flask initdb - Initialized the database. - -.. admonition:: Troubleshooting - - If you get an exception later on stating that a table cannot be found, check - that you did execute the ``initdb`` command and that your table names are - correct (singular vs. plural, for example). - -Continue with :ref:`tutorial-views` diff -Nru flask-0.12.2/docs/tutorial/deploy.rst flask-1.0.2/docs/tutorial/deploy.rst --- flask-0.12.2/docs/tutorial/deploy.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/deploy.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,121 @@ +Deploy to Production +==================== + +This part of the tutorial assumes you have a server that you want to +deploy your application to. It gives an overview of how to create the +distribution file and install it, but won't go into specifics about +what server or software to use. You can set up a new environment on your +development computer to try out the instructions below, but probably +shouldn't use it for hosting a real public application. See +:doc:`/deploying/index` for a list of many different ways to host your +application. + + +Build and Install +----------------- + +When you want to deploy your application elsewhere, you build a +distribution file. The current standard for Python distribution is the +*wheel* format, with the ``.whl`` extension. Make sure the wheel library +is installed first: + +.. code-block:: none + + pip install wheel + +Running ``setup.py`` with Python gives you a command line tool to issue +build-related commands. The ``bdist_wheel`` command will build a wheel +distribution file. + +.. code-block:: none + + python setup.py bdist_wheel + +You can find the file in ``dist/flaskr-1.0.0-py3-none-any.whl``. The +file name is the name of the project, the version, and some tags about +the file can install. + +Copy this file to another machine, +:ref:`set up a new virtualenv `, then install the +file with ``pip``. + +.. code-block:: none + + pip install flaskr-1.0.0-py3-none-any.whl + +Pip will install your project along with its dependencies. + +Since this is a different machine, you need to run ``init-db`` again to +create the database in the instance folder. + +.. code-block:: none + + export FLASK_APP=flaskr + flask init-db + +When Flask detects that it's installed (not in editable mode), it uses +a different directory for the instance folder. You can find it at +``venv/var/flaskr-instance`` instead. + + +Configure the Secret Key +------------------------ + +In the beginning of the tutorial that you gave a default value for +:data:`SECRET_KEY`. This should be changed to some random bytes in +production. Otherwise, attackers could use the public ``'dev'`` key to +modify the session cookie, or anything else that uses the secret key. + +You can use the following command to output a random secret key: + +.. code-block:: none + + python -c 'import os; print(os.urandom(16))' + + b'_5#y2L"F4Q8z\n\xec]/' + +Create the ``config.py`` file in the instance folder, which the factory +will read from if it exists. Copy the generated value into it. + +.. code-block:: python + :caption: ``venv/var/flaskr-instance/config.py`` + + SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/' + +You can also set any other necessary configuration here, although +``SECRET_KEY`` is the only one needed for Flaskr. + + +Run with a Production Server +---------------------------- + +When running publicly rather than in development, you should not use the +built-in development server (``flask run``). The development server is +provided by Werkzeug for convenience, but is not designed to be +particularly efficient, stable, or secure. + +Instead, use a production WSGI server. For example, to use `Waitress`_, +first install it in the virtual environment: + +.. code-block:: none + + pip install waitress + +You need to tell Waitress about your application, but it doesn't use +``FLASK_APP`` like ``flask run`` does. You need to tell it to import and +call the application factory to get an application object. + +.. code-block:: none + + waitress-serve --call 'flaskr:create_app' + + Serving on http://0.0.0.0:8080 + +See :doc:`/deploying/index` for a list of many different ways to host +your application. Waitress is just an example, chosen for the tutorial +because it supports both Windows and Linux. There are many more WSGI +servers and deployment options that you may choose for your project. + +.. _Waitress: https://docs.pylonsproject.org/projects/waitress/ + +Continue to :doc:`next`. diff -Nru flask-0.12.2/docs/tutorial/factory.rst flask-1.0.2/docs/tutorial/factory.rst --- flask-0.12.2/docs/tutorial/factory.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/factory.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,177 @@ +.. currentmodule:: flask + +Application Setup +================= + +A Flask application is an instance of the :class:`Flask` class. +Everything about the application, such as configuration and URLs, will +be registered with this class. + +The most straightforward way to create a Flask application is to create +a global :class:`Flask` instance directly at the top of your code, like +how the "Hello, World!" example did on the previous page. While this is +simple and useful in some cases, it can cause some tricky issues as the +project grows. + +Instead of creating a :class:`Flask` instance globally, you will create +it inside a function. This function is known as the *application +factory*. Any configuration, registration, and other setup the +application needs will happen inside the function, then the application +will be returned. + + +The Application Factory +----------------------- + +It's time to start coding! Create the ``flaskr`` directory and add the +``__init__.py`` file. The ``__init__.py`` serves double duty: it will +contain the application factory, and it tells Python that the ``flaskr`` +directory should be treated as a package. + +.. code-block:: none + + mkdir flaskr + +.. code-block:: python + :caption: ``flaskr/__init__.py`` + + import os + + from flask import Flask + + + def create_app(test_config=None): + # create and configure the app + app = Flask(__name__, instance_relative_config=True) + app.config.from_mapping( + SECRET_KEY='dev', + DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'), + ) + + if test_config is None: + # load the instance config, if it exists, when not testing + app.config.from_pyfile('config.py', silent=True) + else: + # load the test config if passed in + app.config.from_mapping(test_config) + + # ensure the instance folder exists + try: + os.makedirs(app.instance_path) + except OSError: + pass + + # a simple page that says hello + @app.route('/hello') + def hello(): + return 'Hello, World!' + + return app + +``create_app`` is the application factory function. You'll add to it +later in the tutorial, but it already does a lot. + +#. ``app = Flask(__name__, instance_relative_config=True)`` creates the + :class:`Flask` instance. + + * ``__name__`` is the name of the current Python module. The app + needs to know where it's located to set up some paths, and + ``__name__`` is a convenient way to tell it that. + + * ``instance_relative_config=True`` tells the app that + configuration files are relative to the + :ref:`instance folder `. The instance folder + is located outside the ``flaskr`` package and can hold local + data that shouldn't be committed to version control, such as + configuration secrets and the database file. + +#. :meth:`app.config.from_mapping() ` sets + some default configuration that the app will use: + + * :data:`SECRET_KEY` is used by Flask and extensions to keep data + safe. It's set to ``'dev'`` to provide a convenient value + during development, but it should be overridden with a random + value when deploying. + + * ``DATABASE`` is the path where the SQLite database file will be + saved. It's under + :attr:`app.instance_path `, which is the + path that Flask has chosen for the instance folder. You'll learn + more about the database in the next section. + +#. :meth:`app.config.from_pyfile() ` overrides + the default configuration with values taken from the ``config.py`` + file in the instance folder if it exists. For example, when + deploying, this can be used to set a real ``SECRET_KEY``. + + * ``test_config`` can also be passed to the factory, and will be + used instead of the instance configuration. This is so the tests + you'll write later in the tutorial can be configured + independently of any development values you have configured. + +#. :func:`os.makedirs` ensures that + :attr:`app.instance_path ` exists. Flask + doesn't create the instance folder automatically, but it needs to be + created because your project will create the SQLite database file + there. + +#. :meth:`@app.route() ` creates a simple route so you can + see the application working before getting into the rest of the + tutorial. It creates a connection between the URL ``/hello`` and a + function that returns a response, the string ``'Hello, World!'`` in + this case. + + +Run The Application +------------------- + +Now you can run your application using the ``flask`` command. From the +terminal, tell Flask where to find your application, then run it in +development mode. + +Development mode shows an interactive debugger whenever a page raises an +exception, and restarts the server whenever you make changes to the +code. You can leave it running and just reload the browser page as you +follow the tutorial. + +For Linux and Mac: + +.. code-block:: none + + export FLASK_APP=flaskr + export FLASK_ENV=development + flask run + +For Windows cmd, use ``set`` instead of ``export``: + +.. code-block:: none + + set FLASK_APP=flaskr + set FLASK_ENV=development + flask run + +For Windows PowerShell, use ``$env:`` instead of ``export``: + +.. code-block:: none + + $env:FLASK_APP = "flaskr" + $env:FLASK_ENV = "development" + flask run + +You'll see output similar to this: + +.. code-block:: none + + * Serving Flask app "flaskr" + * Environment: development + * Debug mode: on + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + * Restarting with stat + * Debugger is active! + * Debugger PIN: 855-212-761 + +Visit http://127.0.0.1:5000/hello in a browser and you should see the +"Hello, World!" message. Congratulations, you're now running your Flask +web application! + +Continue to :doc:`database`. Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/tutorial/flaskr_edit.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/tutorial/flaskr_edit.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/tutorial/flaskr_index.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/tutorial/flaskr_index.png differ Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/docs/tutorial/flaskr_login.png and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/docs/tutorial/flaskr_login.png differ diff -Nru flask-0.12.2/docs/tutorial/folders.rst flask-1.0.2/docs/tutorial/folders.rst --- flask-0.12.2/docs/tutorial/folders.rst 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/docs/tutorial/folders.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -.. _tutorial-folders: - -Step 0: Creating The Folders -============================ - -Before getting started, you will need to create the folders needed for this -application:: - - /flaskr - /flaskr - /static - /templates - -The application will be installed and run as Python package. This is the -recommended way to install and run Flask applications. You will see exactly -how to run ``flaskr`` later on in this tutorial. For now go ahead and create -the applications directory structure. In the next few steps you will be -creating the database schema as well as the main module. - -As a quick side note, the files inside of the :file:`static` folder are -available to users of the application via HTTP. This is the place where CSS and -JavaScript files go. Inside the :file:`templates` folder, Flask will look for -`Jinja2`_ templates. You will see examples of this later on. - -For now you should continue with :ref:`tutorial-schema`. - -.. _Jinja2: http://jinja.pocoo.org/ diff -Nru flask-0.12.2/docs/tutorial/index.rst flask-1.0.2/docs/tutorial/index.rst --- flask-0.12.2/docs/tutorial/index.rst 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/docs/tutorial/index.rst 2018-05-02 00:50:32.000000000 +0000 @@ -3,31 +3,64 @@ Tutorial ======== -You want to develop an application with Python and Flask? Here you have -the chance to learn by example. In this tutorial, we will create a simple -microblogging application. It only supports one user that can create -text-only entries and there are no feeds or comments, but it still -features everything you need to get started. We will use Flask and SQLite -as a database (which comes out of the box with Python) so there is nothing -else you need. +.. toctree:: + :caption: Contents: + :maxdepth: 1 -If you want the full source code in advance or for comparison, check out -the `example source`_. + layout + factory + database + views + templates + static + blog + install + tests + deploy + next -.. _example source: - https://github.com/pallets/flask/tree/master/examples/flaskr/ +This tutorial will walk you through creating a basic blog application +called Flaskr. Users will be able to register, log in, create posts, +and edit or delete their own posts. You will be able to package and +install the application on other computers. -.. toctree:: - :maxdepth: 2 +.. image:: flaskr_index.png + :align: center + :class: screenshot + :alt: screenshot of index page + +It's assumed that you're already familiar with Python. The `official +tutorial`_ in the Python docs is a great way to learn or review first. + +.. _official tutorial: https://docs.python.org/3/tutorial/ + +While it's designed to give a good starting point, the tutorial doesn't +cover all of Flask's features. Check out the :ref:`quickstart` for an +overview of what Flask can do, then dive into the docs to find out more. +The tutorial only uses what's provided by Flask and Python. In another +project, you might decide to use :ref:`extensions` or other libraries to +make some tasks simpler. + +.. image:: flaskr_login.png + :align: center + :class: screenshot + :alt: screenshot of login page + +Flask is flexible. It doesn't require you to use any particular project +or code layout. However, when first starting, it's helpful to use a more +structured approach. This means that the tutorial will require a bit of +boilerplate up front, but it's done to avoid many common pitfalls that +new developers encounter, and it creates a project that's easy to expand +on. Once you become more comfortable with Flask, you can step out of +this structure and take full advantage of Flask's flexibility. + +.. image:: flaskr_edit.png + :align: center + :class: screenshot + :alt: screenshot of login page + +:gh:`The tutorial project is available as an example in the Flask +repository `, if you want to compare your project +with the final product as you follow the tutorial. - introduction - folders - schema - setup - packaging - dbcon - dbinit - views - templates - css - testing +Continue to :doc:`layout`. diff -Nru flask-0.12.2/docs/tutorial/install.rst flask-1.0.2/docs/tutorial/install.rst --- flask-0.12.2/docs/tutorial/install.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/install.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,113 @@ +Make the Project Installable +============================ + +Making your project installable means that you can build a +*distribution* file and install that in another environment, just like +you installed Flask in your project's environment. This makes deploying +your project the same as installing any other library, so you're using +all the standard Python tools to manage everything. + +Installing also comes with other benefits that might not be obvious from +the tutorial or as a new Python user, including: + +* Currently, Python and Flask understand how to use the ``flaskr`` + package only because you're running from your project's directory. + Installing means you can import it no matter where you run from. + +* You can manage your project's dependencies just like other packages + do, so ``pip install yourproject.whl`` installs them. + +* Test tools can isolate your test environment from your development + environment. + +.. note:: + This is being introduced late in the tutorial, but in your future + projects you should always start with this. + + +Describe the Project +-------------------- + +The ``setup.py`` file describes your project and the files that belong +to it. + +.. code-block:: python + :caption: ``setup.py`` + + from setuptools import find_packages, setup + + setup( + name='flaskr', + version='1.0.0', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=[ + 'flask', + ], + ) + + +``packages`` tells Python what package directories (and the Python files +they contain) to include. ``find_packages()`` finds these directories +automatically so you don't have to type them out. To include other +files, such as the static and templates directories, +``include_package_data`` is set. Python needs another file named +``MANIFEST.in`` to tell what this other data is. + +.. code-block:: none + :caption: ``MANIFEST.in`` + + include flaskr/schema.sql + graft flaskr/static + graft flaskr/templates + global-exclude *.pyc + +This tells Python to copy everything in the ``static`` and ``templates`` +directories, and the ``schema.sql`` file, but to exclude all bytecode +files. + +See the `official packaging guide`_ for another explanation of the files +and options used. + +.. _official packaging guide: https://packaging.python.org/tutorials/distributing-packages/ + + +Install the Project +------------------- + +Use ``pip`` to install your project in the virtual environment. + +.. code-block:: none + + pip install -e . + +This tells pip to find ``setup.py`` in the current directory and install +it in *editable* or *development* mode. Editable mode means that as you +make changes to your local code, you'll only need to re-install if you +change the metadata about the project, such as its dependencies. + +You can observe that the project is now installed with ``pip list``. + +.. code-block:: none + + pip list + + Package Version Location + -------------- --------- ---------------------------------- + click 6.7 + Flask 1.0 + flaskr 1.0.0 /home/user/Projects/flask-tutorial + itsdangerous 0.24 + Jinja2 2.10 + MarkupSafe 1.0 + pip 9.0.3 + setuptools 39.0.1 + Werkzeug 0.14.1 + wheel 0.30.0 + +Nothing changes from how you've been running your project so far. +``FLASK_APP`` is still set to ``flaskr`` and ``flask run`` still runs +the application. + +Continue to :doc:`tests`. diff -Nru flask-0.12.2/docs/tutorial/introduction.rst flask-1.0.2/docs/tutorial/introduction.rst --- flask-0.12.2/docs/tutorial/introduction.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/tutorial/introduction.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -.. _tutorial-introduction: - -Introducing Flaskr -================== - -This tutorial will demonstrate a blogging application named Flaskr, but feel -free to choose your own less Web-2.0-ish name ;) Essentially, it will do the -following things: - -1. Let the user sign in and out with credentials specified in the - configuration. Only one user is supported. -2. When the user is logged in, they can add new entries to the page - consisting of a text-only title and some HTML for the text. This HTML - is not sanitized because we trust the user here. -3. The index page shows all entries so far in reverse chronological order - (newest on top) and the user can add new ones from there if logged in. - -SQLite3 will be used directly for this application because it's good enough -for an application of this size. For larger applications, however, -it makes a lot of sense to use `SQLAlchemy`_, as it handles database -connections in a more intelligent way, allowing you to target different -relational databases at once and more. You might also want to consider -one of the popular NoSQL databases if your data is more suited for those. - -Here a screenshot of the final application: - -.. image:: ../_static/flaskr.png - :align: center - :class: screenshot - :alt: screenshot of the final application - -Continue with :ref:`tutorial-folders`. - -.. _SQLAlchemy: http://www.sqlalchemy.org/ diff -Nru flask-0.12.2/docs/tutorial/layout.rst flask-1.0.2/docs/tutorial/layout.rst --- flask-0.12.2/docs/tutorial/layout.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/layout.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,110 @@ +Project Layout +============== + +Create a project directory and enter it: + +.. code-block:: none + + mkdir flask-tutorial + cd flask-tutorial + +Then follow the :doc:`installation instructions ` to set +up a Python virtual environment and install Flask for your project. + +The tutorial will assume you're working from the ``flask-tutorial`` +directory from now on. The file names at the top of each code block are +relative to this directory. + +---- + +A Flask application can be as simple as a single file. + +.. code-block:: python + :caption: ``hello.py`` + + from flask import Flask + + app = Flask(__name__) + + + @app.route('/') + def hello(): + return 'Hello, World!' + +However, as a project get bigger, it becomes overwhelming to keep all +the code in one file. Python projects use *packages* to organize code +into multiple modules that can be imported where needed, and the +tutorial will do this as well. + +The project directory will contain: + +* ``flaskr/``, a Python package containing your application code and + files. +* ``tests/``, a directory containing test modules. +* ``venv/``, a Python virtual environment where Flask and other + dependencies are installed. +* Installation files telling Python how to install your project. +* Version control config, such as `git`_. You should make a habit of + using some type of version control for all your projects, no matter + the size. +* Any other project files you might add in the future. + +.. _git: https://git-scm.com/ + +By the end, your project layout will look like this: + +.. code-block:: none + + /home/user/Projects/flask-tutorial + ├── flaskr/ + │   ├── __init__.py + │   ├── db.py + │   ├── schema.sql + │   ├── auth.py + │   ├── blog.py + │   ├── templates/ + │   │ ├── base.html + │   │ ├── auth/ + │   │ │   ├── login.html + │   │ │   └── register.html + │   │ └── blog/ + │   │ ├── create.html + │   │ ├── index.html + │   │ └── update.html + │   └── static/ + │      └── style.css + ├── tests/ + │   ├── conftest.py + │   ├── data.sql + │   ├── test_factory.py + │   ├── test_db.py + │  ├── test_auth.py + │  └── test_blog.py + ├── venv/ + ├── setup.py + └── MANIFEST.in + +If you're using version control, the following files that are generated +while running your project should be ignored. There may be other files +based on the editor you use. In general, ignore files that you didn't +write. For example, with git: + +.. code-block:: none + :caption: ``.gitignore`` + + venv/ + + *.pyc + __pycache__/ + + instance/ + + .pytest_cache/ + .coverage + htmlcov/ + + dist/ + build/ + *.egg-info/ + +Continue to :doc:`factory`. diff -Nru flask-0.12.2/docs/tutorial/next.rst flask-1.0.2/docs/tutorial/next.rst --- flask-0.12.2/docs/tutorial/next.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/next.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,38 @@ +Keep Developing! +================ + +You've learned about quite a few Flask and Python concepts throughout +the tutorial. Go back and review the tutorial and compare your code with +the steps you took to get there. Compare your project to the +:gh:`example project `, which might look a bit +different due to the step-by-step nature of the tutorial. + +There's a lot more to Flask than what you've seen so far. Even so, +you're now equipped to start developing your own web applications. Check +out the :ref:`quickstart` for an overview of what Flask can do, then +dive into the docs to keep learning. Flask uses `Jinja`_, `Click`_, +`Werkzeug`_, and `ItsDangerous`_ behind the scenes, and they all have +their own documentation too. You'll also be interested in +:ref:`extensions` which make tasks like working with the database or +validating form data easier and more powerful. + +If you want to keep developing your Flaskr project, here are some ideas +for what to try next: + +* A detail view to show a single post. Click a post's title to go to + its page. +* Like / unlike a post. +* Comments. +* Tags. Clicking a tag shows all the posts with that tag. +* A search box that filters the index page by name. +* Paged display. Only show 5 posts per page. +* Upload an image to go along with a post. +* Format posts using Markdown. +* An RSS feed of new posts. + +Have fun and make awesome applications! + +.. _Jinja: https://palletsprojects.com/p/jinja/ +.. _Click: https://palletsprojects.com/p/click/ +.. _Werkzeug: https://palletsprojects.com/p/werkzeug/ +.. _ItsDangerous: https://palletsprojects.com/p/itsdangerous/ diff -Nru flask-0.12.2/docs/tutorial/packaging.rst flask-1.0.2/docs/tutorial/packaging.rst --- flask-0.12.2/docs/tutorial/packaging.rst 2017-02-10 11:20:32.000000000 +0000 +++ flask-1.0.2/docs/tutorial/packaging.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,108 +0,0 @@ -.. _tutorial-packaging: - -Step 3: Installing flaskr as a Package -====================================== - -Flask is now shipped with built-in support for `Click`_. Click provides -Flask with enhanced and extensible command line utilities. Later in this -tutorial you will see exactly how to extend the ``flask`` command line -interface (CLI). - -A useful pattern to manage a Flask application is to install your app -following the `Python Packaging Guide`_. Presently this involves -creating two new files; :file:`setup.py` and :file:`MANIFEST.in` in the -projects root directory. You also need to add an :file:`__init__.py` -file to make the :file:`flaskr/flaskr` directory a package. After these -changes, your code structure should be:: - - /flaskr - /flaskr - __init__.py - /static - /templates - flaskr.py - schema.sql - setup.py - MANIFEST.in - -The content of the ``setup.py`` file for ``flaskr`` is: - -.. sourcecode:: python - - from setuptools import setup - - setup( - name='flaskr', - packages=['flaskr'], - include_package_data=True, - install_requires=[ - 'flask', - ], - ) - -When using setuptools, it is also necessary to specify any special files -that should be included in your package (in the :file:`MANIFEST.in`). -In this case, the static and templates directories need to be included, -as well as the schema. Create the :file:`MANIFEST.in` and add the -following lines:: - - graft flaskr/templates - graft flaskr/static - include flaskr/schema.sql - -To simplify locating the application, add the following import statement -into this file, :file:`flaskr/__init__.py`: - -.. sourcecode:: python - - from .flaskr import app - -This import statement brings the application instance into the top-level -of the application package. When it is time to run the application, the -Flask development server needs the location of the app instance. This -import statement simplifies the location process. Without it the export -statement a few steps below would need to be -``export FLASK_APP=flaskr.flaskr``. - -At this point you should be able to install the application. As usual, it -is recommended to install your Flask application within a `virtualenv`_. -With that said, go ahead and install the application with:: - - pip install --editable . - -The above installation command assumes that it is run within the projects -root directory, `flaskr/`. The `editable` flag allows editing -source code without having to reinstall the Flask app each time you make -changes. The flaskr app is now installed in your virtualenv (see output -of ``pip freeze``). - -With that out of the way, you should be able to start up the application. -Do this with the following commands:: - - export FLASK_APP=flaskr - export FLASK_DEBUG=true - flask run - -(In case you are on Windows you need to use `set` instead of `export`). -The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger. -*Never leave debug mode activated in a production system*, because it will -allow users to execute code on the server! - -You will see a message telling you that server has started along with -the address at which you can access it. - -When you head over to the server in your browser, you will get a 404 error -because we don't have any views yet. That will be addressed a little later, -but first, you should get the database working. - -.. admonition:: Externally Visible Server - - Want your server to be publicly available? Check out the - :ref:`externally visible server ` section for more - information. - -Continue with :ref:`tutorial-dbcon`. - -.. _Click: http://click.pocoo.org -.. _Python Packaging Guide: https://packaging.python.org -.. _virtualenv: https://virtualenv.pypa.io diff -Nru flask-0.12.2/docs/tutorial/schema.rst flask-1.0.2/docs/tutorial/schema.rst --- flask-0.12.2/docs/tutorial/schema.rst 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/docs/tutorial/schema.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -.. _tutorial-schema: - -Step 1: Database Schema -======================= - -In this step, you will create the database schema. Only a single table is -needed for this application and it will only support SQLite. All you need to do -is put the following contents into a file named :file:`schema.sql` in the -:file:`flaskr/flaskr` folder: - -.. sourcecode:: sql - - drop table if exists entries; - create table entries ( - id integer primary key autoincrement, - title text not null, - 'text' text not null - ); - -This schema consists of a single table called ``entries``. Each row in -this table has an ``id``, a ``title``, and a ``text``. The ``id`` is an -automatically incrementing integer and a primary key, the other two are -strings that must not be null. - -Continue with :ref:`tutorial-setup`. diff -Nru flask-0.12.2/docs/tutorial/setup.rst flask-1.0.2/docs/tutorial/setup.rst --- flask-0.12.2/docs/tutorial/setup.rst 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/docs/tutorial/setup.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -.. _tutorial-setup: - -Step 2: Application Setup Code -============================== - -Now that the schema is in place, you can create the application module, -:file:`flaskr.py`. This file should be placed inside of the -:file:`flaskr/flaskr` folder. The first several lines of code in the -application module are the needed import statements. After that there will be a -few lines of configuration code. For small applications like ``flaskr``, it is -possible to drop the configuration directly into the module. However, a cleaner -solution is to create a separate ``.ini`` or ``.py`` file, load that, and -import the values from there. - -Here are the import statements (in :file:`flaskr.py`):: - - # all the imports - import os - import sqlite3 - from flask import Flask, request, session, g, redirect, url_for, abort, \ - render_template, flash - -The next couple lines will create the actual application instance and -initialize it with the config from the same file in :file:`flaskr.py`: - -.. sourcecode:: python - - app = Flask(__name__) # create the application instance :) - app.config.from_object(__name__) # load config from this file , flaskr.py - - # Load default config and override config from an environment variable - app.config.update(dict( - DATABASE=os.path.join(app.root_path, 'flaskr.db'), - SECRET_KEY='development key', - USERNAME='admin', - PASSWORD='default' - )) - app.config.from_envvar('FLASKR_SETTINGS', silent=True) - -The :class:`~flask.Config` object works similarly to a dictionary, so it can be -updated with new values. - -.. admonition:: Database Path - - Operating systems know the concept of a current working directory for - each process. Unfortunately, you cannot depend on this in web - applications because you might have more than one application in the - same process. - - For this reason the ``app.root_path`` attribute can be used to - get the path to the application. Together with the ``os.path`` module, - files can then easily be found. In this example, we place the - database right next to it. - - For a real-world application, it's recommended to use - :ref:`instance-folders` instead. - -Usually, it is a good idea to load a separate, environment-specific -configuration file. Flask allows you to import multiple configurations and it -will use the setting defined in the last import. This enables robust -configuration setups. :meth:`~flask.Config.from_envvar` can help achieve this. - -.. sourcecode:: python - - app.config.from_envvar('FLASKR_SETTINGS', silent=True) - -Simply define the environment variable :envvar:`FLASKR_SETTINGS` that points to -a config file to be loaded. The silent switch just tells Flask to not complain -if no such environment key is set. - -In addition to that, you can use the :meth:`~flask.Config.from_object` -method on the config object and provide it with an import name of a -module. Flask will then initialize the variable from that module. Note -that in all cases, only variable names that are uppercase are considered. - -The ``SECRET_KEY`` is needed to keep the client-side sessions secure. -Choose that key wisely and as hard to guess and complex as possible. - -Lastly, you will add a method that allows for easy connections to the -specified database. This can be used to open a connection on request and -also from the interactive Python shell or a script. This will come in -handy later. You can create a simple database connection through SQLite and -then tell it to use the :class:`sqlite3.Row` object to represent rows. -This allows the rows to be treated as if they were dictionaries instead of -tuples. - -.. sourcecode:: python - - def connect_db(): - """Connects to the specific database.""" - rv = sqlite3.connect(app.config['DATABASE']) - rv.row_factory = sqlite3.Row - return rv - -In the next section you will see how to run the application. - -Continue with :ref:`tutorial-packaging`. diff -Nru flask-0.12.2/docs/tutorial/static.rst flask-1.0.2/docs/tutorial/static.rst --- flask-0.12.2/docs/tutorial/static.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/static.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,72 @@ +Static Files +============ + +The authentication views and templates work, but they look very plain +right now. Some `CSS`_ can be added to add style to the HTML layout you +constructed. The style won't change, so it's a *static* file rather than +a template. + +Flask automatically adds a ``static`` view that takes a path relative +to the ``flaskr/static`` directory and serves it. The ``base.html`` +template already has a link to the ``style.css`` file: + +.. code-block:: html+jinja + + {{ url_for('static', filename='style.css') }} + +Besides CSS, other types of static files might be files with JavaScript +functions, or a logo image. They are all placed under the +``flaskr/static`` directory and referenced with +``url_for('static', filename='...')``. + +This tutorial isn't focused on how to write CSS, so you can just copy +the following into the ``flaskr/static/style.css`` file: + +.. code-block:: css + :caption: ``flaskr/static/style.css`` + + html { font-family: sans-serif; background: #eee; padding: 1rem; } + body { max-width: 960px; margin: 0 auto; background: white; } + h1 { font-family: serif; color: #377ba8; margin: 1rem 0; } + a { color: #377ba8; } + hr { border: none; border-top: 1px solid lightgray; } + nav { background: lightgray; display: flex; align-items: center; padding: 0 0.5rem; } + nav h1 { flex: auto; margin: 0; } + nav h1 a { text-decoration: none; padding: 0.25rem 0.5rem; } + nav ul { display: flex; list-style: none; margin: 0; padding: 0; } + nav ul li a, nav ul li span, header .action { display: block; padding: 0.5rem; } + .content { padding: 0 1rem 1rem; } + .content > header { border-bottom: 1px solid lightgray; display: flex; align-items: flex-end; } + .content > header h1 { flex: auto; margin: 1rem 0 0.25rem 0; } + .flash { margin: 1em 0; padding: 1em; background: #cae6f6; border: 1px solid #377ba8; } + .post > header { display: flex; align-items: flex-end; font-size: 0.85em; } + .post > header > div:first-of-type { flex: auto; } + .post > header h1 { font-size: 1.5em; margin-bottom: 0; } + .post .about { color: slategray; font-style: italic; } + .post .body { white-space: pre-line; } + .content:last-child { margin-bottom: 0; } + .content form { margin: 1em 0; display: flex; flex-direction: column; } + .content label { font-weight: bold; margin-bottom: 0.5em; } + .content input, .content textarea { margin-bottom: 1em; } + .content textarea { min-height: 12em; resize: vertical; } + input.danger { color: #cc2f2e; } + input[type=submit] { align-self: start; min-width: 10em; } + +You can find a less compact version of ``style.css`` in the +:gh:`example code `. + +Go to http://127.0.0.1:5000/auth/login and the page should look like the +screenshot below. + +.. image:: flaskr_login.png + :align: center + :class: screenshot + :alt: screenshot of login page + +You can read more about CSS from `Mozilla's documentation `_. If +you change a static file, refresh the browser page. If the change +doesn't show up, try clearing your browser's cache. + +.. _CSS: https://developer.mozilla.org/docs/Web/CSS + +Continue to :doc:`blog`. diff -Nru flask-0.12.2/docs/tutorial/templates.rst flask-1.0.2/docs/tutorial/templates.rst --- flask-0.12.2/docs/tutorial/templates.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/tutorial/templates.rst 2018-05-02 00:50:32.000000000 +0000 @@ -1,112 +1,187 @@ -.. _tutorial-templates: +.. currentmodule:: flask -Step 7: The Templates -===================== +Templates +========= -Now it is time to start working on the templates. As you may have -noticed, if you make requests with the app running, you will get -an exception that Flask cannot find the templates. The templates -are using `Jinja2`_ syntax and have autoescaping enabled by -default. This means that unless you mark a value in the code with -:class:`~flask.Markup` or with the ``|safe`` filter in the template, -Jinja2 will ensure that special characters such as ``<`` or ``>`` are -escaped with their XML equivalents. - -We are also using template inheritance which makes it possible to reuse -the layout of the website in all pages. - -Put the following templates into the :file:`templates` folder: - -.. _Jinja2: http://jinja.pocoo.org/docs/templates - -layout.html ------------ - -This template contains the HTML skeleton, the header and a link to log in -(or log out if the user was already logged in). It also displays the -flashed messages if there are any. The ``{% block body %}`` block can be -replaced by a block of the same name (``body``) in a child template. - -The :class:`~flask.session` dict is available in the template as well and -you can use that to check if the user is logged in or not. Note that in -Jinja you can access missing attributes and items of objects / dicts which -makes the following code work, even if there is no ``'logged_in'`` key in -the session: +You've written the authentication views for your application, but if +you're running the server and try to go to any of the URLs, you'll see a +``TemplateNotFound`` error. That's because the views are calling +:func:`render_template`, but you haven't written the templates yet. +The template files will be stored in the ``templates`` directory inside +the ``flaskr`` package. + +Templates are files that contain static data as well as placeholders +for dynamic data. A template is rendered with specific data to produce a +final document. Flask uses the `Jinja`_ template library to render +templates. + +In your application, you will use templates to render `HTML`_ which +will display in the user's browser. In Flask, Jinja is configured to +*autoescape* any data that is rendered in HTML templates. This means +that it's safe to render user input; any characters they've entered that +could mess with the HTML, such as ``<`` and ``>`` will be *escaped* with +*safe* values that look the same in the browser but don't cause unwanted +effects. + +Jinja looks and behaves mostly like Python. Special delimiters are used +to distinguish Jinja syntax from the static data in the template. +Anything between ``{{`` and ``}}`` is an expression that will be output +to the final document. ``{%`` and ``%}`` denotes a control flow +statement like ``if`` and ``for``. Unlike Python, blocks are denoted +by start and end tags rather than indentation since static text within +a block could change indentation. + +.. _Jinja: http://jinja.pocoo.org/docs/templates/ +.. _HTML: https://developer.mozilla.org/docs/Web/HTML + + +The Base Layout +--------------- + +Each page in the application will have the same basic layout around a +different body. Instead of writing the entire HTML structure in each +template, each template will *extend* a base template and override +specific sections. -.. sourcecode:: html+jinja +.. code-block:: html+jinja + :caption: ``flaskr/templates/base.html`` - Flaskr - -
+ {% block title %}{% endblock %} - Flaskr + + +
+
+ {% block header %}{% endblock %} +
{% for message in get_flashed_messages() %} -
{{ message }}
+
{{ message }}
{% endfor %} - {% block body %}{% endblock %} -
+ {% block content %}{% endblock %} + -show_entries.html ------------------ +:data:`g` is automatically available in templates. Based on if +``g.user`` is set (from ``load_logged_in_user``), either the username +and a log out link are displayed, otherwise links to register and log in +are displayed. :func:`url_for` is also automatically available, and is +used to generate URLs to views instead of writing them out manually. -This template extends the :file:`layout.html` template from above to display the -messages. Note that the ``for`` loop iterates over the messages we passed -in with the :func:`~flask.render_template` function. Notice that the form is -configured to to submit to the `add_entry` view function and use ``POST`` as -HTTP method: - -.. sourcecode:: html+jinja - - {% extends "layout.html" %} - {% block body %} - {% if session.logged_in %} -
-
-
Title: -
-
Text: -
-
-
-
- {% endif %} -
    - {% for entry in entries %} -
  • {{ entry.title }}

    {{ entry.text|safe }} - {% else %} -
  • Unbelievable. No entries here so far - {% endfor %} -
+After the page title, and before the content, the template loops over +each message returned by :func:`get_flashed_messages`. You used +:func:`flash` in the views to show error messages, and this is the code +that will display them. + +There are three blocks defined here that will be overridden in the other +templates: + +#. ``{% block title %}`` will change the title displayed in the + browser's tab and window title. + +#. ``{% block header %}`` is similar to ``title`` but will change the + title displayed on the page. + +#. ``{% block content %}`` is where the content of each page goes, such + as the login form or a blog post. + +The base template is directly in the ``templates`` directory. To keep +the others organized, the templates for a blueprint will be placed in a +directory with the same name as the blueprint. + + +Register +-------- + +.. code-block:: html+jinja + :caption: ``flaskr/templates/auth/register.html`` + + {% extends 'base.html' %} + + {% block header %} +

{% block title %}Register{% endblock %}

+ {% endblock %} + + {% block content %} +
+ + + + + +
{% endblock %} -login.html ----------- +``{% extends 'base.html' %}`` tells Jinja that this template should +replace the blocks from the base template. All the rendered content must +appear inside ``{% block %}`` tags that override blocks from the base +template. + +A useful pattern used here is to place ``{% block title %}`` inside +``{% block header %}``. This will set the title block and then output +the value of it into the header block, so that both the window and page +share the same title without writing it twice. + +The ``input`` tags are using the ``required`` attribute here. This tells +the browser not to submit the form until those fields are filled in. If +the user is using an older browser that doesn't support that attribute, +or if they are using something besides a browser to make requests, you +still want to validate the data in the Flask view. It's important to +always fully validate the data on the server, even if the client does +some validation as well. + + +Log In +------ + +This is identical to the register template except for the title and +submit button. -This is the login template, which basically just displays a form to allow -the user to login: +.. code-block:: html+jinja + :caption: ``flaskr/templates/auth/login.html`` -.. sourcecode:: html+jinja + {% extends 'base.html' %} - {% extends "layout.html" %} - {% block body %} -

Login

- {% if error %}

Error: {{ error }}{% endif %} -

-
-
Username: -
-
Password: -
-
-
+ {% block header %} +

{% block title %}Log In{% endblock %}

+ {% endblock %} + + {% block content %} + + + + + +
{% endblock %} -Continue with :ref:`tutorial-css`. + +Register A User +--------------- + +Now that the authentication templates are written, you can register a +user. Make sure the server is still running (``flask run`` if it's not), +then go to http://127.0.0.1:5000/auth/register. + +Try clicking the "Register" button without filling out the form and see +that the browser shows an error message. Try removing the ``required`` +attributes from the ``register.html`` template and click "Register" +again. Instead of the browser showing an error, the page will reload and +the error from :func:`flash` in the view will be shown. + +Fill out a username and password and you'll be redirected to the login +page. Try entering an incorrect username, or the correct username and +incorrect password. If you log in you'll get an error because there's +no ``index`` view to redirect to yet. + +Continue to :doc:`static`. diff -Nru flask-0.12.2/docs/tutorial/testing.rst flask-1.0.2/docs/tutorial/testing.rst --- flask-0.12.2/docs/tutorial/testing.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/tutorial/testing.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -.. _tutorial-testing: - -Bonus: Testing the Application -============================== - -Now that you have finished the application and everything works as -expected, it's probably not a bad idea to add automated tests to simplify -modifications in the future. The application above is used as a basic -example of how to perform unit testing in the :ref:`testing` section of the -documentation. Go there to see how easy it is to test Flask applications. - -Adding tests to flaskr ----------------------- - -Assuming you have seen the :ref:`testing` section and have either written -your own tests for ``flaskr`` or have followed along with the examples -provided, you might be wondering about ways to organize the project. - -One possible and recommended project structure is:: - - flaskr/ - flaskr/ - __init__.py - static/ - templates/ - tests/ - test_flaskr.py - setup.py - MANIFEST.in - -For now go ahead a create the :file:`tests/` directory as well as the -:file:`test_flaskr.py` file. - -Running the tests ------------------ - -At this point you can run the tests. Here ``pytest`` will be used. - -.. note:: Make sure that ``pytest`` is installed in the same virtualenv - as flaskr. Otherwise ``pytest`` test will not be able to import the - required components to test the application:: - - pip install -e . - pip install pytest - -Run and watch the tests pass, within the top-level :file:`flaskr/` -directory as:: - - py.test - -Testing + setuptools --------------------- - -One way to handle testing is to integrate it with ``setuptools``. Here -that requires adding a couple of lines to the :file:`setup.py` file and -creating a new file :file:`setup.cfg`. One benefit of running the tests -this way is that you do not have to install ``pytest``. Go ahead and -update the :file:`setup.py` file to contain:: - - from setuptools import setup - - setup( - name='flaskr', - packages=['flaskr'], - include_package_data=True, - install_requires=[ - 'flask', - ], - setup_requires=[ - 'pytest-runner', - ], - tests_require=[ - 'pytest', - ], - ) - -Now create :file:`setup.cfg` in the project root (alongside -:file:`setup.py`):: - - [aliases] - test=pytest - -Now you can run:: - - python setup.py test - -This calls on the alias created in :file:`setup.cfg` which in turn runs -``pytest`` via ``pytest-runner``, as the :file:`setup.py` script has -been called. (Recall the `setup_requires` argument in :file:`setup.py`) -Following the standard rules of test-discovery your tests will be -found, run, and hopefully pass. - -This is one possible way to run and manage testing. Here ``pytest`` is -used, but there are other options such as ``nose``. Integrating testing -with ``setuptools`` is convenient because it is not necessary to actually -download ``pytest`` or any other testing framework one might use. diff -Nru flask-0.12.2/docs/tutorial/tests.rst flask-1.0.2/docs/tutorial/tests.rst --- flask-0.12.2/docs/tutorial/tests.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/docs/tutorial/tests.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,561 @@ +.. currentmodule:: flask + +Test Coverage +============= + +Writing unit tests for your application lets you check that the code +you wrote works the way you expect. Flask provides a test client that +simulates requests to the application and returns the response data. + +You should test as much of your code as possible. Code in functions only +runs when the function is called, and code in branches, such as ``if`` +blocks, only runs when the condition is met. You want to make sure that +each function is tested with data that covers each branch. + +The closer you get to 100% coverage, the more comfortable you can be +that making a change won't unexpectedly change other behavior. However, +100% coverage doesn't guarantee that your application doesn't have bugs. +In particular, it doesn't test how the user interacts with the +application in the browser. Despite this, test coverage is an important +tool to use during development. + +.. note:: + This is being introduced late in the tutorial, but in your future + projects you should test as you develop. + +You'll use `pytest`_ and `coverage`_ to test and measure your code. +Install them both: + +.. code-block:: none + + pip install pytest coverage + +.. _pytest: https://pytest.readthedocs.io/ +.. _coverage: https://coverage.readthedocs.io/ + + +Setup and Fixtures +------------------ + +The test code is located in the ``tests`` directory. This directory is +*next to* the ``flaskr`` package, not inside it. The +``tests/conftest.py`` file contains setup functions called *fixtures* +that each test will use. Tests are in Python modules that start with +``test_``, and each test function in those modules also starts with +``test_``. + +Each test will create a new temporary database file and populate some +data that will be used in the tests. Write a SQL file to insert that +data. + +.. code-block:: sql + :caption: ``tests/data.sql`` + + INSERT INTO user (username, password) + VALUES + ('test', 'pbkdf2:sha256:50000$TCI4GzcX$0de171a4f4dac32e3364c7ddc7c14f3e2fa61f2d17574483f7ffbb431b4acb2f'), + ('other', 'pbkdf2:sha256:50000$kJPKsz6N$d2d4784f1b030a9761f5ccaeeaca413f27f2ecb76d6168407af962ddce849f79'); + + INSERT INTO post (title, body, author_id, created) + VALUES + ('test title', 'test' || x'0a' || 'body', 1, '2018-01-01 00:00:00'); + +The ``app`` fixture will call the factory and pass ``test_config`` to +configure the application and database for testing instead of using your +local development configuration. + +.. code-block:: python + :caption: ``tests/conftest.py`` + + import os + import tempfile + + import pytest + from flaskr import create_app + from flaskr.db import get_db, init_db + + with open(os.path.join(os.path.dirname(__file__), 'data.sql'), 'rb') as f: + _data_sql = f.read().decode('utf8') + + + @pytest.fixture + def app(): + db_fd, db_path = tempfile.mkstemp() + + app = create_app({ + 'TESTING': True, + 'DATABASE': db_path, + }) + + with app.app_context(): + init_db() + get_db().executescript(_data_sql) + + yield app + + os.close(db_fd) + os.unlink(db_path) + + + @pytest.fixture + def client(app): + return app.test_client() + + + @pytest.fixture + def runner(app): + return app.test_cli_runner() + +:func:`tempfile.mkstemp` creates and opens a temporary file, returning +the file object and the path to it. The ``DATABASE`` path is +overridden so it points to this temporary path instead of the instance +folder. After setting the path, the database tables are created and the +test data is inserted. After the test is over, the temporary file is +closed and removed. + +:data:`TESTING` tells Flask that the app is in test mode. Flask changes +some internal behavior so it's easier to test, and other extensions can +also use the flag to make testing them easier. + +The ``client`` fixture calls +:meth:`app.test_client() ` with the application +object created by the ``app`` fixture. Tests will use the client to make +requests to the application without running the server. + +The ``runner`` fixture is similar to ``client``. +:meth:`app.test_cli_runner() ` creates a runner +that can call the Click commands registered with the application. + +Pytest uses fixtures by matching their function names with the names +of arguments in the test functions. For example, the ``test_hello`` +function you'll write next takes a ``client`` argument. Pytest matches +that with the ``client`` fixture function, calls it, and passes the +returned value to the test function. + + +Factory +------- + +There's not much to test about the factory itself. Most of the code will +be executed for each test already, so if something fails the other tests +will notice. + +The only behavior that can change is passing test config. If config is +not passed, there should be some default configuration, otherwise the +configuration should be overridden. + +.. code-block:: python + :caption: ``tests/test_factory.py`` + + from flaskr import create_app + + + def test_config(): + assert not create_app().testing + assert create_app({'TESTING': True}).testing + + + def test_hello(client): + response = client.get('/hello') + assert response.data == b'Hello, World!' + +You added the ``hello`` route as an example when writing the factory at +the beginning of the tutorial. It returns "Hello, World!", so the test +checks that the response data matches. + + +Database +-------- + +Within an application context, ``get_db`` should return the same +connection each time it's called. After the context, the connection +should be closed. + +.. code-block:: python + :caption: ``tests/test_db.py`` + + import sqlite3 + + import pytest + from flaskr.db import get_db + + + def test_get_close_db(app): + with app.app_context(): + db = get_db() + assert db is get_db() + + with pytest.raises(sqlite3.ProgrammingError) as e: + db.execute('SELECT 1') + + assert 'closed' in str(e) + +The ``init-db`` command should call the ``init_db`` function and output +a message. + +.. code-block:: python + :caption: ``tests/test_db.py`` + + def test_init_db_command(runner, monkeypatch): + class Recorder(object): + called = False + + def fake_init_db(): + Recorder.called = True + + monkeypatch.setattr('flaskr.db.init_db', fake_init_db) + result = runner.invoke(args=['init-db']) + assert 'Initialized' in result.output + assert Recorder.called + +This test uses Pytest's ``monkeypatch`` fixture to replace the +``init_db`` function with one that records that it's been called. The +``runner`` fixture you wrote above is used to call the ``init-db`` +command by name. + + +Authentication +-------------- + +For most of the views, a user needs to be logged in. The easiest way to +do this in tests is to make a ``POST`` request to the ``login`` view +with the client. Rather than writing that out every time, you can write +a class with methods to do that, and use a fixture to pass it the client +for each test. + +.. code-block:: python + :caption: ``tests/conftest.py`` + + class AuthActions(object): + def __init__(self, client): + self._client = client + + def login(self, username='test', password='test'): + return self._client.post( + '/auth/login', + data={'username': username, 'password': password} + ) + + def logout(self): + return self._client.get('/auth/logout') + + + @pytest.fixture + def auth(client): + return AuthActions(client) + +With the ``auth`` fixture, you can call ``auth.login()`` in a test to +log in as the ``test`` user, which was inserted as part of the test +data in the ``app`` fixture. + +The ``register`` view should render successfully on ``GET``. On ``POST`` +with valid form data, it should redirect to the login URL and the user's +data should be in the database. Invalid data should display error +messages. + +.. code-block:: python + :caption: ``tests/test_auth.py`` + + import pytest + from flask import g, session + from flaskr.db import get_db + + + def test_register(client, app): + assert client.get('/auth/register').status_code == 200 + response = client.post( + '/auth/register', data={'username': 'a', 'password': 'a'} + ) + assert 'http://localhost/auth/login' == response.headers['Location'] + + with app.app_context(): + assert get_db().execute( + "select * from user where username = 'a'", + ).fetchone() is not None + + + @pytest.mark.parametrize(('username', 'password', 'message'), ( + ('', '', b'Username is required.'), + ('a', '', b'Password is required.'), + ('test', 'test', b'already registered'), + )) + def test_register_validate_input(client, username, password, message): + response = client.post( + '/auth/register', + data={'username': username, 'password': password} + ) + assert message in response.data + +:meth:`client.get() ` makes a ``GET`` request +and returns the :class:`Response` object returned by Flask. Similarly, +:meth:`client.post() ` makes a ``POST`` +request, converting the ``data`` dict into form data. + +To test that the page renders successfully, a simple request is made and +checked for a ``200 OK`` :attr:`~Response.status_code`. If +rendering failed, Flask would return a ``500 Internal Server Error`` +code. + +:attr:`~Response.headers` will have a ``Location`` header with the login +URL when the register view redirects to the login view. + +:attr:`~Response.data` contains the body of the response as bytes. If +you expect a certain value to render on the page, check that it's in +``data``. Bytes must be compared to bytes. If you want to compare +Unicode text, use :meth:`get_data(as_text=True) ` +instead. + +``pytest.mark.parametrize`` tells Pytest to run the same test function +with different arguments. You use it here to test different invalid +input and error messages without writing the same code three times. + +The tests for the ``login`` view are very similar to those for +``register``. Rather than testing the data in the database, +:data:`session` should have ``user_id`` set after logging in. + +.. code-block:: python + :caption: ``tests/test_auth.py`` + + def test_login(client, auth): + assert client.get('/auth/login').status_code == 200 + response = auth.login() + assert response.headers['Location'] == 'http://localhost/' + + with client: + client.get('/') + assert session['user_id'] == 1 + assert g.user['username'] == 'test' + + + @pytest.mark.parametrize(('username', 'password', 'message'), ( + ('a', 'test', b'Incorrect username.'), + ('test', 'a', b'Incorrect password.'), + )) + def test_login_validate_input(auth, username, password, message): + response = auth.login(username, password) + assert message in response.data + +Using ``client`` in a ``with`` block allows accessing context variables +such as :data:`session` after the response is returned. Normally, +accessing ``session`` outside of a request would raise an error. + +Testing ``logout`` is the opposite of ``login``. :data:`session` should +not contain ``user_id`` after logging out. + +.. code-block:: python + :caption: ``tests/test_auth.py`` + + def test_logout(client, auth): + auth.login() + + with client: + auth.logout() + assert 'user_id' not in session + + +Blog +---- + +All the blog views use the ``auth`` fixture you wrote earlier. Call +``auth.login()`` and subsequent requests from the client will be logged +in as the ``test`` user. + +The ``index`` view should display information about the post that was +added with the test data. When logged in as the author, there should be +a link to edit the post. + +You can also test some more authentication behavior while testing the +``index`` view. When not logged in, each page shows links to log in or +register. When logged in, there's a link to log out. + +.. code-block:: python + :caption: ``tests/test_blog.py`` + + import pytest + from flaskr.db import get_db + + + def test_index(client, auth): + response = client.get('/') + assert b"Log In" in response.data + assert b"Register" in response.data + + auth.login() + response = client.get('/') + assert b'Log Out' in response.data + assert b'test title' in response.data + assert b'by test on 2018-01-01' in response.data + assert b'test\nbody' in response.data + assert b'href="/1/update"' in response.data + +A user must be logged in to access the ``create``, ``update``, and +``delete`` views. The logged in user must be the author of the post to +access ``update`` and ``delete``, otherwise a ``403 Forbidden`` status +is returned. If a ``post`` with the given ``id`` doesn't exist, +``update`` and ``delete`` should return ``404 Not Found``. + +.. code-block:: python + :caption: ``tests/test_blog.py`` + + @pytest.mark.parametrize('path', ( + '/create', + '/1/update', + '/1/delete', + )) + def test_login_required(client, path): + response = client.post(path) + assert response.headers['Location'] == 'http://localhost/auth/login' + + + def test_author_required(app, client, auth): + # change the post author to another user + with app.app_context(): + db = get_db() + db.execute('UPDATE post SET author_id = 2 WHERE id = 1') + db.commit() + + auth.login() + # current user can't modify other user's post + assert client.post('/1/update').status_code == 403 + assert client.post('/1/delete').status_code == 403 + # current user doesn't see edit link + assert b'href="/1/update"' not in client.get('/').data + + + @pytest.mark.parametrize('path', ( + '/2/update', + '/2/delete', + )) + def test_exists_required(client, auth, path): + auth.login() + assert client.post(path).status_code == 404 + +The ``create`` and ``update`` views should render and return a +``200 OK`` status for a ``GET`` request. When valid data is sent in a +``POST`` request, ``create`` should insert the new post data into the +database, and ``update`` should modify the existing data. Both pages +should show an error message on invalid data. + +.. code-block:: python + :caption: ``tests/test_blog.py`` + + def test_create(client, auth, app): + auth.login() + assert client.get('/create').status_code == 200 + client.post('/create', data={'title': 'created', 'body': ''}) + + with app.app_context(): + db = get_db() + count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0] + assert count == 2 + + + def test_update(client, auth, app): + auth.login() + assert client.get('/1/update').status_code == 200 + client.post('/1/update', data={'title': 'updated', 'body': ''}) + + with app.app_context(): + db = get_db() + post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() + assert post['title'] == 'updated' + + + @pytest.mark.parametrize('path', ( + '/create', + '/1/update', + )) + def test_create_update_validate(client, auth, path): + auth.login() + response = client.post(path, data={'title': '', 'body': ''}) + assert b'Title is required.' in response.data + +The ``delete`` view should redirect to the index URL and the post should +no longer exist in the database. + +.. code-block:: python + :caption: ``tests/test_blog.py`` + + def test_delete(client, auth, app): + auth.login() + response = client.post('/1/delete') + assert response.headers['Location'] == 'http://localhost/' + + with app.app_context(): + db = get_db() + post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() + assert post is None + + +Running the Tests +----------------- + +Some extra configuration, which is not required but makes running +tests with coverage less verbose, can be added to the project's +``setup.cfg`` file. + +.. code-block:: none + :caption: ``setup.cfg`` + + [tool:pytest] + testpaths = tests + + [coverage:run] + branch = True + source = + flaskr + +To run the tests, use the ``pytest`` command. It will find and run all +the test functions you've written. + +.. code-block:: none + + pytest + + ========================= test session starts ========================== + platform linux -- Python 3.6.4, pytest-3.5.0, py-1.5.3, pluggy-0.6.0 + rootdir: /home/user/Projects/flask-tutorial, inifile: setup.cfg + collected 23 items + + tests/test_auth.py ........ [ 34%] + tests/test_blog.py ............ [ 86%] + tests/test_db.py .. [ 95%] + tests/test_factory.py .. [100%] + + ====================== 24 passed in 0.64 seconds ======================= + +If any tests fail, pytest will show the error that was raised. You can +run ``pytest -v`` to get a list of each test function rather than dots. + +To measure the code coverage of your tests, use the ``coverage`` command +to run pytest instead of running it directly. + +.. code-block:: none + + coverage run -m pytest + +You can either view a simple coverage report in the terminal: + +.. code-block:: none + + coverage report + + Name Stmts Miss Branch BrPart Cover + ------------------------------------------------------ + flaskr/__init__.py 21 0 2 0 100% + flaskr/auth.py 54 0 22 0 100% + flaskr/blog.py 54 0 16 0 100% + flaskr/db.py 24 0 4 0 100% + ------------------------------------------------------ + TOTAL 153 0 44 0 100% + +An HTML report allows you to see which lines were covered in each file: + +.. code-block:: none + + coverage html + +This generates files in the ``htmlcov`` directory. Open +``htmlcov/index.html`` in your browser to see the report. + +Continue to :doc:`deploy`. diff -Nru flask-0.12.2/docs/tutorial/views.rst flask-1.0.2/docs/tutorial/views.rst --- flask-0.12.2/docs/tutorial/views.rst 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/docs/tutorial/views.rst 2018-05-02 00:50:32.000000000 +0000 @@ -1,117 +1,301 @@ -.. _tutorial-views: +.. currentmodule:: flask -Step 6: The View Functions -========================== +Blueprints and Views +==================== -Now that the database connections are working, you can start writing the -view functions. You will need four of them: +A view function is the code you write to respond to requests to your +application. Flask uses patterns to match the incoming request URL to +the view that should handle it. The view returns data that Flask turns +into an outgoing response. Flask can also go the other direction and +generate a URL to a view based on its name and arguments. -Show Entries ------------- - -This view shows all the entries stored in the database. It listens on the -root of the application and will select title and text from the database. -The one with the highest id (the newest entry) will be on top. The rows -returned from the cursor look a bit like dictionaries because we are using -the :class:`sqlite3.Row` row factory. - -The view function will pass the entries to the :file:`show_entries.html` -template and return the rendered one:: - - @app.route('/') - def show_entries(): - db = get_db() - cur = db.execute('select title, text from entries order by id desc') - entries = cur.fetchall() - return render_template('show_entries.html', entries=entries) - -Add New Entry -------------- - -This view lets the user add new entries if they are logged in. This only -responds to ``POST`` requests; the actual form is shown on the -`show_entries` page. If everything worked out well, it will -:func:`~flask.flash` an information message to the next request and -redirect back to the `show_entries` page:: - - @app.route('/add', methods=['POST']) - def add_entry(): - if not session.get('logged_in'): - abort(401) - db = get_db() - db.execute('insert into entries (title, text) values (?, ?)', - [request.form['title'], request.form['text']]) - db.commit() - flash('New entry was successfully posted') - return redirect(url_for('show_entries')) - -Note that this view checks that the user is logged in (that is, if the -`logged_in` key is present in the session and ``True``). - -.. admonition:: Security Note - - Be sure to use question marks when building SQL statements, as done in the - example above. Otherwise, your app will be vulnerable to SQL injection when - you use string formatting to build SQL statements. - See :ref:`sqlite3` for more. - -Login and Logout ----------------- - -These functions are used to sign the user in and out. Login checks the -username and password against the ones from the configuration and sets the -`logged_in` key for the session. If the user logged in successfully, that -key is set to ``True``, and the user is redirected back to the `show_entries` -page. In addition, a message is flashed that informs the user that he or -she was logged in successfully. If an error occurred, the template is -notified about that, and the user is asked again:: - @app.route('/login', methods=['GET', 'POST']) +Create a Blueprint +------------------ + +A :class:`Blueprint` is a way to organize a group of related views and +other code. Rather than registering views and other code directly with +an application, they are registered with a blueprint. Then the blueprint +is registered with the application when it is available in the factory +function. + +Flaskr will have two blueprints, one for authentication functions and +one for the blog posts functions. The code for each blueprint will go +in a separate module. Since the blog needs to know about authentication, +you'll write the authentication one first. + +.. code-block:: python + :caption: ``flaskr/auth.py`` + + import functools + + from flask import ( + Blueprint, flash, g, redirect, render_template, request, session, url_for + ) + from werkzeug.security import check_password_hash, generate_password_hash + + from flaskr.db import get_db + + bp = Blueprint('auth', __name__, url_prefix='/auth') + +This creates a :class:`Blueprint` named ``'auth'``. Like the application +object, the blueprint needs to know where it's defined, so ``__name__`` +is passed as the second argument. The ``url_prefix`` will be prepended +to all the URLs associated with the blueprint. + +Import and register the blueprint from the factory using +:meth:`app.register_blueprint() `. Place the +new code at the end of the factory function before returning the app. + +.. code-block:: python + :caption: ``flaskr/__init__.py`` + + def create_app(): + app = ... + # existing code omitted + + from . import auth + app.register_blueprint(auth.bp) + + return app + +The authentication blueprint will have views to register new users and +to log in and log out. + + +The First View: Register +------------------------ + +When the user visits the ``/auth/register`` URL, the ``register`` view +will return `HTML`_ with a form for them to fill out. When they submit +the form, it will validate their input and either show the form again +with an error message or create the new user and go to the login page. + +.. _HTML: https://developer.mozilla.org/docs/Web/HTML + +For now you will just write the view code. On the next page, you'll +write templates to generate the HTML form. + +.. code-block:: python + :caption: ``flaskr/auth.py`` + + @bp.route('/register', methods=('GET', 'POST')) + def register(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + db = get_db() + error = None + + if not username: + error = 'Username is required.' + elif not password: + error = 'Password is required.' + elif db.execute( + 'SELECT id FROM user WHERE username = ?', (username,) + ).fetchone() is not None: + error = 'User {} is already registered.'.format(username) + + if error is None: + db.execute( + 'INSERT INTO user (username, password) VALUES (?, ?)', + (username, generate_password_hash(password)) + ) + db.commit() + return redirect(url_for('auth.login')) + + flash(error) + + return render_template('auth/register.html') + +Here's what the ``register`` view function is doing: + +#. :meth:`@bp.route ` associates the URL ``/register`` + with the ``register`` view function. When Flask receives a request + to ``/auth/register``, it will call the ``register`` view and use + the return value as the response. + +#. If the user submitted the form, + :attr:`request.method ` will be ``'POST'``. In this + case, start validating the input. + +#. :attr:`request.form ` is a special type of + :class:`dict` mapping submitted form keys and values. The user will + input their ``username`` and ``password``. + +#. Validate that ``username`` and ``password`` are not empty. + +#. Validate that ``username`` is not already registered by querying the + database and checking if a result is returned. + :meth:`db.execute ` takes a SQL query + with ``?`` placeholders for any user input, and a tuple of values + to replace the placeholders with. The database library will take + care of escaping the values so you are not vulnerable to a + *SQL injection attack*. + + :meth:`~sqlite3.Cursor.fetchone` returns one row from the query. + If the query returned no results, it returns ``None``. Later, + :meth:`~sqlite3.Cursor.fetchall` is used, which returns a list of + all results. + +#. If validation succeeds, insert the new user data into the database. + For security, passwords should never be stored in the database + directly. Instead, + :func:`~werkzeug.security.generate_password_hash` is used to + securely hash the password, and that hash is stored. Since this + query modifies data, :meth:`db.commit() ` + needs to be called afterwards to save the changes. + +#. After storing the user, they are redirected to the login page. + :func:`url_for` generates the URL for the login view based on its + name. This is preferable to writing the URL directly as it allows + you to change the URL later without changing all code that links to + it. :func:`redirect` generates a redirect response to the generated + URL. + +#. If validation fails, the error is shown to the user. :func:`flash` + stores messages that can be retrieved when rendering the template. + +#. When the user initially navigates to ``auth/register``, or + there was an validation error, an HTML page with the registration + form should be shown. :func:`render_template` will render a template + containing the HTML, which you'll write in the next step of the + tutorial. + + +Login +----- + +This view follows the same pattern as the ``register`` view above. + +.. code-block:: python + :caption: ``flaskr/auth.py`` + + @bp.route('/login', methods=('GET', 'POST')) def login(): - error = None if request.method == 'POST': - if request.form['username'] != app.config['USERNAME']: - error = 'Invalid username' - elif request.form['password'] != app.config['PASSWORD']: - error = 'Invalid password' - else: - session['logged_in'] = True - flash('You were logged in') - return redirect(url_for('show_entries')) - return render_template('login.html', error=error) - -The `logout` function, on the other hand, removes that key from the session -again. There is a neat trick here: if you use the :meth:`~dict.pop` method -of the dict and pass a second parameter to it (the default), the method -will delete the key from the dictionary if present or do nothing when that -key is not in there. This is helpful because now it is not necessary to -check if the user was logged in. + username = request.form['username'] + password = request.form['password'] + db = get_db() + error = None + user = db.execute( + 'SELECT * FROM user WHERE username = ?', (username,) + ).fetchone() + + if user is None: + error = 'Incorrect username.' + elif not check_password_hash(user['password'], password): + error = 'Incorrect password.' + + if error is None: + session.clear() + session['user_id'] = user['id'] + return redirect(url_for('index')) + + flash(error) + + return render_template('auth/login.html') + +There are a few differences from the ``register`` view: + +#. The user is queried first and stored in a variable for later use. + +#. :func:`~werkzeug.security.check_password_hash` hashes the submitted + password in the same way as the stored hash and securely compares + them. If they match, the password is valid. + +#. :data:`session` is a :class:`dict` that stores data across requests. + When validation succeeds, the user's ``id`` is stored in a new + session. The data is stored in a *cookie* that is sent to the + browser, and the browser then sends it back with subsequent requests. + Flask securely *signs* the data so that it can't be tampered with. + +Now that the user's ``id`` is stored in the :data:`session`, it will be +available on subsequent requests. At the beginning of each request, if +a user is logged in their information should be loaded and made +available to other views. + +.. code-block:: python + :caption: ``flaskr/auth.py`` + + @bp.before_app_request + def load_logged_in_user(): + user_id = session.get('user_id') + + if user_id is None: + g.user = None + else: + g.user = get_db().execute( + 'SELECT * FROM user WHERE id = ?', (user_id,) + ).fetchone() + +:meth:`bp.before_app_request() ` registers +a function that runs before the view function, no matter what URL is +requested. ``load_logged_in_user`` checks if a user id is stored in the +:data:`session` and gets that user's data from the database, storing it +on :data:`g.user `, which lasts for the length of the request. If +there is no user id, or if the id doesn't exist, ``g.user`` will be +``None``. + + +Logout +------ + +To log out, you need to remove the user id from the :data:`session`. +Then ``load_logged_in_user`` won't load a user on subsequent requests. -:: +.. code-block:: python + :caption: ``flaskr/auth.py`` - @app.route('/logout') + @bp.route('/logout') def logout(): - session.pop('logged_in', None) - flash('You were logged out') - return redirect(url_for('show_entries')) + session.clear() + return redirect(url_for('index')) + + +Require Authentication in Other Views +------------------------------------- + +Creating, editing, and deleting blog posts will require a user to be +logged in. A *decorator* can be used to check this for each view it's +applied to. + +.. code-block:: python + :caption: ``flaskr/auth.py`` + + def login_required(view): + @functools.wraps(view) + def wrapped_view(**kwargs): + if g.user is None: + return redirect(url_for('auth.login')) + + return view(**kwargs) -.. admonition:: Security Note + return wrapped_view - Passwords should never be stored in plain text in a production - system. This tutorial uses plain text passwords for simplicity. If you - plan to release a project based off this tutorial out into the world, - passwords should be both `hashed and salted`_ before being stored in a - database or file. +This decorator returns a new view function that wraps the original view +it's applied to. The new function checks if a user is loaded and +redirects to the login page otherwise. If a user is loaded the original +view is called and continues normally. You'll use this decorator when +writing the blog views. - Fortunately, there are Flask extensions for the purpose of - hashing passwords and verifying passwords against hashes, so adding - this functionality is fairly straight forward. There are also - many general python libraries that can be used for hashing. +Endpoints and URLs +------------------ - You can find a list of recommended Flask extensions - `here `_ +The :func:`url_for` function generates the URL to a view based on a name +and arguments. The name associated with a view is also called the +*endpoint*, and by default it's the same as the name of the view +function. +For example, the ``hello()`` view that was added to the app +factory earlier in the tutorial has the name ``'hello'`` and can be +linked to with ``url_for('hello')``. If it took an argument, which +you'll see later, it would be linked to using +``url_for('hello', who='World')``. -Continue with :ref:`tutorial-templates`. +When using a blueprint, the name of the blueprint is prepended to the +name of the function, so the endpoint for the ``login`` function you +wrote above is ``'auth.login'`` because you added it to the ``'auth'`` +blueprint. -.. _hashed and salted: https://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/ +Continue to :doc:`templates`. diff -Nru flask-0.12.2/docs/upgrading.rst flask-1.0.2/docs/upgrading.rst --- flask-0.12.2/docs/upgrading.rst 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/docs/upgrading.rst 2018-05-02 00:50:32.000000000 +0000 @@ -49,7 +49,7 @@ response = send_file(open(fname), attachment_filename=fname) response.set_etag(...) -The reason for this is that some file-like objects have a invalid or even +The reason for this is that some file-like objects have an invalid or even misleading ``name`` attribute. Silently swallowing errors in such cases was not a satisfying solution. @@ -143,7 +143,7 @@ ``flask.Flask.request_globals_class`` attribute was renamed to :attr:`flask.Flask.app_ctx_globals_class`. -.. _Flask-OldSessions: http://pythonhosted.org/Flask-OldSessions/ +.. _Flask-OldSessions: https://pythonhosted.org/Flask-OldSessions/ Version 0.9 ----------- @@ -198,7 +198,7 @@ possible we tried to counter the problems arising from these changes by providing a script that can ease the transition. -The script scans your whole application and generates an unified diff with +The script scans your whole application and generates a unified diff with changes it assumes are safe to apply. However as this is an automated tool it won't be able to find all use cases and it might miss some. We internally spread a lot of deprecation warnings all over the place to make @@ -215,7 +215,7 @@ To apply the upgrade script do the following: 1. Download the script: `flask-07-upgrade.py - `_ + `_ 2. Run it in the directory of your application:: python flask-07-upgrade.py > patchfile.diff diff -Nru flask-0.12.2/examples/blueprintexample/blueprintexample.py flask-1.0.2/examples/blueprintexample/blueprintexample.py --- flask-0.12.2/examples/blueprintexample/blueprintexample.py 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/examples/blueprintexample/blueprintexample.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from flask import Flask -from simple_page.simple_page import simple_page - -app = Flask(__name__) -app.register_blueprint(simple_page) -# Blueprint can be registered many times -app.register_blueprint(simple_page, url_prefix='/pages') - -if __name__=='__main__': - app.run() diff -Nru flask-0.12.2/examples/blueprintexample/simple_page/simple_page.py flask-1.0.2/examples/blueprintexample/simple_page/simple_page.py --- flask-0.12.2/examples/blueprintexample/simple_page/simple_page.py 2015-04-16 17:02:46.000000000 +0000 +++ flask-1.0.2/examples/blueprintexample/simple_page/simple_page.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from flask import Blueprint, render_template, abort -from jinja2 import TemplateNotFound - -simple_page = Blueprint('simple_page', __name__, - template_folder='templates') - -@simple_page.route('/', defaults={'page': 'index'}) -@simple_page.route('/') -def show(page): - try: - return render_template('pages/%s.html' % page) - except TemplateNotFound: - abort(404) diff -Nru flask-0.12.2/examples/blueprintexample/simple_page/templates/pages/hello.html flask-1.0.2/examples/blueprintexample/simple_page/templates/pages/hello.html --- flask-0.12.2/examples/blueprintexample/simple_page/templates/pages/hello.html 2014-11-01 15:48:02.000000000 +0000 +++ flask-1.0.2/examples/blueprintexample/simple_page/templates/pages/hello.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -{% extends "pages/layout.html" %} - -{% block body %} - Hello -{% endblock %} diff -Nru flask-0.12.2/examples/blueprintexample/simple_page/templates/pages/index.html flask-1.0.2/examples/blueprintexample/simple_page/templates/pages/index.html --- flask-0.12.2/examples/blueprintexample/simple_page/templates/pages/index.html 2014-11-01 15:48:02.000000000 +0000 +++ flask-1.0.2/examples/blueprintexample/simple_page/templates/pages/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -{% extends "pages/layout.html" %} - -{% block body %} - Blueprint example page -{% endblock %} diff -Nru flask-0.12.2/examples/blueprintexample/simple_page/templates/pages/layout.html flask-1.0.2/examples/blueprintexample/simple_page/templates/pages/layout.html --- flask-0.12.2/examples/blueprintexample/simple_page/templates/pages/layout.html 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/examples/blueprintexample/simple_page/templates/pages/layout.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - -Simple Page Blueprint -
-

This is blueprint example

-

- A simple page blueprint is registered under / and /pages - you can access it using this URLs: -

-

- Also you can register the same blueprint under another path -

- - {% block body %}{% endblock %} -
diff -Nru flask-0.12.2/examples/blueprintexample/simple_page/templates/pages/world.html flask-1.0.2/examples/blueprintexample/simple_page/templates/pages/world.html --- flask-0.12.2/examples/blueprintexample/simple_page/templates/pages/world.html 2014-11-01 15:48:02.000000000 +0000 +++ flask-1.0.2/examples/blueprintexample/simple_page/templates/pages/world.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -{% extends "pages/layout.html" %} -{% block body %} - World -{% endblock %} diff -Nru flask-0.12.2/examples/blueprintexample/test_blueprintexample.py flask-1.0.2/examples/blueprintexample/test_blueprintexample.py --- flask-0.12.2/examples/blueprintexample/test_blueprintexample.py 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/examples/blueprintexample/test_blueprintexample.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -""" - Blueprint Example Tests - ~~~~~~~~~~~~~~ - - Tests the Blueprint example app -""" -import pytest - -import blueprintexample - - -@pytest.fixture -def client(): - return blueprintexample.app.test_client() - - -def test_urls(client): - r = client.get('/') - assert r.status_code == 200 - - r = client.get('/hello') - assert r.status_code == 200 - - r = client.get('/world') - assert r.status_code == 200 - - # second blueprint instance - r = client.get('/pages/hello') - assert r.status_code == 200 - - r = client.get('/pages/world') - assert r.status_code == 200 diff -Nru flask-0.12.2/examples/flaskr/flaskr/flaskr.py flask-1.0.2/examples/flaskr/flaskr/flaskr.py --- flask-0.12.2/examples/flaskr/flaskr/flaskr.py 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr/flaskr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -# -*- coding: utf-8 -*- -""" - Flaskr - ~~~~~~ - - A microblog example application written as Flask tutorial with - Flask and sqlite3. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import os -from sqlite3 import dbapi2 as sqlite3 -from flask import Flask, request, session, g, redirect, url_for, abort, \ - render_template, flash - - -# create our little application :) -app = Flask(__name__) - -# Load default config and override config from an environment variable -app.config.update(dict( - DATABASE=os.path.join(app.root_path, 'flaskr.db'), - DEBUG=True, - SECRET_KEY='development key', - USERNAME='admin', - PASSWORD='default' -)) -app.config.from_envvar('FLASKR_SETTINGS', silent=True) - - -def connect_db(): - """Connects to the specific database.""" - rv = sqlite3.connect(app.config['DATABASE']) - rv.row_factory = sqlite3.Row - return rv - - -def init_db(): - """Initializes the database.""" - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() - - -@app.cli.command('initdb') -def initdb_command(): - """Creates the database tables.""" - init_db() - print('Initialized the database.') - - -def get_db(): - """Opens a new database connection if there is none yet for the - current application context. - """ - if not hasattr(g, 'sqlite_db'): - g.sqlite_db = connect_db() - return g.sqlite_db - - -@app.teardown_appcontext -def close_db(error): - """Closes the database again at the end of the request.""" - if hasattr(g, 'sqlite_db'): - g.sqlite_db.close() - - -@app.route('/') -def show_entries(): - db = get_db() - cur = db.execute('select title, text from entries order by id desc') - entries = cur.fetchall() - return render_template('show_entries.html', entries=entries) - - -@app.route('/add', methods=['POST']) -def add_entry(): - if not session.get('logged_in'): - abort(401) - db = get_db() - db.execute('insert into entries (title, text) values (?, ?)', - [request.form['title'], request.form['text']]) - db.commit() - flash('New entry was successfully posted') - return redirect(url_for('show_entries')) - - -@app.route('/login', methods=['GET', 'POST']) -def login(): - error = None - if request.method == 'POST': - if request.form['username'] != app.config['USERNAME']: - error = 'Invalid username' - elif request.form['password'] != app.config['PASSWORD']: - error = 'Invalid password' - else: - session['logged_in'] = True - flash('You were logged in') - return redirect(url_for('show_entries')) - return render_template('login.html', error=error) - - -@app.route('/logout') -def logout(): - session.pop('logged_in', None) - flash('You were logged out') - return redirect(url_for('show_entries')) diff -Nru flask-0.12.2/examples/flaskr/flaskr/__init__.py flask-1.0.2/examples/flaskr/flaskr/__init__.py --- flask-0.12.2/examples/flaskr/flaskr/__init__.py 2017-02-10 11:20:32.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -from .flaskr import app diff -Nru flask-0.12.2/examples/flaskr/flaskr/schema.sql flask-1.0.2/examples/flaskr/flaskr/schema.sql --- flask-0.12.2/examples/flaskr/flaskr/schema.sql 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr/schema.sql 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -drop table if exists entries; -create table entries ( - id integer primary key autoincrement, - title text not null, - 'text' text not null -); diff -Nru flask-0.12.2/examples/flaskr/flaskr/static/style.css flask-1.0.2/examples/flaskr/flaskr/static/style.css --- flask-0.12.2/examples/flaskr/flaskr/static/style.css 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr/static/style.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -body { font-family: sans-serif; background: #eee; } -a, h1, h2 { color: #377BA8; } -h1, h2 { font-family: 'Georgia', serif; margin: 0; } -h1 { border-bottom: 2px solid #eee; } -h2 { font-size: 1.2em; } - -.page { margin: 2em auto; width: 35em; border: 5px solid #ccc; - padding: 0.8em; background: white; } -.entries { list-style: none; margin: 0; padding: 0; } -.entries li { margin: 0.8em 1.2em; } -.entries li h2 { margin-left: -1em; } -.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; } -.add-entry dl { font-weight: bold; } -.metanav { text-align: right; font-size: 0.8em; padding: 0.3em; - margin-bottom: 1em; background: #fafafa; } -.flash { background: #CEE5F5; padding: 0.5em; - border: 1px solid #AACBE2; } -.error { background: #F0D6D6; padding: 0.5em; } diff -Nru flask-0.12.2/examples/flaskr/flaskr/templates/layout.html flask-1.0.2/examples/flaskr/flaskr/templates/layout.html --- flask-0.12.2/examples/flaskr/flaskr/templates/layout.html 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr/templates/layout.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - -Flaskr - -
-

Flaskr

-
- {% if not session.logged_in %} - log in - {% else %} - log out - {% endif %} -
- {% for message in get_flashed_messages() %} -
{{ message }}
- {% endfor %} - {% block body %}{% endblock %} -
diff -Nru flask-0.12.2/examples/flaskr/flaskr/templates/login.html flask-1.0.2/examples/flaskr/flaskr/templates/login.html --- flask-0.12.2/examples/flaskr/flaskr/templates/login.html 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr/templates/login.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -{% extends "layout.html" %} -{% block body %} -

Login

- {% if error %}

Error: {{ error }}{% endif %} -

-
-
Username: -
-
Password: -
-
-
-
-{% endblock %} diff -Nru flask-0.12.2/examples/flaskr/flaskr/templates/show_entries.html flask-1.0.2/examples/flaskr/flaskr/templates/show_entries.html --- flask-0.12.2/examples/flaskr/flaskr/templates/show_entries.html 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr/templates/show_entries.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -{% extends "layout.html" %} -{% block body %} - {% if session.logged_in %} -
-
-
Title: -
-
Text: -
-
-
-
- {% endif %} -
    - {% for entry in entries %} -
  • {{ entry.title }}

    {{ entry.text|safe }}
  • - {% else %} -
  • Unbelievable. No entries here so far
  • - {% endfor %} -
-{% endblock %} diff -Nru flask-0.12.2/examples/flaskr/flaskr.egg-info/dependency_links.txt flask-1.0.2/examples/flaskr/flaskr.egg-info/dependency_links.txt --- flask-0.12.2/examples/flaskr/flaskr.egg-info/dependency_links.txt 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr.egg-info/dependency_links.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ - diff -Nru flask-0.12.2/examples/flaskr/flaskr.egg-info/PKG-INFO flask-1.0.2/examples/flaskr/flaskr.egg-info/PKG-INFO --- flask-0.12.2/examples/flaskr/flaskr.egg-info/PKG-INFO 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr.egg-info/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -Metadata-Version: 1.0 -Name: flaskr -Version: 0.0.0 -Summary: UNKNOWN -Home-page: UNKNOWN -Author: UNKNOWN -Author-email: UNKNOWN -License: UNKNOWN -Description: UNKNOWN -Platform: UNKNOWN diff -Nru flask-0.12.2/examples/flaskr/flaskr.egg-info/requires.txt flask-1.0.2/examples/flaskr/flaskr.egg-info/requires.txt --- flask-0.12.2/examples/flaskr/flaskr.egg-info/requires.txt 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr.egg-info/requires.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -flask \ No newline at end of file diff -Nru flask-0.12.2/examples/flaskr/flaskr.egg-info/SOURCES.txt flask-1.0.2/examples/flaskr/flaskr.egg-info/SOURCES.txt --- flask-0.12.2/examples/flaskr/flaskr.egg-info/SOURCES.txt 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr.egg-info/SOURCES.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -MANIFEST.in -README -setup.cfg -flaskr/__init__.py -flaskr/flaskr.py -flaskr/schema.sql -flaskr.egg-info/PKG-INFO -flaskr.egg-info/SOURCES.txt -flaskr.egg-info/dependency_links.txt -flaskr.egg-info/requires.txt -flaskr.egg-info/top_level.txt -flaskr/static/style.css -flaskr/templates/layout.html -flaskr/templates/login.html -flaskr/templates/show_entries.html \ No newline at end of file diff -Nru flask-0.12.2/examples/flaskr/flaskr.egg-info/top_level.txt flask-1.0.2/examples/flaskr/flaskr.egg-info/top_level.txt --- flask-0.12.2/examples/flaskr/flaskr.egg-info/top_level.txt 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/flaskr/flaskr.egg-info/top_level.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -flaskr diff -Nru flask-0.12.2/examples/flaskr/.gitignore flask-1.0.2/examples/flaskr/.gitignore --- flask-0.12.2/examples/flaskr/.gitignore 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/examples/flaskr/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -flaskr.db -.eggs/ diff -Nru flask-0.12.2/examples/flaskr/MANIFEST.in flask-1.0.2/examples/flaskr/MANIFEST.in --- flask-0.12.2/examples/flaskr/MANIFEST.in 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/examples/flaskr/MANIFEST.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -graft flaskr/templates -graft flaskr/static -include flaskr/schema.sql Binary files /tmp/tmpuVwMMU/XwFI0z8Ro6/flask-0.12.2/examples/flaskr/pytest_runner-2.11.1-py2.7.egg and /tmp/tmpuVwMMU/G9IPXPbDjx/flask-1.0.2/examples/flaskr/pytest_runner-2.11.1-py2.7.egg differ diff -Nru flask-0.12.2/examples/flaskr/README flask-1.0.2/examples/flaskr/README --- flask-0.12.2/examples/flaskr/README 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/flaskr/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ - / Flaskr / - - a minimal blog application - - - ~ What is Flaskr? - - A sqlite powered thumble blog application - - ~ How do I use it? - - 1. edit the configuration in the flaskr.py file or - export an FLASKR_SETTINGS environment variable - pointing to a configuration file. - - 2. install the app from the root of the project directory - - pip install --editable . - - 3. Instruct flask to use the right application - - export FLASK_APP=flaskr - - 4. initialize the database with this command: - - flask initdb - - 5. now you can run flaskr: - - flask run - - the application will greet you on - http://localhost:5000/ - - ~ Is it tested? - - You betcha. Run `python setup.py test` to see - the tests pass. diff -Nru flask-0.12.2/examples/flaskr/setup.cfg flask-1.0.2/examples/flaskr/setup.cfg --- flask-0.12.2/examples/flaskr/setup.cfg 2017-02-10 11:20:32.000000000 +0000 +++ flask-1.0.2/examples/flaskr/setup.cfg 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -[tool:pytest] -test=pytest diff -Nru flask-0.12.2/examples/flaskr/setup.py flask-1.0.2/examples/flaskr/setup.py --- flask-0.12.2/examples/flaskr/setup.py 2016-09-08 13:20:52.000000000 +0000 +++ flask-1.0.2/examples/flaskr/setup.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -from setuptools import setup - -setup( - name='flaskr', - packages=['flaskr'], - include_package_data=True, - install_requires=[ - 'flask', - ], - setup_requires=[ - 'pytest-runner', - ], - tests_require=[ - 'pytest', - ], -) diff -Nru flask-0.12.2/examples/flaskr/tests/test_flaskr.py flask-1.0.2/examples/flaskr/tests/test_flaskr.py --- flask-0.12.2/examples/flaskr/tests/test_flaskr.py 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/flaskr/tests/test_flaskr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -# -*- coding: utf-8 -*- -""" - Flaskr Tests - ~~~~~~~~~~~~ - - Tests the Flaskr application. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import os -import tempfile -import pytest -from flaskr import flaskr - - -@pytest.fixture -def client(request): - db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.config['TESTING'] = True - client = flaskr.app.test_client() - with flaskr.app.app_context(): - flaskr.init_db() - - def teardown(): - os.close(db_fd) - os.unlink(flaskr.app.config['DATABASE']) - request.addfinalizer(teardown) - - return client - - -def login(client, username, password): - return client.post('/login', data=dict( - username=username, - password=password - ), follow_redirects=True) - - -def logout(client): - return client.get('/logout', follow_redirects=True) - - -def test_empty_db(client): - """Start with a blank database.""" - rv = client.get('/') - assert b'No entries here so far' in rv.data - - -def test_login_logout(client): - """Make sure login and logout works""" - rv = login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD']) - assert b'You were logged in' in rv.data - rv = logout(client) - assert b'You were logged out' in rv.data - rv = login(client, flaskr.app.config['USERNAME'] + 'x', - flaskr.app.config['PASSWORD']) - assert b'Invalid username' in rv.data - rv = login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD'] + 'x') - assert b'Invalid password' in rv.data - - -def test_messages(client): - """Test that messages work""" - login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD']) - rv = client.post('/add', data=dict( - title='', - text='HTML allowed here' - ), follow_redirects=True) - assert b'No entries here so far' not in rv.data - assert b'<Hello>' in rv.data - assert b'HTML allowed here' in rv.data diff -Nru flask-0.12.2/examples/javascript/.gitignore flask-1.0.2/examples/javascript/.gitignore --- flask-0.12.2/examples/javascript/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/.gitignore 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,14 @@ +venv/ +*.pyc +__pycache__/ +instance/ +.cache/ +.pytest_cache/ +.coverage +htmlcov/ +dist/ +build/ +*.egg-info/ +.idea/ +*.swp +*~ diff -Nru flask-0.12.2/examples/javascript/js_example/__init__.py flask-1.0.2/examples/javascript/js_example/__init__.py --- flask-0.12.2/examples/javascript/js_example/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/js_example/__init__.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,5 @@ +from flask import Flask + +app = Flask(__name__) + +from js_example import views diff -Nru flask-0.12.2/examples/javascript/js_example/templates/base.html flask-1.0.2/examples/javascript/js_example/templates/base.html --- flask-0.12.2/examples/javascript/js_example/templates/base.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/js_example/templates/base.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,33 @@ + +JavaScript Example + + + + +
+

{% block intro %}{% endblock %}

+
+
+ + + + + +
+= +{% block script %}{% endblock %} diff -Nru flask-0.12.2/examples/javascript/js_example/templates/fetch.html flask-1.0.2/examples/javascript/js_example/templates/fetch.html --- flask-0.12.2/examples/javascript/js_example/templates/fetch.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/js_example/templates/fetch.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,36 @@ +{% extends 'base.html' %} + +{% block intro %} + fetch + is the new plain JavaScript way to make requests. It's + supported in all modern browsers except IE, which requires a + polyfill. +{% endblock %} + +{% block script %} + + + +{% endblock %} diff -Nru flask-0.12.2/examples/javascript/js_example/templates/jquery.html flask-1.0.2/examples/javascript/js_example/templates/jquery.html --- flask-0.12.2/examples/javascript/js_example/templates/jquery.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/js_example/templates/jquery.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,27 @@ +{% extends 'base.html' %} + +{% block intro %} + jQuery is a popular library that + adds cross browser APIs for common tasks. However, it requires loading + an extra library. +{% endblock %} + +{% block script %} + + +{% endblock %} diff -Nru flask-0.12.2/examples/javascript/js_example/templates/plain.html flask-1.0.2/examples/javascript/js_example/templates/plain.html --- flask-0.12.2/examples/javascript/js_example/templates/plain.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/js_example/templates/plain.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,28 @@ +{% extends 'base.html' %} + +{% block intro %} + XMLHttpRequest + is the plain JavaScript way to make requests. It's natively supported + by all browsers. +{% endblock %} + +{% block script %} + +{% endblock %} diff -Nru flask-0.12.2/examples/javascript/js_example/views.py flask-1.0.2/examples/javascript/js_example/views.py --- flask-0.12.2/examples/javascript/js_example/views.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/js_example/views.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,16 @@ +from flask import jsonify, render_template, request + +from js_example import app + + +@app.route('/', defaults={'js': 'plain'}) +@app.route('/') +def index(js): + return render_template('{0}.html'.format(js), js=js) + + +@app.route('/add', methods=['POST']) +def add(): + a = request.form.get('a', 0, type=float) + b = request.form.get('b', 0, type=float) + return jsonify(result=a + b) diff -Nru flask-0.12.2/examples/javascript/LICENSE flask-1.0.2/examples/javascript/LICENSE --- flask-0.12.2/examples/javascript/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/LICENSE 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,31 @@ +Copyright © 2010 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as +well as documentation, with or without modification, are permitted +provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff -Nru flask-0.12.2/examples/javascript/MANIFEST.in flask-1.0.2/examples/javascript/MANIFEST.in --- flask-0.12.2/examples/javascript/MANIFEST.in 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/MANIFEST.in 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,4 @@ +include LICENSE +graft js_example/templates +graft tests +global-exclude *.pyc diff -Nru flask-0.12.2/examples/javascript/README.rst flask-1.0.2/examples/javascript/README.rst --- flask-0.12.2/examples/javascript/README.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/README.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,49 @@ +JavaScript Ajax Example +======================= + +Demonstrates how to post form data and process a JSON response using +JavaScript. This allows making requests without navigating away from the +page. Demonstrates using |XMLHttpRequest|_, |fetch|_, and +|jQuery.ajax|_. See the `Flask docs`_ about jQuery and Ajax. + +.. |XMLHttpRequest| replace:: ``XMLHttpRequest`` +.. _XMLHttpRequest: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest + +.. |fetch| replace:: ``fetch`` +.. _fetch: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch + +.. |jQuery.ajax| replace:: ``jQuery.ajax`` +.. _jQuery.ajax: https://api.jquery.com/jQuery.ajax/ + +.. _Flask docs: http://flask.pocoo.org/docs/patterns/jquery/ + + +Install +------- + +:: + + python3 -m venv venv + . venv/bin/activate + pip install -e . + + +Run +--- + +:: + + export FLASK_APP=js_example + flask run + +Open http://127.0.0.1:5000 in a browser. + + +Test +---- + +:: + + pip install -e '.[test]' + coverage run -m pytest + coverage report diff -Nru flask-0.12.2/examples/javascript/setup.cfg flask-1.0.2/examples/javascript/setup.cfg --- flask-0.12.2/examples/javascript/setup.cfg 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/setup.cfg 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,13 @@ +[metadata] +license_file = LICENSE + +[bdist_wheel] +universal = True + +[tool:pytest] +testpaths = tests + +[coverage:run] +branch = True +source = + js_example diff -Nru flask-0.12.2/examples/javascript/setup.py flask-1.0.2/examples/javascript/setup.py --- flask-0.12.2/examples/javascript/setup.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/setup.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,30 @@ +import io + +from setuptools import find_packages, setup + +with io.open('README.rst', 'rt', encoding='utf8') as f: + readme = f.read() + +setup( + name='js_example', + version='1.0.0', + url='http://flask.pocoo.org/docs/patterns/jquery/', + license='BSD', + maintainer='Pallets team', + maintainer_email='contact@palletsprojects.com', + description='Demonstrates making Ajax requests to Flask.', + long_description=readme, + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=[ + 'flask', + ], + extras_require={ + 'test': [ + 'pytest', + 'coverage', + 'blinker', + ], + }, +) diff -Nru flask-0.12.2/examples/javascript/tests/conftest.py flask-1.0.2/examples/javascript/tests/conftest.py --- flask-0.12.2/examples/javascript/tests/conftest.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/tests/conftest.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,15 @@ +import pytest + +from js_example import app + + +@pytest.fixture(name='app') +def fixture_app(): + app.testing = True + yield app + app.testing = False + + +@pytest.fixture +def client(app): + return app.test_client() diff -Nru flask-0.12.2/examples/javascript/tests/test_js_example.py flask-1.0.2/examples/javascript/tests/test_js_example.py --- flask-0.12.2/examples/javascript/tests/test_js_example.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/javascript/tests/test_js_example.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,28 @@ +import pytest + +from flask import template_rendered + + +@pytest.mark.parametrize(('path', 'template_name'), ( + ('/', 'plain.html'), + ('/plain', 'plain.html'), + ('/fetch', 'fetch.html'), + ('/jquery', 'jquery.html'), +)) +def test_index(app, client, path, template_name): + def check(sender, template, context): + assert template.name == template_name + + with template_rendered.connected_to(check, app): + client.get(path) + + +@pytest.mark.parametrize(('a', 'b', 'result'), ( + (2, 3, 5), + (2.5, 3, 5.5), + (2, None, 2), + (2, 'b', 2), +)) +def test_add(client, a, b, result): + response = client.post('/add', data={'a': a, 'b': b}) + assert response.get_json()['result'] == result diff -Nru flask-0.12.2/examples/jqueryexample/jqueryexample.py flask-1.0.2/examples/jqueryexample/jqueryexample.py --- flask-0.12.2/examples/jqueryexample/jqueryexample.py 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/examples/jqueryexample/jqueryexample.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -""" - jQuery Example - ~~~~~~~~~~~~~~ - - A simple application that shows how Flask and jQuery get along. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -from flask import Flask, jsonify, render_template, request -app = Flask(__name__) - - -@app.route('/_add_numbers') -def add_numbers(): - """Add two numbers server side, ridiculous but well...""" - a = request.args.get('a', 0, type=int) - b = request.args.get('b', 0, type=int) - return jsonify(result=a + b) - - -@app.route('/') -def index(): - return render_template('index.html') - -if __name__ == '__main__': - app.run() diff -Nru flask-0.12.2/examples/jqueryexample/templates/index.html flask-1.0.2/examples/jqueryexample/templates/index.html --- flask-0.12.2/examples/jqueryexample/templates/index.html 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/examples/jqueryexample/templates/index.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -{% extends "layout.html" %} -{% block body %} - -

jQuery Example

-

- + - = - ? -

calculate server side -{% endblock %} diff -Nru flask-0.12.2/examples/jqueryexample/templates/layout.html flask-1.0.2/examples/jqueryexample/templates/layout.html --- flask-0.12.2/examples/jqueryexample/templates/layout.html 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/examples/jqueryexample/templates/layout.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ - -jQuery Example - - -{% block body %}{% endblock %} diff -Nru flask-0.12.2/examples/minitwit/.gitignore flask-1.0.2/examples/minitwit/.gitignore --- flask-0.12.2/examples/minitwit/.gitignore 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -minitwit.db -.eggs/ diff -Nru flask-0.12.2/examples/minitwit/MANIFEST.in flask-1.0.2/examples/minitwit/MANIFEST.in --- flask-0.12.2/examples/minitwit/MANIFEST.in 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/MANIFEST.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -graft minitwit/templates -graft minitwit/static -include minitwit/schema.sql \ No newline at end of file diff -Nru flask-0.12.2/examples/minitwit/minitwit/__init__.py flask-1.0.2/examples/minitwit/minitwit/__init__.py --- flask-0.12.2/examples/minitwit/minitwit/__init__.py 2017-02-10 11:20:32.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -from .minitwit import app diff -Nru flask-0.12.2/examples/minitwit/minitwit/minitwit.py flask-1.0.2/examples/minitwit/minitwit/minitwit.py --- flask-0.12.2/examples/minitwit/minitwit/minitwit.py 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit/minitwit.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,256 +0,0 @@ -# -*- coding: utf-8 -*- -""" - MiniTwit - ~~~~~~~~ - - A microblogging application written with Flask and sqlite3. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import time -from sqlite3 import dbapi2 as sqlite3 -from hashlib import md5 -from datetime import datetime -from flask import Flask, request, session, url_for, redirect, \ - render_template, abort, g, flash, _app_ctx_stack -from werkzeug import check_password_hash, generate_password_hash - - -# configuration -DATABASE = '/tmp/minitwit.db' -PER_PAGE = 30 -DEBUG = True -SECRET_KEY = 'development key' - -# create our little application :) -app = Flask(__name__) -app.config.from_object(__name__) -app.config.from_envvar('MINITWIT_SETTINGS', silent=True) - - -def get_db(): - """Opens a new database connection if there is none yet for the - current application context. - """ - top = _app_ctx_stack.top - if not hasattr(top, 'sqlite_db'): - top.sqlite_db = sqlite3.connect(app.config['DATABASE']) - top.sqlite_db.row_factory = sqlite3.Row - return top.sqlite_db - - -@app.teardown_appcontext -def close_database(exception): - """Closes the database again at the end of the request.""" - top = _app_ctx_stack.top - if hasattr(top, 'sqlite_db'): - top.sqlite_db.close() - - -def init_db(): - """Initializes the database.""" - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() - - -@app.cli.command('initdb') -def initdb_command(): - """Creates the database tables.""" - init_db() - print('Initialized the database.') - - -def query_db(query, args=(), one=False): - """Queries the database and returns a list of dictionaries.""" - cur = get_db().execute(query, args) - rv = cur.fetchall() - return (rv[0] if rv else None) if one else rv - - -def get_user_id(username): - """Convenience method to look up the id for a username.""" - rv = query_db('select user_id from user where username = ?', - [username], one=True) - return rv[0] if rv else None - - -def format_datetime(timestamp): - """Format a timestamp for display.""" - return datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d @ %H:%M') - - -def gravatar_url(email, size=80): - """Return the gravatar image for the given email address.""" - return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \ - (md5(email.strip().lower().encode('utf-8')).hexdigest(), size) - - -@app.before_request -def before_request(): - g.user = None - if 'user_id' in session: - g.user = query_db('select * from user where user_id = ?', - [session['user_id']], one=True) - - -@app.route('/') -def timeline(): - """Shows a users timeline or if no user is logged in it will - redirect to the public timeline. This timeline shows the user's - messages as well as all the messages of followed users. - """ - if not g.user: - return redirect(url_for('public_timeline')) - return render_template('timeline.html', messages=query_db(''' - select message.*, user.* from message, user - where message.author_id = user.user_id and ( - user.user_id = ? or - user.user_id in (select whom_id from follower - where who_id = ?)) - order by message.pub_date desc limit ?''', - [session['user_id'], session['user_id'], PER_PAGE])) - - -@app.route('/public') -def public_timeline(): - """Displays the latest messages of all users.""" - return render_template('timeline.html', messages=query_db(''' - select message.*, user.* from message, user - where message.author_id = user.user_id - order by message.pub_date desc limit ?''', [PER_PAGE])) - - -@app.route('/') -def user_timeline(username): - """Display's a users tweets.""" - profile_user = query_db('select * from user where username = ?', - [username], one=True) - if profile_user is None: - abort(404) - followed = False - if g.user: - followed = query_db('''select 1 from follower where - follower.who_id = ? and follower.whom_id = ?''', - [session['user_id'], profile_user['user_id']], - one=True) is not None - return render_template('timeline.html', messages=query_db(''' - select message.*, user.* from message, user where - user.user_id = message.author_id and user.user_id = ? - order by message.pub_date desc limit ?''', - [profile_user['user_id'], PER_PAGE]), followed=followed, - profile_user=profile_user) - - -@app.route('//follow') -def follow_user(username): - """Adds the current user as follower of the given user.""" - if not g.user: - abort(401) - whom_id = get_user_id(username) - if whom_id is None: - abort(404) - db = get_db() - db.execute('insert into follower (who_id, whom_id) values (?, ?)', - [session['user_id'], whom_id]) - db.commit() - flash('You are now following "%s"' % username) - return redirect(url_for('user_timeline', username=username)) - - -@app.route('//unfollow') -def unfollow_user(username): - """Removes the current user as follower of the given user.""" - if not g.user: - abort(401) - whom_id = get_user_id(username) - if whom_id is None: - abort(404) - db = get_db() - db.execute('delete from follower where who_id=? and whom_id=?', - [session['user_id'], whom_id]) - db.commit() - flash('You are no longer following "%s"' % username) - return redirect(url_for('user_timeline', username=username)) - - -@app.route('/add_message', methods=['POST']) -def add_message(): - """Registers a new message for the user.""" - if 'user_id' not in session: - abort(401) - if request.form['text']: - db = get_db() - db.execute('''insert into message (author_id, text, pub_date) - values (?, ?, ?)''', (session['user_id'], request.form['text'], - int(time.time()))) - db.commit() - flash('Your message was recorded') - return redirect(url_for('timeline')) - - -@app.route('/login', methods=['GET', 'POST']) -def login(): - """Logs the user in.""" - if g.user: - return redirect(url_for('timeline')) - error = None - if request.method == 'POST': - user = query_db('''select * from user where - username = ?''', [request.form['username']], one=True) - if user is None: - error = 'Invalid username' - elif not check_password_hash(user['pw_hash'], - request.form['password']): - error = 'Invalid password' - else: - flash('You were logged in') - session['user_id'] = user['user_id'] - return redirect(url_for('timeline')) - return render_template('login.html', error=error) - - -@app.route('/register', methods=['GET', 'POST']) -def register(): - """Registers the user.""" - if g.user: - return redirect(url_for('timeline')) - error = None - if request.method == 'POST': - if not request.form['username']: - error = 'You have to enter a username' - elif not request.form['email'] or \ - '@' not in request.form['email']: - error = 'You have to enter a valid email address' - elif not request.form['password']: - error = 'You have to enter a password' - elif request.form['password'] != request.form['password2']: - error = 'The two passwords do not match' - elif get_user_id(request.form['username']) is not None: - error = 'The username is already taken' - else: - db = get_db() - db.execute('''insert into user ( - username, email, pw_hash) values (?, ?, ?)''', - [request.form['username'], request.form['email'], - generate_password_hash(request.form['password'])]) - db.commit() - flash('You were successfully registered and can login now') - return redirect(url_for('login')) - return render_template('register.html', error=error) - - -@app.route('/logout') -def logout(): - """Logs the user out.""" - flash('You were logged out') - session.pop('user_id', None) - return redirect(url_for('public_timeline')) - - -# add some filters to jinja -app.jinja_env.filters['datetimeformat'] = format_datetime -app.jinja_env.filters['gravatar'] = gravatar_url diff -Nru flask-0.12.2/examples/minitwit/minitwit/schema.sql flask-1.0.2/examples/minitwit/minitwit/schema.sql --- flask-0.12.2/examples/minitwit/minitwit/schema.sql 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit/schema.sql 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -drop table if exists user; -create table user ( - user_id integer primary key autoincrement, - username text not null, - email text not null, - pw_hash text not null -); - -drop table if exists follower; -create table follower ( - who_id integer, - whom_id integer -); - -drop table if exists message; -create table message ( - message_id integer primary key autoincrement, - author_id integer not null, - text text not null, - pub_date integer -); diff -Nru flask-0.12.2/examples/minitwit/minitwit/static/style.css flask-1.0.2/examples/minitwit/minitwit/static/style.css --- flask-0.12.2/examples/minitwit/minitwit/static/style.css 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit/static/style.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,178 +0,0 @@ -body { - background: #CAECE9; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; -} - -a { - color: #26776F; -} - -a:hover { - color: #333; -} - -input[type="text"], -input[type="password"] { - background: white; - border: 1px solid #BFE6E2; - padding: 2px; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - color: #105751; -} - -input[type="submit"] { - background: #105751; - border: 1px solid #073B36; - padding: 1px 3px; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; - font-weight: bold; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - color: white; -} - -div.page { - background: white; - border: 1px solid #6ECCC4; - width: 700px; - margin: 30px auto; -} - -div.page h1 { - background: #6ECCC4; - margin: 0; - padding: 10px 14px; - color: white; - letter-spacing: 1px; - text-shadow: 0 0 3px #24776F; - font-weight: normal; -} - -div.page div.navigation { - background: #DEE9E8; - padding: 4px 10px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #eee; - color: #888; - font-size: 12px; - letter-spacing: 0.5px; -} - -div.page div.navigation a { - color: #444; - font-weight: bold; -} - -div.page h2 { - margin: 0 0 15px 0; - color: #105751; - text-shadow: 0 1px 2px #ccc; -} - -div.page div.body { - padding: 10px; -} - -div.page div.footer { - background: #eee; - color: #888; - padding: 5px 10px; - font-size: 12px; -} - -div.page div.followstatus { - border: 1px solid #ccc; - background: #E3EBEA; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 3px; - font-size: 13px; -} - -div.page ul.messages { - list-style: none; - margin: 0; - padding: 0; -} - -div.page ul.messages li { - margin: 10px 0; - padding: 5px; - background: #F0FAF9; - border: 1px solid #DBF3F1; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - min-height: 48px; -} - -div.page ul.messages p { - margin: 0; -} - -div.page ul.messages li img { - float: left; - padding: 0 10px 0 0; -} - -div.page ul.messages li small { - font-size: 0.9em; - color: #888; -} - -div.page div.twitbox { - margin: 10px 0; - padding: 5px; - background: #F0FAF9; - border: 1px solid #94E2DA; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; -} - -div.page div.twitbox h3 { - margin: 0; - font-size: 1em; - color: #2C7E76; -} - -div.page div.twitbox p { - margin: 0; -} - -div.page div.twitbox input[type="text"] { - width: 585px; -} - -div.page div.twitbox input[type="submit"] { - width: 70px; - margin-left: 5px; -} - -ul.flashes { - list-style: none; - margin: 10px 10px 0 10px; - padding: 0; -} - -ul.flashes li { - background: #B9F3ED; - border: 1px solid #81CEC6; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 4px; - font-size: 13px; -} - -div.error { - margin: 10px 0; - background: #FAE4E4; - border: 1px solid #DD6F6F; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 4px; - font-size: 13px; -} diff -Nru flask-0.12.2/examples/minitwit/minitwit/templates/layout.html flask-1.0.2/examples/minitwit/minitwit/templates/layout.html --- flask-0.12.2/examples/minitwit/minitwit/templates/layout.html 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit/templates/layout.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ - -{% block title %}Welcome{% endblock %} | MiniTwit - -

-

MiniTwit

- - {% with flashes = get_flashed_messages() %} - {% if flashes %} -
    - {% for message in flashes %} -
  • {{ message }} - {% endfor %} -
- {% endif %} - {% endwith %} -
- {% block body %}{% endblock %} -
- -
diff -Nru flask-0.12.2/examples/minitwit/minitwit/templates/login.html flask-1.0.2/examples/minitwit/minitwit/templates/login.html --- flask-0.12.2/examples/minitwit/minitwit/templates/login.html 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit/templates/login.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -{% extends "layout.html" %} -{% block title %}Sign In{% endblock %} -{% block body %} -

Sign In

- {% if error %}
Error: {{ error }}
{% endif %} -
-
-
Username: -
-
Password: -
-
-
-
-{% endblock %} - diff -Nru flask-0.12.2/examples/minitwit/minitwit/templates/register.html flask-1.0.2/examples/minitwit/minitwit/templates/register.html --- flask-0.12.2/examples/minitwit/minitwit/templates/register.html 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit/templates/register.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -{% extends "layout.html" %} -{% block title %}Sign Up{% endblock %} -{% block body %} -

Sign Up

- {% if error %}
Error: {{ error }}
{% endif %} -
-
-
Username: -
-
E-Mail: -
-
Password: -
-
Password (repeat): -
-
-
-
-{% endblock %} diff -Nru flask-0.12.2/examples/minitwit/minitwit/templates/timeline.html flask-1.0.2/examples/minitwit/minitwit/templates/timeline.html --- flask-0.12.2/examples/minitwit/minitwit/templates/timeline.html 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit/templates/timeline.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -{% extends "layout.html" %} -{% block title %} - {% if request.endpoint == 'public_timeline' %} - Public Timeline - {% elif request.endpoint == 'user_timeline' %} - {{ profile_user.username }}'s Timeline - {% else %} - My Timeline - {% endif %} -{% endblock %} -{% block body %} -

{{ self.title() }}

- {% if g.user %} - {% if request.endpoint == 'user_timeline' %} -
- {% if g.user.user_id == profile_user.user_id %} - This is you! - {% elif followed %} - You are currently following this user. - Unfollow user. - {% else %} - You are not yet following this user. - . - {% endif %} -
- {% elif request.endpoint == 'timeline' %} -
-

What's on your mind {{ g.user.username }}?

-
-

-

-
- {% endif %} - {% endif %} -
    - {% for message in messages %} -
  • - {{ message.username }} - {{ message.text }} - — {{ message.pub_date|datetimeformat }} - {% else %} -

  • There's no message so far. - {% endfor %} -
-{% endblock %} diff -Nru flask-0.12.2/examples/minitwit/minitwit.egg-info/dependency_links.txt flask-1.0.2/examples/minitwit/minitwit.egg-info/dependency_links.txt --- flask-0.12.2/examples/minitwit/minitwit.egg-info/dependency_links.txt 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit.egg-info/dependency_links.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ - diff -Nru flask-0.12.2/examples/minitwit/minitwit.egg-info/PKG-INFO flask-1.0.2/examples/minitwit/minitwit.egg-info/PKG-INFO --- flask-0.12.2/examples/minitwit/minitwit.egg-info/PKG-INFO 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit.egg-info/PKG-INFO 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -Metadata-Version: 1.0 -Name: minitwit -Version: 0.0.0 -Summary: UNKNOWN -Home-page: UNKNOWN -Author: UNKNOWN -Author-email: UNKNOWN -License: UNKNOWN -Description: UNKNOWN -Platform: UNKNOWN diff -Nru flask-0.12.2/examples/minitwit/minitwit.egg-info/requires.txt flask-1.0.2/examples/minitwit/minitwit.egg-info/requires.txt --- flask-0.12.2/examples/minitwit/minitwit.egg-info/requires.txt 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit.egg-info/requires.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -flask \ No newline at end of file diff -Nru flask-0.12.2/examples/minitwit/minitwit.egg-info/SOURCES.txt flask-1.0.2/examples/minitwit/minitwit.egg-info/SOURCES.txt --- flask-0.12.2/examples/minitwit/minitwit.egg-info/SOURCES.txt 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit.egg-info/SOURCES.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -MANIFEST.in -README -setup.cfg -minitwit/__init__.py -minitwit/minitwit.py -minitwit/schema.sql -minitwit.egg-info/PKG-INFO -minitwit.egg-info/SOURCES.txt -minitwit.egg-info/dependency_links.txt -minitwit.egg-info/requires.txt -minitwit.egg-info/top_level.txt -minitwit/static/style.css -minitwit/templates/layout.html -minitwit/templates/login.html -minitwit/templates/register.html -minitwit/templates/timeline.html \ No newline at end of file diff -Nru flask-0.12.2/examples/minitwit/minitwit.egg-info/top_level.txt flask-1.0.2/examples/minitwit/minitwit.egg-info/top_level.txt --- flask-0.12.2/examples/minitwit/minitwit.egg-info/top_level.txt 2017-03-31 16:42:53.000000000 +0000 +++ flask-1.0.2/examples/minitwit/minitwit.egg-info/top_level.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -minitwit diff -Nru flask-0.12.2/examples/minitwit/README flask-1.0.2/examples/minitwit/README --- flask-0.12.2/examples/minitwit/README 2017-02-10 11:20:32.000000000 +0000 +++ flask-1.0.2/examples/minitwit/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ - - / MiniTwit / - - because writing todo lists is not fun - - - ~ What is MiniTwit? - - A SQLite and Flask powered twitter clone - - ~ How do I use it? - - 1. edit the configuration in the minitwit.py file or - export an MINITWIT_SETTINGS environment variable - pointing to a configuration file. - - 2. install the app from the root of the project directory - - pip install --editable . - - 3. tell flask about the right application: - - export FLASK_APP=minitwit - - 4. fire up a shell and run this: - - flask initdb - - 5. now you can run minitwit: - - flask run - - the application will greet you on - http://localhost:5000/ - - ~ Is it tested? - - You betcha. Run the `python setup.py test` file to - see the tests pass. diff -Nru flask-0.12.2/examples/minitwit/setup.cfg flask-1.0.2/examples/minitwit/setup.cfg --- flask-0.12.2/examples/minitwit/setup.cfg 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/setup.cfg 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -[aliases] -test=pytest diff -Nru flask-0.12.2/examples/minitwit/setup.py flask-1.0.2/examples/minitwit/setup.py --- flask-0.12.2/examples/minitwit/setup.py 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/setup.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -from setuptools import setup - -setup( - name='minitwit', - packages=['minitwit'], - include_package_data=True, - install_requires=[ - 'flask', - ], - setup_requires=[ - 'pytest-runner', - ], - tests_require=[ - 'pytest', - ], -) \ No newline at end of file diff -Nru flask-0.12.2/examples/minitwit/tests/test_minitwit.py flask-1.0.2/examples/minitwit/tests/test_minitwit.py --- flask-0.12.2/examples/minitwit/tests/test_minitwit.py 2016-09-11 15:55:14.000000000 +0000 +++ flask-1.0.2/examples/minitwit/tests/test_minitwit.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,151 +0,0 @@ -# -*- coding: utf-8 -*- -""" - MiniTwit Tests - ~~~~~~~~~~~~~~ - - Tests the MiniTwit application. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import os -import tempfile -import pytest -from minitwit import minitwit - - -@pytest.fixture -def client(request): - db_fd, minitwit.app.config['DATABASE'] = tempfile.mkstemp() - client = minitwit.app.test_client() - with minitwit.app.app_context(): - minitwit.init_db() - - def teardown(): - """Get rid of the database again after each test.""" - os.close(db_fd) - os.unlink(minitwit.app.config['DATABASE']) - request.addfinalizer(teardown) - return client - - -def register(client, username, password, password2=None, email=None): - """Helper function to register a user""" - if password2 is None: - password2 = password - if email is None: - email = username + '@example.com' - return client.post('/register', data={ - 'username': username, - 'password': password, - 'password2': password2, - 'email': email, - }, follow_redirects=True) - - -def login(client, username, password): - """Helper function to login""" - return client.post('/login', data={ - 'username': username, - 'password': password - }, follow_redirects=True) - - -def register_and_login(client, username, password): - """Registers and logs in in one go""" - register(client, username, password) - return login(client, username, password) - - -def logout(client): - """Helper function to logout""" - return client.get('/logout', follow_redirects=True) - - -def add_message(client, text): - """Records a message""" - rv = client.post('/add_message', data={'text': text}, - follow_redirects=True) - if text: - assert b'Your message was recorded' in rv.data - return rv - - -def test_register(client): - """Make sure registering works""" - rv = register(client, 'user1', 'default') - assert b'You were successfully registered ' \ - b'and can login now' in rv.data - rv = register(client, 'user1', 'default') - assert b'The username is already taken' in rv.data - rv = register(client, '', 'default') - assert b'You have to enter a username' in rv.data - rv = register(client, 'meh', '') - assert b'You have to enter a password' in rv.data - rv = register(client, 'meh', 'x', 'y') - assert b'The two passwords do not match' in rv.data - rv = register(client, 'meh', 'foo', email='broken') - assert b'You have to enter a valid email address' in rv.data - - -def test_login_logout(client): - """Make sure logging in and logging out works""" - rv = register_and_login(client, 'user1', 'default') - assert b'You were logged in' in rv.data - rv = logout(client) - assert b'You were logged out' in rv.data - rv = login(client, 'user1', 'wrongpassword') - assert b'Invalid password' in rv.data - rv = login(client, 'user2', 'wrongpassword') - assert b'Invalid username' in rv.data - - -def test_message_recording(client): - """Check if adding messages works""" - register_and_login(client, 'foo', 'default') - add_message(client, 'test message 1') - add_message(client, '') - rv = client.get('/') - assert b'test message 1' in rv.data - assert b'<test message 2>' in rv.data - - -def test_timelines(client): - """Make sure that timelines work""" - register_and_login(client, 'foo', 'default') - add_message(client, 'the message by foo') - logout(client) - register_and_login(client, 'bar', 'default') - add_message(client, 'the message by bar') - rv = client.get('/public') - assert b'the message by foo' in rv.data - assert b'the message by bar' in rv.data - - # bar's timeline should just show bar's message - rv = client.get('/') - assert b'the message by foo' not in rv.data - assert b'the message by bar' in rv.data - - # now let's follow foo - rv = client.get('/foo/follow', follow_redirects=True) - assert b'You are now following "foo"' in rv.data - - # we should now see foo's message - rv = client.get('/') - assert b'the message by foo' in rv.data - assert b'the message by bar' in rv.data - - # but on the user's page we only want the user's message - rv = client.get('/bar') - assert b'the message by foo' not in rv.data - assert b'the message by bar' in rv.data - rv = client.get('/foo') - assert b'the message by foo' in rv.data - assert b'the message by bar' not in rv.data - - # now unfollow and check if that worked - rv = client.get('/foo/unfollow', follow_redirects=True) - assert b'You are no longer following "foo"' in rv.data - rv = client.get('/') - assert b'the message by foo' not in rv.data - assert b'the message by bar' in rv.data diff -Nru flask-0.12.2/examples/tutorial/flaskr/auth.py flask-1.0.2/examples/tutorial/flaskr/auth.py --- flask-0.12.2/examples/tutorial/flaskr/auth.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/auth.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,108 @@ +import functools + +from flask import ( + Blueprint, flash, g, redirect, render_template, request, session, url_for +) +from werkzeug.security import check_password_hash, generate_password_hash + +from flaskr.db import get_db + +bp = Blueprint('auth', __name__, url_prefix='/auth') + + +def login_required(view): + """View decorator that redirects anonymous users to the login page.""" + @functools.wraps(view) + def wrapped_view(**kwargs): + if g.user is None: + return redirect(url_for('auth.login')) + + return view(**kwargs) + + return wrapped_view + + +@bp.before_app_request +def load_logged_in_user(): + """If a user id is stored in the session, load the user object from + the database into ``g.user``.""" + user_id = session.get('user_id') + + if user_id is None: + g.user = None + else: + g.user = get_db().execute( + 'SELECT * FROM user WHERE id = ?', (user_id,) + ).fetchone() + + +@bp.route('/register', methods=('GET', 'POST')) +def register(): + """Register a new user. + + Validates that the username is not already taken. Hashes the + password for security. + """ + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + db = get_db() + error = None + + if not username: + error = 'Username is required.' + elif not password: + error = 'Password is required.' + elif db.execute( + 'SELECT id FROM user WHERE username = ?', (username,) + ).fetchone() is not None: + error = 'User {0} is already registered.'.format(username) + + if error is None: + # the name is available, store it in the database and go to + # the login page + db.execute( + 'INSERT INTO user (username, password) VALUES (?, ?)', + (username, generate_password_hash(password)) + ) + db.commit() + return redirect(url_for('auth.login')) + + flash(error) + + return render_template('auth/register.html') + + +@bp.route('/login', methods=('GET', 'POST')) +def login(): + """Log in a registered user by adding the user id to the session.""" + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + db = get_db() + error = None + user = db.execute( + 'SELECT * FROM user WHERE username = ?', (username,) + ).fetchone() + + if user is None: + error = 'Incorrect username.' + elif not check_password_hash(user['password'], password): + error = 'Incorrect password.' + + if error is None: + # store the user id in a new session and return to the index + session.clear() + session['user_id'] = user['id'] + return redirect(url_for('index')) + + flash(error) + + return render_template('auth/login.html') + + +@bp.route('/logout') +def logout(): + """Clear the current session, including the stored user id.""" + session.clear() + return redirect(url_for('index')) diff -Nru flask-0.12.2/examples/tutorial/flaskr/blog.py flask-1.0.2/examples/tutorial/flaskr/blog.py --- flask-0.12.2/examples/tutorial/flaskr/blog.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/blog.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,119 @@ +from flask import ( + Blueprint, flash, g, redirect, render_template, request, url_for +) +from werkzeug.exceptions import abort + +from flaskr.auth import login_required +from flaskr.db import get_db + +bp = Blueprint('blog', __name__) + + +@bp.route('/') +def index(): + """Show all the posts, most recent first.""" + db = get_db() + posts = db.execute( + 'SELECT p.id, title, body, created, author_id, username' + ' FROM post p JOIN user u ON p.author_id = u.id' + ' ORDER BY created DESC' + ).fetchall() + return render_template('blog/index.html', posts=posts) + + +def get_post(id, check_author=True): + """Get a post and its author by id. + + Checks that the id exists and optionally that the current user is + the author. + + :param id: id of post to get + :param check_author: require the current user to be the author + :return: the post with author information + :raise 404: if a post with the given id doesn't exist + :raise 403: if the current user isn't the author + """ + post = get_db().execute( + 'SELECT p.id, title, body, created, author_id, username' + ' FROM post p JOIN user u ON p.author_id = u.id' + ' WHERE p.id = ?', + (id,) + ).fetchone() + + if post is None: + abort(404, "Post id {0} doesn't exist.".format(id)) + + if check_author and post['author_id'] != g.user['id']: + abort(403) + + return post + + +@bp.route('/create', methods=('GET', 'POST')) +@login_required +def create(): + """Create a new post for the current user.""" + if request.method == 'POST': + title = request.form['title'] + body = request.form['body'] + error = None + + if not title: + error = 'Title is required.' + + if error is not None: + flash(error) + else: + db = get_db() + db.execute( + 'INSERT INTO post (title, body, author_id)' + ' VALUES (?, ?, ?)', + (title, body, g.user['id']) + ) + db.commit() + return redirect(url_for('blog.index')) + + return render_template('blog/create.html') + + +@bp.route('//update', methods=('GET', 'POST')) +@login_required +def update(id): + """Update a post if the current user is the author.""" + post = get_post(id) + + if request.method == 'POST': + title = request.form['title'] + body = request.form['body'] + error = None + + if not title: + error = 'Title is required.' + + if error is not None: + flash(error) + else: + db = get_db() + db.execute( + 'UPDATE post SET title = ?, body = ? WHERE id = ?', + (title, body, id) + ) + db.commit() + return redirect(url_for('blog.index')) + + return render_template('blog/update.html', post=post) + + +@bp.route('//delete', methods=('POST',)) +@login_required +def delete(id): + """Delete a post. + + Ensures that the post exists and that the logged in user is the + author of the post. + """ + get_post(id) + db = get_db() + db.execute('DELETE FROM post WHERE id = ?', (id,)) + db.commit() + return redirect(url_for('blog.index')) diff -Nru flask-0.12.2/examples/tutorial/flaskr/db.py flask-1.0.2/examples/tutorial/flaskr/db.py --- flask-0.12.2/examples/tutorial/flaskr/db.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/db.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,54 @@ +import sqlite3 + +import click +from flask import current_app, g +from flask.cli import with_appcontext + + +def get_db(): + """Connect to the application's configured database. The connection + is unique for each request and will be reused if this is called + again. + """ + if 'db' not in g: + g.db = sqlite3.connect( + current_app.config['DATABASE'], + detect_types=sqlite3.PARSE_DECLTYPES + ) + g.db.row_factory = sqlite3.Row + + return g.db + + +def close_db(e=None): + """If this request connected to the database, close the + connection. + """ + db = g.pop('db', None) + + if db is not None: + db.close() + + +def init_db(): + """Clear existing data and create new tables.""" + db = get_db() + + with current_app.open_resource('schema.sql') as f: + db.executescript(f.read().decode('utf8')) + + +@click.command('init-db') +@with_appcontext +def init_db_command(): + """Clear existing data and create new tables.""" + init_db() + click.echo('Initialized the database.') + + +def init_app(app): + """Register database functions with the Flask app. This is called by + the application factory. + """ + app.teardown_appcontext(close_db) + app.cli.add_command(init_db_command) diff -Nru flask-0.12.2/examples/tutorial/flaskr/__init__.py flask-1.0.2/examples/tutorial/flaskr/__init__.py --- flask-0.12.2/examples/tutorial/flaskr/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/__init__.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,48 @@ +import os + +from flask import Flask + + +def create_app(test_config=None): + """Create and configure an instance of the Flask application.""" + app = Flask(__name__, instance_relative_config=True) + app.config.from_mapping( + # a default secret that should be overridden by instance config + SECRET_KEY='dev', + # store the database in the instance folder + DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'), + ) + + if test_config is None: + # load the instance config, if it exists, when not testing + app.config.from_pyfile('config.py', silent=True) + else: + # load the test config if passed in + app.config.update(test_config) + + # ensure the instance folder exists + try: + os.makedirs(app.instance_path) + except OSError: + pass + + @app.route('/hello') + def hello(): + return 'Hello, World!' + + # register the database commands + from flaskr import db + db.init_app(app) + + # apply the blueprints to the app + from flaskr import auth, blog + app.register_blueprint(auth.bp) + app.register_blueprint(blog.bp) + + # make url_for('index') == url_for('blog.index') + # in another app, you might define a separate main index here with + # app.route, while giving the blog blueprint a url_prefix, but for + # the tutorial the blog will be the main index + app.add_url_rule('/', endpoint='index') + + return app diff -Nru flask-0.12.2/examples/tutorial/flaskr/schema.sql flask-1.0.2/examples/tutorial/flaskr/schema.sql --- flask-0.12.2/examples/tutorial/flaskr/schema.sql 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/schema.sql 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,20 @@ +-- Initialize the database. +-- Drop any existing data and create empty tables. + +DROP TABLE IF EXISTS user; +DROP TABLE IF EXISTS post; + +CREATE TABLE user ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password TEXT NOT NULL +); + +CREATE TABLE post ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + author_id INTEGER NOT NULL, + created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + title TEXT NOT NULL, + body TEXT NOT NULL, + FOREIGN KEY (author_id) REFERENCES user (id) +); diff -Nru flask-0.12.2/examples/tutorial/flaskr/static/style.css flask-1.0.2/examples/tutorial/flaskr/static/style.css --- flask-0.12.2/examples/tutorial/flaskr/static/style.css 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/static/style.css 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,134 @@ +html { + font-family: sans-serif; + background: #eee; + padding: 1rem; +} + +body { + max-width: 960px; + margin: 0 auto; + background: white; +} + +h1, h2, h3, h4, h5, h6 { + font-family: serif; + color: #377ba8; + margin: 1rem 0; +} + +a { + color: #377ba8; +} + +hr { + border: none; + border-top: 1px solid lightgray; +} + +nav { + background: lightgray; + display: flex; + align-items: center; + padding: 0 0.5rem; +} + +nav h1 { + flex: auto; + margin: 0; +} + +nav h1 a { + text-decoration: none; + padding: 0.25rem 0.5rem; +} + +nav ul { + display: flex; + list-style: none; + margin: 0; + padding: 0; +} + +nav ul li a, nav ul li span, header .action { + display: block; + padding: 0.5rem; +} + +.content { + padding: 0 1rem 1rem; +} + +.content > header { + border-bottom: 1px solid lightgray; + display: flex; + align-items: flex-end; +} + +.content > header h1 { + flex: auto; + margin: 1rem 0 0.25rem 0; +} + +.flash { + margin: 1em 0; + padding: 1em; + background: #cae6f6; + border: 1px solid #377ba8; +} + +.post > header { + display: flex; + align-items: flex-end; + font-size: 0.85em; +} + +.post > header > div:first-of-type { + flex: auto; +} + +.post > header h1 { + font-size: 1.5em; + margin-bottom: 0; +} + +.post .about { + color: slategray; + font-style: italic; +} + +.post .body { + white-space: pre-line; +} + +.content:last-child { + margin-bottom: 0; +} + +.content form { + margin: 1em 0; + display: flex; + flex-direction: column; +} + +.content label { + font-weight: bold; + margin-bottom: 0.5em; +} + +.content input, .content textarea { + margin-bottom: 1em; +} + +.content textarea { + min-height: 12em; + resize: vertical; +} + +input.danger { + color: #cc2f2e; +} + +input[type=submit] { + align-self: start; + min-width: 10em; +} diff -Nru flask-0.12.2/examples/tutorial/flaskr/templates/auth/login.html flask-1.0.2/examples/tutorial/flaskr/templates/auth/login.html --- flask-0.12.2/examples/tutorial/flaskr/templates/auth/login.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/templates/auth/login.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,15 @@ +{% extends 'base.html' %} + +{% block header %} +

{% block title %}Log In{% endblock %}

+{% endblock %} + +{% block content %} +
+ + + + + +
+{% endblock %} diff -Nru flask-0.12.2/examples/tutorial/flaskr/templates/auth/register.html flask-1.0.2/examples/tutorial/flaskr/templates/auth/register.html --- flask-0.12.2/examples/tutorial/flaskr/templates/auth/register.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/templates/auth/register.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,15 @@ +{% extends 'base.html' %} + +{% block header %} +

{% block title %}Register{% endblock %}

+{% endblock %} + +{% block content %} +
+ + + + + +
+{% endblock %} diff -Nru flask-0.12.2/examples/tutorial/flaskr/templates/base.html flask-1.0.2/examples/tutorial/flaskr/templates/base.html --- flask-0.12.2/examples/tutorial/flaskr/templates/base.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/templates/base.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,24 @@ + +{% block title %}{% endblock %} - Flaskr + + +
+
+ {% block header %}{% endblock %} +
+ {% for message in get_flashed_messages() %} +
{{ message }}
+ {% endfor %} + {% block content %}{% endblock %} +
diff -Nru flask-0.12.2/examples/tutorial/flaskr/templates/blog/create.html flask-1.0.2/examples/tutorial/flaskr/templates/blog/create.html --- flask-0.12.2/examples/tutorial/flaskr/templates/blog/create.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/templates/blog/create.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,15 @@ +{% extends 'base.html' %} + +{% block header %} +

{% block title %}New Post{% endblock %}

+{% endblock %} + +{% block content %} +
+ + + + + +
+{% endblock %} diff -Nru flask-0.12.2/examples/tutorial/flaskr/templates/blog/index.html flask-1.0.2/examples/tutorial/flaskr/templates/blog/index.html --- flask-0.12.2/examples/tutorial/flaskr/templates/blog/index.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/templates/blog/index.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,28 @@ +{% extends 'base.html' %} + +{% block header %} +

{% block title %}Posts{% endblock %}

+ {% if g.user %} + New + {% endif %} +{% endblock %} + +{% block content %} + {% for post in posts %} +
+
+
+

{{ post['title'] }}

+
by {{ post['username'] }} on {{ post['created'].strftime('%Y-%m-%d') }}
+
+ {% if g.user['id'] == post['author_id'] %} + Edit + {% endif %} +
+

{{ post['body'] }}

+
+ {% if not loop.last %} +
+ {% endif %} + {% endfor %} +{% endblock %} diff -Nru flask-0.12.2/examples/tutorial/flaskr/templates/blog/update.html flask-1.0.2/examples/tutorial/flaskr/templates/blog/update.html --- flask-0.12.2/examples/tutorial/flaskr/templates/blog/update.html 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/flaskr/templates/blog/update.html 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,19 @@ +{% extends 'base.html' %} + +{% block header %} +

{% block title %}Edit "{{ post['title'] }}"{% endblock %}

+{% endblock %} + +{% block content %} +
+ + + + + +
+
+
+ +
+{% endblock %} diff -Nru flask-0.12.2/examples/tutorial/.gitignore flask-1.0.2/examples/tutorial/.gitignore --- flask-0.12.2/examples/tutorial/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/.gitignore 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,14 @@ +venv/ +*.pyc +__pycache__/ +instance/ +.cache/ +.pytest_cache/ +.coverage +htmlcov/ +dist/ +build/ +*.egg-info/ +.idea/ +*.swp +*~ diff -Nru flask-0.12.2/examples/tutorial/LICENSE flask-1.0.2/examples/tutorial/LICENSE --- flask-0.12.2/examples/tutorial/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/LICENSE 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,31 @@ +Copyright © 2010 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as +well as documentation, with or without modification, are permitted +provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff -Nru flask-0.12.2/examples/tutorial/MANIFEST.in flask-1.0.2/examples/tutorial/MANIFEST.in --- flask-0.12.2/examples/tutorial/MANIFEST.in 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/MANIFEST.in 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,6 @@ +include LICENSE +include flaskr/schema.sql +graft flaskr/static +graft flaskr/templates +graft tests +global-exclude *.pyc diff -Nru flask-0.12.2/examples/tutorial/README.rst flask-1.0.2/examples/tutorial/README.rst --- flask-0.12.2/examples/tutorial/README.rst 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/README.rst 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,75 @@ +Flaskr +====== + +The basic blog app built in the Flask `tutorial`_. + +.. _tutorial: http://flask.pocoo.org/docs/tutorial/ + + +Install +------- + +**Be sure to use the same version of the code as the version of the docs +you're reading.** You probably want the latest tagged version, but the +default Git version is the master branch. :: + + # clone the repository + git clone https://github.com/pallets/flask + cd flask + # checkout the correct version + git tag # shows the tagged versions + git checkout latest-tag-found-above + cd examples/tutorial + +Create a virtualenv and activate it:: + + python3 -m venv venv + . venv/bin/activate + +Or on Windows cmd:: + + py -3 -m venv venv + venv\Scripts\activate.bat + +Install Flaskr:: + + pip install -e . + +Or if you are using the master branch, install Flask from source before +installing Flaskr:: + + pip install -e ../.. + pip install -e . + + +Run +--- + +:: + + export FLASK_APP=flaskr + export FLASK_ENV=development + flask run + +Or on Windows cmd:: + + set FLASK_APP=flaskr + set FLASK_ENV=development + flask run + +Open http://127.0.0.1:5000 in a browser. + + +Test +---- + +:: + + pip install '.[test]' + pytest + +Run with coverage report:: + + coverage run -m pytest + coverage report + coverage html # open htmlcov/index.html in a browser diff -Nru flask-0.12.2/examples/tutorial/setup.cfg flask-1.0.2/examples/tutorial/setup.cfg --- flask-0.12.2/examples/tutorial/setup.cfg 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/setup.cfg 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,13 @@ +[metadata] +license_file = LICENSE + +[bdist_wheel] +universal = True + +[tool:pytest] +testpaths = tests + +[coverage:run] +branch = True +source = + flaskr diff -Nru flask-0.12.2/examples/tutorial/setup.py flask-1.0.2/examples/tutorial/setup.py --- flask-0.12.2/examples/tutorial/setup.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/setup.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,29 @@ +import io + +from setuptools import find_packages, setup + +with io.open('README.rst', 'rt', encoding='utf8') as f: + readme = f.read() + +setup( + name='flaskr', + version='1.0.0', + url='http://flask.pocoo.org/docs/tutorial/', + license='BSD', + maintainer='Pallets team', + maintainer_email='contact@palletsprojects.com', + description='The basic blog app built in the Flask tutorial.', + long_description=readme, + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=[ + 'flask', + ], + extras_require={ + 'test': [ + 'pytest', + 'coverage', + ], + }, +) diff -Nru flask-0.12.2/examples/tutorial/tests/conftest.py flask-1.0.2/examples/tutorial/tests/conftest.py --- flask-0.12.2/examples/tutorial/tests/conftest.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/tests/conftest.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,64 @@ +import os +import tempfile + +import pytest +from flaskr import create_app +from flaskr.db import get_db, init_db + +# read in SQL for populating test data +with open(os.path.join(os.path.dirname(__file__), 'data.sql'), 'rb') as f: + _data_sql = f.read().decode('utf8') + + +@pytest.fixture +def app(): + """Create and configure a new app instance for each test.""" + # create a temporary file to isolate the database for each test + db_fd, db_path = tempfile.mkstemp() + # create the app with common test config + app = create_app({ + 'TESTING': True, + 'DATABASE': db_path, + }) + + # create the database and load test data + with app.app_context(): + init_db() + get_db().executescript(_data_sql) + + yield app + + # close and remove the temporary database + os.close(db_fd) + os.unlink(db_path) + + +@pytest.fixture +def client(app): + """A test client for the app.""" + return app.test_client() + + +@pytest.fixture +def runner(app): + """A test runner for the app's Click commands.""" + return app.test_cli_runner() + + +class AuthActions(object): + def __init__(self, client): + self._client = client + + def login(self, username='test', password='test'): + return self._client.post( + '/auth/login', + data={'username': username, 'password': password} + ) + + def logout(self): + return self._client.get('/auth/logout') + + +@pytest.fixture +def auth(client): + return AuthActions(client) diff -Nru flask-0.12.2/examples/tutorial/tests/data.sql flask-1.0.2/examples/tutorial/tests/data.sql --- flask-0.12.2/examples/tutorial/tests/data.sql 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/tests/data.sql 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,8 @@ +INSERT INTO user (username, password) +VALUES + ('test', 'pbkdf2:sha256:50000$TCI4GzcX$0de171a4f4dac32e3364c7ddc7c14f3e2fa61f2d17574483f7ffbb431b4acb2f'), + ('other', 'pbkdf2:sha256:50000$kJPKsz6N$d2d4784f1b030a9761f5ccaeeaca413f27f2ecb76d6168407af962ddce849f79'); + +INSERT INTO post (title, body, author_id, created) +VALUES + ('test title', 'test' || x'0a' || 'body', 1, '2018-01-01 00:00:00'); diff -Nru flask-0.12.2/examples/tutorial/tests/test_auth.py flask-1.0.2/examples/tutorial/tests/test_auth.py --- flask-0.12.2/examples/tutorial/tests/test_auth.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/tests/test_auth.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,66 @@ +import pytest +from flask import g, session +from flaskr.db import get_db + + +def test_register(client, app): + # test that viewing the page renders without template errors + assert client.get('/auth/register').status_code == 200 + + # test that successful registration redirects to the login page + response = client.post( + '/auth/register', data={'username': 'a', 'password': 'a'} + ) + assert 'http://localhost/auth/login' == response.headers['Location'] + + # test that the user was inserted into the database + with app.app_context(): + assert get_db().execute( + "select * from user where username = 'a'", + ).fetchone() is not None + + +@pytest.mark.parametrize(('username', 'password', 'message'), ( + ('', '', b'Username is required.'), + ('a', '', b'Password is required.'), + ('test', 'test', b'already registered'), +)) +def test_register_validate_input(client, username, password, message): + response = client.post( + '/auth/register', + data={'username': username, 'password': password} + ) + assert message in response.data + + +def test_login(client, auth): + # test that viewing the page renders without template errors + assert client.get('/auth/login').status_code == 200 + + # test that successful login redirects to the index page + response = auth.login() + assert response.headers['Location'] == 'http://localhost/' + + # login request set the user_id in the session + # check that the user is loaded from the session + with client: + client.get('/') + assert session['user_id'] == 1 + assert g.user['username'] == 'test' + + +@pytest.mark.parametrize(('username', 'password', 'message'), ( + ('a', 'test', b'Incorrect username.'), + ('test', 'a', b'Incorrect password.'), +)) +def test_login_validate_input(auth, username, password, message): + response = auth.login(username, password) + assert message in response.data + + +def test_logout(client, auth): + auth.login() + + with client: + auth.logout() + assert 'user_id' not in session diff -Nru flask-0.12.2/examples/tutorial/tests/test_blog.py flask-1.0.2/examples/tutorial/tests/test_blog.py --- flask-0.12.2/examples/tutorial/tests/test_blog.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/tests/test_blog.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,92 @@ +import pytest +from flaskr.db import get_db + + +def test_index(client, auth): + response = client.get('/') + assert b"Log In" in response.data + assert b"Register" in response.data + + auth.login() + response = client.get('/') + assert b'test title' in response.data + assert b'by test on 2018-01-01' in response.data + assert b'test\nbody' in response.data + assert b'href="/1/update"' in response.data + + +@pytest.mark.parametrize('path', ( + '/create', + '/1/update', + '/1/delete', +)) +def test_login_required(client, path): + response = client.post(path) + assert response.headers['Location'] == 'http://localhost/auth/login' + + +def test_author_required(app, client, auth): + # change the post author to another user + with app.app_context(): + db = get_db() + db.execute('UPDATE post SET author_id = 2 WHERE id = 1') + db.commit() + + auth.login() + # current user can't modify other user's post + assert client.post('/1/update').status_code == 403 + assert client.post('/1/delete').status_code == 403 + # current user doesn't see edit link + assert b'href="/1/update"' not in client.get('/').data + + +@pytest.mark.parametrize('path', ( + '/2/update', + '/2/delete', +)) +def test_exists_required(client, auth, path): + auth.login() + assert client.post(path).status_code == 404 + + +def test_create(client, auth, app): + auth.login() + assert client.get('/create').status_code == 200 + client.post('/create', data={'title': 'created', 'body': ''}) + + with app.app_context(): + db = get_db() + count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0] + assert count == 2 + + +def test_update(client, auth, app): + auth.login() + assert client.get('/1/update').status_code == 200 + client.post('/1/update', data={'title': 'updated', 'body': ''}) + + with app.app_context(): + db = get_db() + post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() + assert post['title'] == 'updated' + + +@pytest.mark.parametrize('path', ( + '/create', + '/1/update', +)) +def test_create_update_validate(client, auth, path): + auth.login() + response = client.post(path, data={'title': '', 'body': ''}) + assert b'Title is required.' in response.data + + +def test_delete(client, auth, app): + auth.login() + response = client.post('/1/delete') + assert response.headers['Location'] == 'http://localhost/' + + with app.app_context(): + db = get_db() + post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() + assert post is None diff -Nru flask-0.12.2/examples/tutorial/tests/test_db.py flask-1.0.2/examples/tutorial/tests/test_db.py --- flask-0.12.2/examples/tutorial/tests/test_db.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/tests/test_db.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,28 @@ +import sqlite3 + +import pytest +from flaskr.db import get_db + + +def test_get_close_db(app): + with app.app_context(): + db = get_db() + assert db is get_db() + + with pytest.raises(sqlite3.ProgrammingError) as e: + db.execute('SELECT 1') + + assert 'closed' in str(e) + + +def test_init_db_command(runner, monkeypatch): + class Recorder(object): + called = False + + def fake_init_db(): + Recorder.called = True + + monkeypatch.setattr('flaskr.db.init_db', fake_init_db) + result = runner.invoke(args=['init-db']) + assert 'Initialized' in result.output + assert Recorder.called diff -Nru flask-0.12.2/examples/tutorial/tests/test_factory.py flask-1.0.2/examples/tutorial/tests/test_factory.py --- flask-0.12.2/examples/tutorial/tests/test_factory.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/examples/tutorial/tests/test_factory.py 2018-05-02 00:50:32.000000000 +0000 @@ -0,0 +1,12 @@ +from flaskr import create_app + + +def test_config(): + """Test create_app without passing test config.""" + assert not create_app().testing + assert create_app({'TESTING': True}).testing + + +def test_hello(client): + response = client.get('/hello') + assert response.data == b'Hello, World!' diff -Nru flask-0.12.2/flask/app.py flask-1.0.2/flask/app.py --- flask-0.12.2/flask/app.py 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/flask/app.py 2018-05-02 00:50:32.000000000 +0000 @@ -5,38 +5,40 @@ This module implements the central WSGI application object. - :copyright: (c) 2015 by Armin Ronacher. + :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ + import os import sys -from threading import Lock +import warnings from datetime import timedelta -from itertools import chain from functools import update_wrapper +from itertools import chain +from threading import Lock -from werkzeug.datastructures import ImmutableDict -from werkzeug.routing import Map, Rule, RequestRedirect, BuildError -from werkzeug.exceptions import HTTPException, InternalServerError, \ - MethodNotAllowed, BadRequest, default_exceptions - -from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \ - locked_cached_property, _endpoint_from_view_func, find_package, \ - get_debug_flag -from . import json, cli -from .wrappers import Request, Response -from .config import ConfigAttribute, Config -from .ctx import RequestContext, AppContext, _AppCtxGlobals -from .globals import _request_ctx_stack, request, session, g +from werkzeug.datastructures import Headers, ImmutableDict +from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \ + InternalServerError, MethodNotAllowed, default_exceptions +from werkzeug.routing import BuildError, Map, RequestRedirect, Rule + +from . import cli, json +from ._compat import integer_types, reraise, string_types, text_type +from .config import Config, ConfigAttribute +from .ctx import AppContext, RequestContext, _AppCtxGlobals +from .globals import _request_ctx_stack, g, request, session +from .helpers import ( + _PackageBoundObject, + _endpoint_from_view_func, find_package, get_env, get_debug_flag, + get_flashed_messages, locked_cached_property, url_for, get_load_dotenv +) +from .logging import create_logger from .sessions import SecureCookieSessionInterface +from .signals import appcontext_tearing_down, got_request_exception, \ + request_finished, request_started, request_tearing_down from .templating import DispatchingJinjaLoader, Environment, \ - _default_template_ctx_processor -from .signals import request_started, request_finished, got_request_exception, \ - request_tearing_down, appcontext_tearing_down -from ._compat import reraise, string_types, text_type, integer_types - -# a lock used for logger initialization -_logger_lock = Lock() + _default_template_ctx_processor +from .wrappers import Request, Response # a singleton sentinel value for parameter defaults _sentinel = object() @@ -123,6 +125,14 @@ .. versionadded:: 0.11 The `root_path` parameter was added. + .. versionadded:: 1.0 + The ``host_matching`` and ``static_host`` parameters were added. + + .. versionadded:: 1.0 + The ``subdomain_matching`` parameter was added. Subdomain + matching needs to be enabled manually now. Setting + :data:`SERVER_NAME` does not implicitly enable it. + :param import_name: the name of the application package :param static_url_path: can be used to specify a different path for the static files on the web. Defaults to the name @@ -130,6 +140,13 @@ :param static_folder: the folder with static files that should be served at `static_url_path`. Defaults to the ``'static'`` folder in the root path of the application. + :param static_host: the host to use when adding the static route. + Defaults to None. Required when using ``host_matching=True`` + with a ``static_folder`` configured. + :param host_matching: set ``url_map.host_matching`` attribute. + Defaults to False. + :param subdomain_matching: consider the subdomain relative to + :data:`SERVER_NAME` when matching routes. Defaults to False. :param template_folder: the folder that contains the templates that should be used by the application. Defaults to ``'templates'`` folder in the root path of the @@ -178,18 +195,6 @@ #: .. versionadded:: 0.10 app_ctx_globals_class = _AppCtxGlobals - # Backwards compatibility support - def _get_request_globals_class(self): - return self.app_ctx_globals_class - def _set_request_globals_class(self, value): - from warnings import warn - warn(DeprecationWarning('request_globals_class attribute is now ' - 'called app_ctx_globals_class')) - self.app_ctx_globals_class = value - request_globals_class = property(_get_request_globals_class, - _set_request_globals_class) - del _get_request_globals_class, _set_request_globals_class - #: The class that is used for the ``config`` attribute of this app. #: Defaults to :class:`~flask.Config`. #: @@ -201,18 +206,9 @@ #: .. versionadded:: 0.11 config_class = Config - #: The debug flag. Set this to ``True`` to enable debugging of the - #: application. In debug mode the debugger will kick in when an unhandled - #: exception occurs and the integrated server will automatically reload - #: the application if changes in the code are detected. - #: - #: This attribute can also be configured from the config with the ``DEBUG`` - #: configuration key. Defaults to ``False``. - debug = ConfigAttribute('DEBUG') - #: The testing flag. Set this to ``True`` to enable the test mode of #: Flask extensions (and in the future probably also Flask itself). - #: For example this might activate unittest helpers that have an + #: For example this might activate test helpers that have an #: additional runtime cost which should not be enabled by default. #: #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the @@ -223,11 +219,11 @@ testing = ConfigAttribute('TESTING') #: If a secret key is set, cryptographic components can use this to - #: sign cookies and other things. Set this to a complex random value + #: sign cookies and other things. Set this to a complex random value #: when you want to use the secure cookie for instance. #: #: This attribute can also be configured from the config with the - #: ``SECRET_KEY`` configuration key. Defaults to ``None``. + #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. secret_key = ConfigAttribute('SECRET_KEY') #: The secure cookie uses this for the name of the session cookie. @@ -266,12 +262,6 @@ #: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``. use_x_sendfile = ConfigAttribute('USE_X_SENDFILE') - #: The name of the logger to use. By default the logger name is the - #: package name passed to the constructor. - #: - #: .. versionadded:: 0.4 - logger_name = ConfigAttribute('LOGGER_NAME') - #: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`. #: #: .. versionadded:: 0.10 @@ -289,34 +279,35 @@ #: Default configuration parameters. default_config = ImmutableDict({ - 'DEBUG': get_debug_flag(default=False), + 'ENV': None, + 'DEBUG': None, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 'USE_X_SENDFILE': False, - 'LOGGER_NAME': None, - 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, - 'APPLICATION_ROOT': None, + 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, + 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), - 'TRAP_BAD_REQUEST_ERRORS': False, + 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, - 'JSONIFY_PRETTYPRINT_REGULAR': True, + 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, + 'MAX_COOKIE_SIZE': 4093, }) #: The rule object to use for URL rules created. This is used by @@ -330,34 +321,68 @@ #: .. versionadded:: 0.7 test_client_class = None + #: The :class:`~click.testing.CliRunner` subclass, by default + #: :class:`~flask.testing.FlaskCliRunner` that is used by + #: :meth:`test_cli_runner`. Its ``__init__`` method should take a + #: Flask app object as the first argument. + #: + #: .. versionadded:: 1.0 + test_cli_runner_class = None + #: the session interface to use. By default an instance of #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. #: #: .. versionadded:: 0.8 session_interface = SecureCookieSessionInterface() - def __init__(self, import_name, static_path=None, static_url_path=None, - static_folder='static', template_folder='templates', - instance_path=None, instance_relative_config=False, - root_path=None): - _PackageBoundObject.__init__(self, import_name, - template_folder=template_folder, - root_path=root_path) - if static_path is not None: - from warnings import warn - warn(DeprecationWarning('static_path is now called ' - 'static_url_path'), stacklevel=2) - static_url_path = static_path + # 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, + import_name, + static_url_path=None, + static_folder='static', + static_host=None, + host_matching=False, + subdomain_matching=False, + template_folder='templates', + instance_path=None, + instance_relative_config=False, + root_path=None + ): + _PackageBoundObject.__init__( + self, + import_name, + template_folder=template_folder, + root_path=root_path + ) if static_url_path is not None: self.static_url_path = static_url_path + if static_folder is not None: self.static_folder = static_folder + if instance_path is None: instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): - raise ValueError('If an instance path is provided it must be ' - 'absolute. A relative path was given instead.') + raise ValueError( + 'If an instance path is provided it must be absolute.' + ' A relative path was given instead.' + ) #: Holds the path to the instance folder. #: @@ -369,20 +394,12 @@ #: to load a config from files. self.config = self.make_config(instance_relative_config) - # Prepare the deferred setup of the logger. - self._logger = None - self.logger_name = self.import_name - #: A dictionary of all view functions registered. The keys will #: be function names which are also used to generate URLs and #: the values are the function objects themselves. #: To register a view function, use the :meth:`route` decorator. self.view_functions = {} - # support for the now deprecated `error_handlers` attribute. The - # :attr:`error_handler_spec` shall be used now. - self._error_handlers = {} - #: A dictionary of all registered error handlers. The key is ``None`` #: for error handlers active on the application, otherwise the key is #: the name of the blueprint. Each key points to another dictionary @@ -391,9 +408,9 @@ #: is the class for the instance check and the second the error handler #: function. #: - #: To register a error handler, use the :meth:`errorhandler` + #: To register an error handler, use the :meth:`errorhandler` #: decorator. - self.error_handler_spec = {None: self._error_handlers} + self.error_handler_spec = {} #: A list of functions that are called when :meth:`url_for` raises a #: :exc:`~werkzeug.routing.BuildError`. Each function registered here @@ -404,17 +421,16 @@ #: .. versionadded:: 0.9 self.url_build_error_handlers = [] - #: A dictionary with lists of functions that should be called at the - #: beginning of the request. The key of the dictionary is the name of - #: the blueprint this function is active for, ``None`` for all requests. - #: This can for example be used to open database connections or - #: getting hold of the currently logged in user. To register a - #: function here, use the :meth:`before_request` decorator. + #: A dictionary with lists of functions that will be called at the + #: beginning of each request. The key of the dictionary is the name of + #: the blueprint this function is active for, or ``None`` for all + #: requests. To register a function, use the :meth:`before_request` + #: decorator. self.before_request_funcs = {} - #: A lists of functions that should be called at the beginning of the - #: first request to this instance. To register a function here, use - #: the :meth:`before_first_request` decorator. + #: A list of functions that will be called at the beginning of the + #: first request to this instance. To register a function, use the + #: :meth:`before_first_request` decorator. #: #: .. versionadded:: 0.8 self.before_first_request_funcs = [] @@ -446,12 +462,11 @@ #: .. versionadded:: 0.9 self.teardown_appcontext_funcs = [] - #: A dictionary with lists of functions that can be used as URL - #: value processor functions. Whenever a URL is built these functions - #: are called to modify the dictionary of values in place. The key - #: ``None`` here is used for application wide - #: callbacks, otherwise the key is the name of the blueprint. - #: Each of these functions has the chance to modify the dictionary + #: A dictionary with lists of functions that are called before the + #: :attr:`before_request_funcs` functions. The key of the dictionary is + #: the name of the blueprint this function is active for, or ``None`` + #: for all requests. To register a function, use + #: :meth:`url_value_preprocessor`. #: #: .. versionadded:: 0.7 self.url_value_preprocessors = {} @@ -525,20 +540,27 @@ #: app.url_map.converters['list'] = ListConverter self.url_map = Map() + self.url_map.host_matching = host_matching + self.subdomain_matching = subdomain_matching + # tracks internally if the application already handled at least one # request. self._got_first_request = False self._before_request_lock = Lock() - # register the static folder for the application. Do that even - # if the folder does not exist. First of all it might be created - # while the server is running (usually happens during development) - # but also because google appengine stores static files somewhere - # else when mapped with the .yml file. + # Add a static route using the provided static_url_path, static_host, + # and static_folder if there is a configured static_folder. + # Note we do this without checking if static_folder exists. + # For one, it might be created while the server is running (e.g. during + # development). Also, Google App Engine stores static files somewhere if self.has_static_folder: - self.add_url_rule(self.static_url_path + '/', - endpoint='static', - view_func=self.send_static_file) + assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination' + self.add_url_rule( + self.static_url_path + '/', + endpoint='static', + host=static_host, + view_func=self.send_static_file + ) #: The click command line context for this application. Commands #: registered here show up in the :command:`flask` command once the @@ -548,17 +570,6 @@ #: This is an instance of a :class:`click.Group` object. self.cli = cli.AppGroup(self.name) - def _get_error_handlers(self): - from warnings import warn - warn(DeprecationWarning('error_handlers is deprecated, use the ' - 'new error_handler_spec attribute instead.'), stacklevel=1) - return self._error_handlers - def _set_error_handlers(self, value): - self._error_handlers = value - self.error_handler_spec[None] = value - error_handlers = property(_get_error_handlers, _set_error_handlers) - del _get_error_handlers, _set_error_handlers - @locked_cached_property def name(self): """The name of the application. This is usually the import name @@ -601,27 +612,28 @@ return rv return self.debug - @property + @locked_cached_property def logger(self): - """A :class:`logging.Logger` object for this application. The - default configuration is to log to stderr if the application is - in debug mode. This logger can be used to (surprise) log messages. - Here some examples:: - - app.logger.debug('A value for debugging') - app.logger.warning('A warning occurred (%d apples)', 42) - app.logger.error('An error occurred') + """The ``'flask.app'`` logger, a standard Python + :class:`~logging.Logger`. + + In debug mode, the logger's :attr:`~logging.Logger.level` will be set + to :data:`~logging.DEBUG`. + + If there are no handlers configured, a default handler will be added. + See :ref:`logging` for more information. + + .. versionchanged:: 1.0 + Behavior was simplified. The logger is always named + ``flask.app``. The level is only set during configuration, it + doesn't check ``app.debug`` each time. Only one format is used, + not different ones depending on ``app.debug``. No handlers are + removed, and a handler is only added if no handlers are already + configured. .. versionadded:: 0.3 """ - if self._logger and self._logger.name == self.logger_name: - return self._logger - with _logger_lock: - if self._logger and self._logger.name == self.logger_name: - return self._logger - from flask.logging import create_logger - self._logger = rv = create_logger(self) - return rv + return create_logger(self) @locked_cached_property def jinja_env(self): @@ -649,7 +661,10 @@ root_path = self.root_path if instance_relative: root_path = self.instance_path - return self.config_class(root_path, self.default_config) + defaults = dict(self.default_config) + defaults['ENV'] = get_env() + defaults['DEBUG'] = get_debug_flag() + return self.config_class(root_path, defaults) def auto_find_instance_path(self): """Tries to locate the instance path if it was not provided to the @@ -676,6 +691,28 @@ """ return open(os.path.join(self.instance_path, resource), mode) + def _get_templates_auto_reload(self): + """Reload templates when they are changed. Used by + :meth:`create_jinja_environment`. + + This attribute can be configured with :data:`TEMPLATES_AUTO_RELOAD`. If + not set, it will be enabled in debug mode. + + .. versionadded:: 1.0 + This property was added but the underlying config and behavior + already existed. + """ + rv = self.config['TEMPLATES_AUTO_RELOAD'] + return rv if rv is not None else self.debug + + def _set_templates_auto_reload(self, value): + self.config['TEMPLATES_AUTO_RELOAD'] = value + + templates_auto_reload = property( + _get_templates_auto_reload, _set_templates_auto_reload + ) + del _get_templates_auto_reload, _set_templates_auto_reload + def create_jinja_environment(self): """Creates the Jinja2 environment based on :attr:`jinja_options` and :meth:`select_jinja_autoescape`. Since 0.7 this also adds @@ -688,13 +725,13 @@ ``TEMPLATES_AUTO_RELOAD`` configuration option. """ options = dict(self.jinja_options) + if 'autoescape' not in options: options['autoescape'] = self.select_jinja_autoescape + if 'auto_reload' not in options: - if self.config['TEMPLATES_AUTO_RELOAD'] is not None: - options['auto_reload'] = self.config['TEMPLATES_AUTO_RELOAD'] - else: - options['auto_reload'] = self.debug + options['auto_reload'] = self.templates_auto_reload + rv = self.jinja_environment(self, **options) rv.globals.update( url_for=url_for, @@ -723,15 +760,6 @@ """ return DispatchingJinjaLoader(self) - def init_jinja_globals(self): - """Deprecated. Used to initialize the Jinja2 globals. - - .. versionadded:: 0.5 - .. versionchanged:: 0.7 - This method is deprecated with 0.7. Override - :meth:`create_jinja_environment` instead. - """ - def select_jinja_autoescape(self, filename): """Returns ``True`` if autoescaping should be active for the given template name. If no template name is given, returns `True`. @@ -779,7 +807,41 @@ rv.update(processor()) return rv - def run(self, host=None, port=None, debug=None, **options): + #: What environment the app is running in. Flask and extensions may + #: enable behaviors based on the environment, such as enabling debug + #: mode. This maps to the :data:`ENV` config key. This is set by the + #: :envvar:`FLASK_ENV` environment variable and may not behave as + #: expected if set in code. + #: + #: **Do not enable development when deploying in production.** + #: + #: Default: ``'production'`` + env = ConfigAttribute('ENV') + + def _get_debug(self): + return self.config['DEBUG'] + + def _set_debug(self, value): + self.config['DEBUG'] = value + self.jinja_env.auto_reload = self.templates_auto_reload + + #: Whether debug mode is enabled. When using ``flask run`` to start + #: the development server, an interactive debugger will be shown for + #: unhandled exceptions, and the server will be reloaded when code + #: changes. This maps to the :data:`DEBUG` config key. This is + #: enabled when :attr:`env` is ``'development'`` and is overridden + #: by the ``FLASK_DEBUG`` environment variable. It may not behave as + #: expected if set in code. + #: + #: **Do not enable debug mode when deploying in production.** + #: + #: Default: ``True`` if :attr:`env` is ``'development'``, or + #: ``False`` otherwise. + debug = property(_get_debug, _set_debug) + del _get_debug, _set_debug + + def run(self, host=None, port=None, debug=None, + load_dotenv=True, **options): """Runs the application on a local development server. Do not use ``run()`` in a production setting. It is not intended to @@ -808,35 +870,75 @@ won't catch any exceptions because there won't be any to catch. - .. versionchanged:: 0.10 - The default port is now picked from the ``SERVER_NAME`` variable. - :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to - have the server available externally as well. Defaults to - ``'127.0.0.1'``. + have the server available externally as well. Defaults to + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable + if present. :param port: the port of the webserver. Defaults to ``5000`` or the - port defined in the ``SERVER_NAME`` config variable if - present. - :param debug: if given, enable or disable debug mode. - See :attr:`debug`. - :param options: the options to be forwarded to the underlying - Werkzeug server. See - :func:`werkzeug.serving.run_simple` for more - information. + port defined in the ``SERVER_NAME`` config variable if present. + :param debug: if given, enable or disable debug mode. See + :attr:`debug`. + :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. + :param options: the options to be forwarded to the underlying Werkzeug + server. See :func:`werkzeug.serving.run_simple` for more + information. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment + variables from :file:`.env` and :file:`.flaskenv` files. + + If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG` + environment variables will override :attr:`env` and + :attr:`debug`. + + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. """ - from werkzeug.serving import run_simple - if host is None: - host = '127.0.0.1' - if port is None: - server_name = self.config['SERVER_NAME'] - if server_name and ':' in server_name: - port = int(server_name.rsplit(':', 1)[1]) - else: - port = 5000 + # Change this into a no-op if the server is invoked from the + # command line. Have a look at cli.py for more information. + if os.environ.get('FLASK_RUN_FROM_CLI') == 'true': + from .debughelpers import explain_ignored_app_run + explain_ignored_app_run() + return + + if get_load_dotenv(load_dotenv): + cli.load_dotenv() + + # if set, let env vars override previous values + if 'FLASK_ENV' in os.environ: + self.env = get_env() + self.debug = get_debug_flag() + elif 'FLASK_DEBUG' in os.environ: + self.debug = get_debug_flag() + + # debug passed to method overrides all other sources if debug is not None: self.debug = bool(debug) + + _host = '127.0.0.1' + _port = 5000 + server_name = self.config.get('SERVER_NAME') + sn_host, sn_port = None, None + + if server_name: + sn_host, _, sn_port = server_name.partition(':') + + host = host or sn_host or _host + port = int(port or sn_port or _port) + options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) + options.setdefault('threaded', True) + + cli.show_server_banner(self.env, self.debug, self.name, False) + + from werkzeug.serving import run_simple + try: run_simple(host, port, self, **options) finally: @@ -901,14 +1003,40 @@ from flask.testing import FlaskClient as cls return cls(self, self.response_class, use_cookies=use_cookies, **kwargs) + def test_cli_runner(self, **kwargs): + """Create a CLI runner for testing CLI commands. + See :ref:`testing-cli`. + + Returns an instance of :attr:`test_cli_runner_class`, by default + :class:`~flask.testing.FlaskCliRunner`. The Flask app object is + passed as the first argument. + + .. versionadded:: 1.0 + """ + cls = self.test_cli_runner_class + + if cls is None: + from flask.testing import FlaskCliRunner as cls + + return cls(self, **kwargs) + def open_session(self, request): """Creates or opens a new session. Default implementation stores all session data in a signed cookie. This requires that the :attr:`secret_key` is set. Instead of overriding this method we recommend replacing the :class:`session_interface`. + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.open_session`` + instead. + :param request: an instance of :attr:`request_class`. """ + + warnings.warn(DeprecationWarning( + '"open_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.open_session" instead.' + )) return self.session_interface.open_session(self, request) def save_session(self, session, response): @@ -916,38 +1044,74 @@ implementation, check :meth:`open_session`. Instead of overriding this method we recommend replacing the :class:`session_interface`. + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.save_session`` + instead. + :param session: the session to be saved (a :class:`~werkzeug.contrib.securecookie.SecureCookie` object) :param response: an instance of :attr:`response_class` """ + + warnings.warn(DeprecationWarning( + '"save_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.save_session" instead.' + )) return self.session_interface.save_session(self, session, response) def make_null_session(self): """Creates a new instance of a missing session. Instead of overriding this method we recommend replacing the :class:`session_interface`. + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.make_null_session`` + instead. + .. versionadded:: 0.7 """ + + warnings.warn(DeprecationWarning( + '"make_null_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.make_null_session" instead.' + )) return self.session_interface.make_null_session(self) @setupmethod def register_blueprint(self, blueprint, **options): - """Registers a blueprint on the application. + """Register a :class:`~flask.Blueprint` on the application. Keyword + arguments passed to this method will override the defaults set on the + blueprint. + + Calls the blueprint's :meth:`~flask.Blueprint.register` method after + recording the blueprint in the application's :attr:`blueprints`. + + :param blueprint: The blueprint to register. + :param url_prefix: Blueprint routes will be prefixed with this. + :param subdomain: Blueprint routes will match on this subdomain. + :param url_defaults: Blueprint routes will use these default values for + view arguments. + :param options: Additional keyword arguments are passed to + :class:`~flask.blueprints.BlueprintSetupState`. They can be + accessed in :meth:`~flask.Blueprint.record` callbacks. .. versionadded:: 0.7 """ first_registration = False + if blueprint.name in self.blueprints: - assert self.blueprints[blueprint.name] is blueprint, \ - 'A blueprint\'s name collision occurred between %r and ' \ - '%r. Both share the same name "%s". Blueprints that ' \ - 'are created on the fly need unique names.' % \ - (blueprint, self.blueprints[blueprint.name], blueprint.name) + assert self.blueprints[blueprint.name] is blueprint, ( + 'A name collision occurred between blueprints %r and %r. Both' + ' share the same name "%s". Blueprints that are created on the' + ' fly need unique names.' % ( + blueprint, self.blueprints[blueprint.name], blueprint.name + ) + ) else: self.blueprints[blueprint.name] = blueprint self._blueprint_order.append(blueprint) first_registration = True + blueprint.register(self, options, first_registration) def iter_blueprints(self): @@ -958,7 +1122,8 @@ return iter(self._blueprint_order) @setupmethod - def add_url_rule(self, rule, endpoint=None, view_func=None, **options): + def add_url_rule(self, rule, endpoint=None, view_func=None, + provide_automatic_options=None, **options): """Connects a URL rule. Works exactly like the :meth:`route` decorator. If a view_func is provided it will be registered with the endpoint. @@ -998,6 +1163,10 @@ endpoint :param view_func: the function to call when serving a request to the provided endpoint + :param provide_automatic_options: controls whether the ``OPTIONS`` + method should be added automatically. This can also be controlled + by setting the ``view_func.provide_automatic_options = False`` + before adding the rule. :param options: the options to be forwarded to the underlying :class:`~werkzeug.routing.Rule` object. A change to Werkzeug is handling of method options. methods @@ -1027,8 +1196,9 @@ # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. - provide_automatic_options = getattr(view_func, - 'provide_automatic_options', None) + if provide_automatic_options is None: + provide_automatic_options = getattr(view_func, + 'provide_automatic_options', None) if provide_automatic_options is None: if 'OPTIONS' not in methods: @@ -1114,7 +1284,9 @@ @setupmethod def errorhandler(self, code_or_exception): - """A decorator that is used to register a function given an + """Register a function to handle errors by code or exception class. + + A decorator that is used to register a function given an error code. Example:: @app.errorhandler(404) @@ -1127,21 +1299,6 @@ def special_exception_handler(error): return 'Database connection failed', 500 - You can also register a function as error handler without using - the :meth:`errorhandler` decorator. The following example is - equivalent to the one above:: - - def page_not_found(error): - return 'This page does not exist', 404 - app.error_handler_spec[None][404] = page_not_found - - Setting error handlers via assignments to :attr:`error_handler_spec` - however is discouraged as it requires fiddling with nested dictionaries - and the special case for arbitrary exception types. - - The first ``None`` refers to the active blueprint. If the error - handler should be application wide ``None`` shall be used. - .. versionadded:: 0.7 Use :meth:`register_error_handler` instead of modifying :attr:`error_handler_spec` directly, for application wide error @@ -1160,6 +1317,7 @@ return f return decorator + @setupmethod def register_error_handler(self, code_or_exception, f): """Alternative error attach function to the :meth:`errorhandler` decorator that is more straightforward to use for non decorator @@ -1178,11 +1336,18 @@ """ if isinstance(code_or_exception, HTTPException): # old broken behavior raise ValueError( - 'Tried to register a handler for an exception instance {0!r}. ' - 'Handlers can only be registered for exception classes or HTTP error codes.' - .format(code_or_exception)) + 'Tried to register a handler for an exception instance {0!r}.' + ' Handlers can only be registered for exception classes or' + ' HTTP error codes.'.format(code_or_exception) + ) - exc_class, code = self._get_exc_class_and_code(code_or_exception) + try: + exc_class, code = self._get_exc_class_and_code(code_or_exception) + except KeyError: + raise KeyError( + "'{0}' is not a recognized HTTP error code. Use a subclass of" + " HTTPException with that code instead.".format(code_or_exception) + ) handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {}) handlers[exc_class] = f @@ -1288,10 +1453,12 @@ def before_request(self, f): """Registers a function to run before each request. - The function will be called without any arguments. - If the function returns a non-None value, it's handled as - if it was the return value from the view and further - request handling is stopped. + For example, this can be used to open a database connection, or to load + the logged in user from the session. + + The function will be called without any arguments. If it returns a + non-None value, the value is handled as if it was the return value from + the view, and further request handling is stopped. """ self.before_request_funcs.setdefault(None, []).append(f) return f @@ -1347,7 +1514,7 @@ will have to surround the execution of these code by try/except statements and log occurring errors. - When a teardown function was called because of a exception it will + When a teardown function was called because of an exception it will be passed an error object. The return values of teardown functions are ignored. @@ -1383,8 +1550,10 @@ Since a request context typically also manages an application context it would also be called when you pop a request context. - When a teardown function was called because of an exception it will - be passed an error object. + When a teardown function was called because of an unhandled exception + it will be passed an error object. If an :meth:`errorhandler` is + registered, it will handle the exception and the teardown will not + receive it. The return values of teardown functions are ignored. @@ -1410,9 +1579,17 @@ @setupmethod def url_value_preprocessor(self, f): - """Registers a function as URL value preprocessor for all view - functions of the application. It's called before the view functions - are called and can modify the url values provided. + """Register a URL value preprocessor function for all view + functions in the application. These functions will be called before the + :meth:`before_request` functions. + + The function can modify the values captured from the matched url before + they are passed to the view. For example, this can be used to pop a + common language code value and place it in ``g`` rather than pass it to + every view. + + The function is passed the endpoint name and values dict. The return + value is ignored. """ self.url_value_preprocessors.setdefault(None, []).append(f) return f @@ -1427,32 +1604,28 @@ return f def _find_error_handler(self, e): - """Finds a registered error handler for the request’s blueprint. - Otherwise falls back to the app, returns None if not a suitable - handler is found. + """Return a registered error handler for an exception in this order: + blueprint handler for a specific code, app handler for a specific code, + blueprint handler for an exception class, app handler for an exception + class, or ``None`` if a suitable handler is not found. """ exc_class, code = self._get_exc_class_and_code(type(e)) - def find_handler(handler_map): + for name, c in ( + (request.blueprint, code), (None, code), + (request.blueprint, None), (None, None) + ): + handler_map = self.error_handler_spec.setdefault(name, {}).get(c) + if not handler_map: - return + continue + for cls in exc_class.__mro__: handler = handler_map.get(cls) + if handler is not None: - # cache for next time exc_class is raised - handler_map[exc_class] = handler return handler - # try blueprint handlers - handler = find_handler(self.error_handler_spec - .get(request.blueprint, {}) - .get(code)) - if handler is not None: - return handler - - # fall back to app handlers - return find_handler(self.error_handler_spec[None].get(code)) - def handle_http_exception(self, e): """Handles an HTTP exception. By default this will invoke the registered error handlers and fall back to returning the @@ -1482,12 +1655,26 @@ traceback. This is helpful for debugging implicitly raised HTTP exceptions. + .. versionchanged:: 1.0 + Bad request errors are not trapped by default in debug mode. + .. versionadded:: 0.8 """ if self.config['TRAP_HTTP_EXCEPTIONS']: return True - if self.config['TRAP_BAD_REQUEST_ERRORS']: + + trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS'] + + # if unset, trap key errors in debug mode + if ( + trap_bad_request is None and self.debug + and isinstance(e, BadRequestKeyError) + ): + return True + + if trap_bad_request: return isinstance(e, BadRequest) + return False def handle_user_exception(self, e): @@ -1498,16 +1685,30 @@ function will either return a response value or reraise the exception with the same traceback. + .. versionchanged:: 1.0 + Key errors raised from request data like ``form`` show the the bad + key in debug mode rather than a generic bad request message. + .. versionadded:: 0.7 """ exc_type, exc_value, tb = sys.exc_info() assert exc_value is e - # ensure not to trash sys.exc_info() at that point in case someone # wants the traceback preserved in handle_http_exception. Of course # we cannot prevent users from trashing it themselves in a custom # trap_http_exception method so that's their fault then. + # MultiDict passes the key to the exception, but that's ignored + # when generating the response message. Set an informative + # description for key errors in debug mode or when trapping errors. + if ( + (self.debug or self.config['TRAP_BAD_REQUEST_ERRORS']) + and isinstance(e, BadRequestKeyError) + # only set it if it's still the default description + and e.description is BadRequestKeyError.description + ): + e.description = "KeyError: '{0}'".format(*e.args) + if isinstance(e, HTTPException) and not self.trap_http_exception(e): return self.handle_http_exception(e) @@ -1688,87 +1889,142 @@ return False def make_response(self, rv): - """Converts the return value from a view function to a real - response object that is an instance of :attr:`response_class`. - - The following types are allowed for `rv`: - - .. tabularcolumns:: |p{3.5cm}|p{9.5cm}| + """Convert the return value from a view function to an instance of + :attr:`response_class`. - ======================= =========================================== - :attr:`response_class` the object is returned unchanged - :class:`str` a response object is created with the - string as body - :class:`unicode` a response object is created with the - string encoded to utf-8 as body - a WSGI function the function is called as WSGI application - and buffered as response object - :class:`tuple` A tuple in the form ``(response, status, - headers)`` or ``(response, headers)`` - where `response` is any of the - types defined here, `status` is a string - or an integer and `headers` is a list or - a dictionary with header values. - ======================= =========================================== - - :param rv: the return value from the view function + :param rv: the return value from the view function. The view function + must return a response. Returning ``None``, or the view ending + without returning, is not allowed. The following types are allowed + for ``view_rv``: + + ``str`` (``unicode`` in Python 2) + A response object is created with the string encoded to UTF-8 + as the body. + + ``bytes`` (``str`` in Python 2) + A response object is created with the bytes as the body. + + ``tuple`` + Either ``(body, status, headers)``, ``(body, status)``, or + ``(body, headers)``, where ``body`` is any of the other types + allowed here, ``status`` is a string or an integer, and + ``headers`` is a dictionary or a list of ``(key, value)`` + tuples. If ``body`` is a :attr:`response_class` instance, + ``status`` overwrites the exiting value and ``headers`` are + extended. + + :attr:`response_class` + The object is returned unchanged. + + other :class:`~werkzeug.wrappers.Response` class + The object is coerced to :attr:`response_class`. + + :func:`callable` + The function is called as a WSGI application. The result is + used to create a response object. .. versionchanged:: 0.9 Previously a tuple was interpreted as the arguments for the response object. """ - status_or_headers = headers = None + + status = headers = None + + # unpack tuple returns if isinstance(rv, tuple): - rv, status_or_headers, headers = rv + (None,) * (3 - len(rv)) + len_rv = len(rv) - if rv is None: - raise ValueError('View function did not return a response') + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv + else: + rv, status = rv + # other sized tuples are not allowed + else: + raise TypeError( + 'The view function did not return a valid response tuple.' + ' The tuple must have the form (body, status, headers),' + ' (body, status), or (body, headers).' + ) - if isinstance(status_or_headers, (dict, list)): - headers, status_or_headers = status_or_headers, None + # the body must not be None + if rv is None: + raise TypeError( + 'The view function did not return a valid response. The' + ' function either returned None or ended without a return' + ' statement.' + ) + # make sure the body is an instance of the response class if not isinstance(rv, self.response_class): - # When we create a response object directly, we let the constructor - # set the headers and status. We do this because there can be - # some extra logic involved when creating these objects with - # specific values (like default content type selection). if isinstance(rv, (text_type, bytes, bytearray)): - rv = self.response_class(rv, headers=headers, - status=status_or_headers) - headers = status_or_headers = None + # let the response class set the status and headers instead of + # waiting to do it manually, so that the class can handle any + # special logic + rv = self.response_class(rv, status=status, headers=headers) + status = headers = None else: - rv = self.response_class.force_type(rv, request.environ) - - if status_or_headers is not None: - if isinstance(status_or_headers, string_types): - rv.status = status_or_headers + # evaluate a WSGI callable, or coerce a different response + # class to the correct type + try: + rv = self.response_class.force_type(rv, request.environ) + except TypeError as e: + new_error = TypeError( + '{e}\nThe view function did not return a valid' + ' response. The return type must be a string, tuple,' + ' Response instance, or WSGI callable, but it was a' + ' {rv.__class__.__name__}.'.format(e=e, rv=rv) + ) + reraise(TypeError, new_error, sys.exc_info()[2]) + + # prefer the status if it was provided + if status is not None: + if isinstance(status, (text_type, bytes, bytearray)): + rv.status = status else: - rv.status_code = status_or_headers + rv.status_code = status + + # extend existing headers with provided headers if headers: rv.headers.extend(headers) return rv def create_url_adapter(self, request): - """Creates a URL adapter for the given request. The URL adapter - is created at a point where the request context is not yet set up - so the request is passed explicitly. + """Creates a URL adapter for the given request. The URL adapter + is created at a point where the request context is not yet set + up so the request is passed explicitly. .. versionadded:: 0.6 .. versionchanged:: 0.9 This can now also be called without a request object when the URL adapter is created for the application context. + + .. versionchanged:: 1.0 + :data:`SERVER_NAME` no longer implicitly enables subdomain + matching. Use :attr:`subdomain_matching` instead. """ if request is not None: - return self.url_map.bind_to_environ(request.environ, - server_name=self.config['SERVER_NAME']) + # If subdomain matching is disabled (the default), use the + # default subdomain in all cases. This should be the default + # in Werkzeug but it currently does not have that feature. + subdomain = ((self.url_map.default_subdomain or None) + if not self.subdomain_matching else None) + return self.url_map.bind_to_environ( + request.environ, + server_name=self.config['SERVER_NAME'], + subdomain=subdomain) # We need at the very least the server name to be set for this # to work. if self.config['SERVER_NAME'] is not None: return self.url_map.bind( self.config['SERVER_NAME'], - script_name=self.config['APPLICATION_ROOT'] or '/', + script_name=self.config['APPLICATION_ROOT'], url_scheme=self.config['PREFERRED_URL_SCHEME']) def inject_url_defaults(self, endpoint, values): @@ -1806,16 +2062,16 @@ raise error def preprocess_request(self): - """Called before the actual request dispatching and will - call each :meth:`before_request` decorated function, passing no - arguments. - If any of these functions returns a value, it's handled as - if it was the return value from the view and further - request handling is stopped. - - This also triggers the :meth:`url_value_preprocessor` functions before - the actual :meth:`before_request` functions are called. + """Called before the request is dispatched. Calls + :attr:`url_value_preprocessors` registered with the app and the + current blueprint (if any). Then calls :attr:`before_request_funcs` + registered with the app and the blueprint. + + If any :meth:`before_request` handler returns a non-None value, the + value is handled as if it was the return value from the view, and + further request handling is stopped. """ + bp = _request_ctx_stack.top.request.blueprint funcs = self.url_value_preprocessors.get(None, ()) @@ -1855,19 +2111,29 @@ for handler in funcs: response = handler(response) if not self.session_interface.is_null_session(ctx.session): - self.save_session(ctx.session, response) + self.session_interface.save_session(self, ctx.session, response) return response def do_teardown_request(self, exc=_sentinel): - """Called after the actual request dispatching and will - call every as :meth:`teardown_request` decorated function. This is - not actually called by the :class:`Flask` object itself but is always - triggered when the request context is popped. That way we have a - tighter control over certain resources under testing environments. + """Called after the request is dispatched and the response is + returned, right before the request context is popped. + + This calls all functions decorated with + :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` + if a blueprint handled the request. Finally, the + :data:`request_tearing_down` signal is sent. + + This is called by + :meth:`RequestContext.pop() `, + which may be delayed during testing to maintain access to + resources. + + :param exc: An unhandled exception raised while dispatching the + request. Detected from the current exception information if + not passed. Passed to each teardown function. .. versionchanged:: 0.9 - Added the `exc` argument. Previously this was always using the - current exception information. + Added the ``exc`` argument. """ if exc is _sentinel: exc = sys.exc_info()[1] @@ -1880,9 +2146,17 @@ request_tearing_down.send(self, exc=exc) def do_teardown_appcontext(self, exc=_sentinel): - """Called when an application context is popped. This works pretty - much the same as :meth:`do_teardown_request` but for the application - context. + """Called right before the application context is popped. + + When handling a request, the application context is popped + after the request context. See :meth:`do_teardown_request`. + + This calls all functions decorated with + :meth:`teardown_appcontext`. Then the + :data:`appcontext_tearing_down` signal is sent. + + This is called by + :meth:`AppContext.pop() `. .. versionadded:: 0.9 """ @@ -1893,66 +2167,102 @@ appcontext_tearing_down.send(self, exc=exc) def app_context(self): - """Binds the application only. For as long as the application is bound - to the current context the :data:`flask.current_app` points to that - application. An application context is automatically created when a - request context is pushed if necessary. + """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` + block to push the context, which will make :data:`current_app` + point at this application. + + An application context is automatically pushed by + :meth:`RequestContext.push() ` + when handling a request, and when running a CLI command. Use + this to manually create a context outside of these situations. - Example usage:: + :: with app.app_context(): - ... + init_db() + + See :doc:`/appcontext`. .. versionadded:: 0.9 """ return AppContext(self) def request_context(self, environ): - """Creates a :class:`~flask.ctx.RequestContext` from the given - environment and binds it to the current context. This must be used in - combination with the ``with`` statement because the request is only bound - to the current context for the duration of the ``with`` block. - - Example usage:: - - with app.request_context(environ): - do_something_with(request) - - The object returned can also be used without the ``with`` statement - which is useful for working in the shell. The example above is - doing exactly the same as this code:: - - ctx = app.request_context(environ) - ctx.push() - try: - do_something_with(request) - finally: - ctx.pop() - - .. versionchanged:: 0.3 - Added support for non-with statement usage and ``with`` statement - is now passed the ctx object. + """Create a :class:`~flask.ctx.RequestContext` representing a + WSGI environment. Use a ``with`` block to push the context, + which will make :data:`request` point at this request. + + See :doc:`/reqcontext`. + + Typically you should not call this from your own code. A request + context is automatically pushed by the :meth:`wsgi_app` when + handling a request. Use :meth:`test_request_context` to create + an environment and context instead of this method. :param environ: a WSGI environment """ return RequestContext(self, environ) def test_request_context(self, *args, **kwargs): - """Creates a WSGI environment from the given values (see - :class:`werkzeug.test.EnvironBuilder` for more information, this - function accepts the same arguments). + """Create a :class:`~flask.ctx.RequestContext` for a WSGI + environment created from the given values. This is mostly useful + during testing, where you may want to run a function that uses + request data without dispatching a full request. + + See :doc:`/reqcontext`. + + Use a ``with`` block to push the context, which will make + :data:`request` point at the request for the created + environment. :: + + with test_request_context(...): + generate_report() + + When using the shell, it may be easier to push and pop the + context manually to avoid indentation. :: + + ctx = app.test_request_context(...) + ctx.push() + ... + ctx.pop() + + Takes the same arguments as Werkzeug's + :class:`~werkzeug.test.EnvironBuilder`, with some defaults from + the application. See the linked Werkzeug docs for most of the + available arguments. Flask-specific behavior is listed here. + + :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 data: The request body, either as a string or a dict of + form keys and values. + :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`. """ from flask.testing import make_test_environ_builder + builder = make_test_environ_builder(self, *args, **kwargs) + try: return self.request_context(builder.get_environ()) finally: builder.close() def wsgi_app(self, environ, start_response): - """The actual WSGI application. This is not implemented in - `__call__` so that middlewares can be applied without losing a - reference to the class. So instead of doing this:: + """The actual WSGI application. This is not implemented in + :meth:`__call__` so that middlewares can be applied without + losing a reference to the app object. Instead of doing this:: app = MyMiddleware(app) @@ -1964,21 +2274,21 @@ can continue to call methods on it. .. versionchanged:: 0.7 - The behavior of the before and after request callbacks was changed - under error conditions and a new callback was added that will - always execute at the end of the request, independent on if an - error occurred or not. See :ref:`callbacks-and-errors`. - - :param environ: a WSGI environment - :param start_response: a callable accepting a status code, - a list of headers and an optional - exception context to start the response + Teardown events for the request and app contexts are called + even if an unhandled error occurs. Other events may not be + called depending on when an error occurs during dispatch. + See :ref:`callbacks-and-errors`. + + :param environ: A WSGI environment. + :param start_response: A callable accepting a status code, + a list of headers, and an optional exception context to + start the response. """ ctx = self.request_context(environ) - ctx.push() error = None try: try: + ctx.push() response = self.full_dispatch_request() except Exception as e: error = e @@ -1993,7 +2303,9 @@ ctx.auto_pop(error) def __call__(self, environ, start_response): - """Shortcut for :attr:`wsgi_app`.""" + """The WSGI server calls the Flask application object as the + WSGI application. This calls :meth:`wsgi_app` which can be + wrapped to applying middleware.""" return self.wsgi_app(environ, start_response) def __repr__(self): diff -Nru flask-0.12.2/flask/blueprints.py flask-1.0.2/flask/blueprints.py --- flask-0.12.2/flask/blueprints.py 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/flask/blueprints.py 2018-05-02 13:06:15.000000000 +0000 @@ -6,10 +6,11 @@ Blueprints are the recommended way to implement larger or more pluggable applications in Flask 0.7 and later. - :copyright: (c) 2015 by Armin Ronacher. + :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 @@ -49,7 +50,6 @@ 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 @@ -64,8 +64,12 @@ to the application. The endpoint is automatically prefixed with the blueprint's name. """ - if self.url_prefix: - rule = self.url_prefix + rule + 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) @@ -89,6 +93,28 @@ 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, @@ -137,18 +163,25 @@ return BlueprintSetupState(self, app, options, first_registration) def register(self, app, options, first_registration=False): - """Called by :meth:`Flask.register_blueprint` to register a blueprint - on the application. This can be overridden to customize the register - behavior. Keyword arguments from - :func:`~flask.Flask.register_blueprint` are directly forwarded to this - method in the `options` dictionary. + """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 + '/', - view_func=self.send_static_file, - endpoint='static') + state.add_url_rule( + self.static_url_path + '/', + view_func=self.send_static_file, endpoint='static' + ) for deferred in self.deferred_functions: deferred(state) @@ -169,6 +202,8 @@ """ 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)) diff -Nru flask-0.12.2/flask/cli.py flask-1.0.2/flask/cli.py --- flask-0.12.2/flask/cli.py 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/flask/cli.py 2018-05-02 13:16:56.000000000 +0000 @@ -5,119 +5,255 @@ A simple command line application to run flask apps. - :copyright: (c) 2015 by Armin Ronacher. + :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 -from threading import Lock, Thread +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 ._compat import iteritems, reraise -from .helpers import get_debug_flag 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(module): +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': + for attr_name in ('app', 'application'): app = getattr(module, attr_name, None) - if app is not None and isinstance(app, Flask): + + 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)] + matches = [ + v for k, v in iteritems(module.__dict__) if isinstance(v, Flask) + ] if len(matches) == 1: return matches[0] - raise NoAppException('Failed to find application in module "%s". Are ' - 'you sure it contains a Flask application? Maybe ' - 'you wrapped it in a WSGI middleware or you are ' - 'using a factory function.' % module.__name__) + 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 prepare_exec_for_file(filename): +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. """ - module = [] + path = os.path.realpath(path) - # Chop off file extensions or package markers - if os.path.split(filename)[1] == '__init__.py': - filename = os.path.dirname(filename) - elif filename.endswith('.py'): - filename = filename[:-3] - else: - raise NoAppException('The file provided (%s) does exist but is not a ' - 'valid Python file. This means that it cannot ' - 'be used as application. Please change the ' - 'extension to .py' % filename) - filename = os.path.realpath(filename) - - dirpath = filename - while 1: - dirpath, extra = os.path.split(dirpath) - module.append(extra) - if not os.path.isfile(os.path.join(dirpath, '__init__.py')): + 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 - sys.path.insert(0, dirpath) - return '.'.join(module[::-1]) + if sys.path[0] != path: + sys.path.insert(0, path) + + return '.'.join(module_name[::-1]) -def locate_app(app_id): - """Attempts to locate the application.""" +def locate_app(script_info, module_name, app_name, raise_if_not_found=True): __traceback_hide__ = True - if ':' in app_id: - module, app_obj = app_id.split(':', 1) - else: - module = app_id - app_obj = None try: - __import__(module) + __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 + 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: - raise NoAppException('The file/path provided (%s) does not appear' - ' to exist. Please verify the path is ' - 'correct. If app is not on PYTHONPATH, ' - 'ensure the extension is .py' % module) - - mod = sys.modules[module] - if app_obj is None: - app = find_best_app(mod) - else: - app = getattr(mod, app_obj, None) - if app is None: - raise RuntimeError('Failed to find application in module "%s"' - % module) - - return app + return + module = sys.modules[module_name] -def find_default_import_path(): - app = os.environ.get('FLASK_APP') - if app is None: - return - if os.path.isfile(app): - return prepare_exec_for_file(app) - return app + 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): @@ -130,11 +266,16 @@ }, 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) + +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 @@ -200,15 +341,8 @@ """ def __init__(self, app_import_path=None, create_app=None): - if create_app is None: - if app_import_path is None: - app_import_path = find_default_import_path() - self.app_import_path = app_import_path - else: - app_import_path = None - #: Optionally the import path for the Flask application. - self.app_import_path = app_import_path + 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 @@ -223,23 +357,44 @@ 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: - rv = self.create_app(self) + app = call_factory(self, self.create_app) else: - if not self.app_import_path: - raise NoAppException( - 'Could not locate Flask application. You did not provide ' - 'the FLASK_APP environment variable.\n\nFor more ' - 'information see ' - 'http://flask.pocoo.org/docs/latest/quickstart/') - rv = locate_app(self.app_import_path) + 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: - rv.debug = debug - self._loaded_app = rv - return rv + app.debug = debug + + self._loaded_app = app + return app pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) @@ -297,14 +452,21 @@ 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. + 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 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, **extra): + add_version_option=True, load_dotenv=True, **extra): params = list(extra.pop('params', None) or ()) if add_version_option: @@ -312,10 +474,12 @@ 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 @@ -368,16 +532,191 @@ # want the help page to break if the app does not exist. # If someone attempts to use the command we try to create # the app again and this will give us the error. - pass + # 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 AppGroup.main(self, *args, **kwargs) + 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.') @@ -385,57 +724,51 @@ 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 ' + 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 ' + 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 ' + help='Enable or disable eager loading. By default eager ' 'loading is enabled if the reloader is disabled.') -@click.option('--with-threads/--without-threads', default=False, +@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): - """Runs a local development server for the Flask application. + with_threads, cert): + """Run a local development server. - This local server is recommended for development purposes only but it - can also be used for simple intranet deployments. By default it will - not support any sort of concurrency at all to simplify debugging. This - can be changed with the --with-threads option which will enable basic - multithreading. + 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 by default enabled if the debug flag of - Flask is enabled and disabled otherwise. + The reloader and debugger are enabled by default if + FLASK_ENV=development or FLASK_DEBUG=1. """ - from werkzeug.serving import run_simple - debug = get_debug_flag() + if reload is None: - reload = bool(debug) + reload = debug + if debugger is None: - debugger = bool(debug) + 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) - # Extra startup messages. This depends a bit on Werkzeug internals to - # not double execute when the reloader kicks in. - if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': - # If we have an import path we can print it out now which can help - # people understand what's being served. If we do not have an - # import path because the app was loaded through a callback then - # we won't print anything. - if info.app_import_path is not None: - print(' * Serving Flask app "%s"' % info.app_import_path) - if debug is not None: - print(' * Forcing debug mode %s' % (debug and 'on' or 'off')) - - run_simple(host, port, app, use_reloader=reload, - use_debugger=debugger, threaded=with_threads) + 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.') @@ -446,16 +779,16 @@ namespace of this shell according to it's configuration. This is useful for executing small snippets of management code - without having to manually configuring the application. + 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' % ( + banner = 'Python %s on %s\nApp: %s [%s]\nInstance: %s' % ( sys.version, sys.platform, app.import_name, - app.debug and ' [debug]' or '', + app.env, app.instance_path, ) ctx = {} @@ -472,41 +805,89 @@ code.interact(banner=banner, local=ctx) -cli = FlaskGroup(help="""\ -This shell command acts as general utility script for Flask applications. +@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()) -It loads the application configured (through the FLASK_APP environment -variable) and then provides commands either provided by the application or -Flask itself. -The most useful commands are the "run" and "shell" command. +cli = FlaskGroup(help="""\ +A general utility script for Flask applications. -Example usage: +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)s%(cmd)s FLASK_APP=hello.py - %(prefix)s%(cmd)s FLASK_DEBUG=1 - %(prefix)sflask run -""" % { - 'cmd': os.name == 'posix' and 'export' or 'set', - 'prefix': os.name == 'posix' and '$ ' or '', -}) + {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): - this_module = __package__ + '.cli' args = sys.argv[1:] if as_module: - if sys.version_info >= (2, 7): - name = 'python -m ' + this_module.rsplit('.', 1)[0] - else: - name = 'python -m ' + this_module + this_module = 'flask' + + if sys.version_info < (2, 7): + this_module += '.cli' + + name = 'python -m ' + this_module - # This module is always executed as "python -m flask.run" and as such - # we need to ensure that we restore the actual command line so that - # the reloader can properly operate. - sys.argv = ['-m', this_module] + sys.argv[1:] + # 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 diff -Nru flask-0.12.2/flask/_compat.py flask-1.0.2/flask/_compat.py --- flask-0.12.2/flask/_compat.py 2016-09-07 16:17:50.000000000 +0000 +++ flask-1.0.2/flask/_compat.py 2018-05-02 00:50:32.000000000 +0000 @@ -7,9 +7,10 @@ version of six so we don't have to depend on a specific version of it. - :copyright: (c) 2015 by Armin Ronacher. + :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ + import sys PY2 = sys.version_info[0] == 2 @@ -25,6 +26,7 @@ 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): @@ -43,6 +45,7 @@ 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') diff -Nru flask-0.12.2/flask/config.py flask-1.0.2/flask/config.py --- flask-0.12.2/flask/config.py 2017-02-10 11:20:32.000000000 +0000 +++ flask-1.0.2/flask/config.py 2018-05-02 00:50:32.000000000 +0000 @@ -5,7 +5,7 @@ Implements the configuration related objects. - :copyright: (c) 2015 by Armin Ronacher. + :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ @@ -129,7 +129,9 @@ 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): + 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 diff -Nru flask-0.12.2/flask/ctx.py flask-1.0.2/flask/ctx.py --- flask-0.12.2/flask/ctx.py 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/flask/ctx.py 2018-05-02 00:50:32.000000000 +0000 @@ -5,7 +5,7 @@ Implements the objects required to keep the context. - :copyright: (c) 2015 by Armin Ronacher. + :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ @@ -24,18 +24,60 @@ class _AppCtxGlobals(object): - """A plain 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): @@ -325,13 +367,18 @@ _request_ctx_stack.push(self) - # Open the session at the moment that the request context is - # available. This allows a custom open_session method to use the - # request context (e.g. code that access database information - # stored on `g` instead of the appcontext). - self.session = self.app.open_session(self.request) + # 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: - self.session = self.app.make_null_session() + 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 diff -Nru flask-0.12.2/flask/debughelpers.py flask-1.0.2/flask/debughelpers.py --- flask-0.12.2/flask/debughelpers.py 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/flask/debughelpers.py 2018-05-02 00:50:32.000000000 +0000 @@ -5,9 +5,13 @@ Various helpers to make the development experience better. - :copyright: (c) 2015 by Armin Ronacher. + :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 @@ -153,3 +157,12 @@ 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) diff -Nru flask-0.12.2/flask/ext/__init__.py flask-1.0.2/flask/ext/__init__.py --- flask-0.12.2/flask/ext/__init__.py 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/flask/ext/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.ext - ~~~~~~~~~ - - Redirect imports for extensions. This module basically makes it possible - for us to transition from flaskext.foo to flask_foo without having to - force all extensions to upgrade at the same time. - - When a user does ``from flask.ext.foo import bar`` it will attempt to - import ``from flask_foo import bar`` first and when that fails it will - try to import ``from flaskext.foo import bar``. - - We're switching from namespace packages because it was just too painful for - everybody involved. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - - -def setup(): - from ..exthook import ExtensionImporter - importer = ExtensionImporter(['flask_%s', 'flaskext.%s'], __name__) - importer.install() - - -setup() -del setup diff -Nru flask-0.12.2/flask/exthook.py flask-1.0.2/flask/exthook.py --- flask-0.12.2/flask/exthook.py 2016-09-11 15:55:31.000000000 +0000 +++ flask-1.0.2/flask/exthook.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.exthook - ~~~~~~~~~~~~~ - - Redirect imports for extensions. This module basically makes it possible - for us to transition from flaskext.foo to flask_foo without having to - force all extensions to upgrade at the same time. - - When a user does ``from flask.ext.foo import bar`` it will attempt to - import ``from flask_foo import bar`` first and when that fails it will - try to import ``from flaskext.foo import bar``. - - We're switching from namespace packages because it was just too painful for - everybody involved. - - This is used by `flask.ext`. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import sys -import os -import warnings -from ._compat import reraise - - -class ExtDeprecationWarning(DeprecationWarning): - pass - -warnings.simplefilter('always', ExtDeprecationWarning) - - -class ExtensionImporter(object): - """This importer redirects imports from this submodule to other locations. - This makes it possible to transition from the old flaskext.name to the - newer flask_name without people having a hard time. - """ - - def __init__(self, module_choices, wrapper_module): - self.module_choices = module_choices - self.wrapper_module = wrapper_module - self.prefix = wrapper_module + '.' - self.prefix_cutoff = wrapper_module.count('.') + 1 - - def __eq__(self, other): - return self.__class__.__module__ == other.__class__.__module__ and \ - self.__class__.__name__ == other.__class__.__name__ and \ - self.wrapper_module == other.wrapper_module and \ - self.module_choices == other.module_choices - - def __ne__(self, other): - return not self.__eq__(other) - - def install(self): - sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self] - - def find_module(self, fullname, path=None): - if fullname.startswith(self.prefix) and \ - fullname != 'flask.ext.ExtDeprecationWarning': - return self - - def load_module(self, fullname): - if fullname in sys.modules: - return sys.modules[fullname] - - modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff] - - warnings.warn( - "Importing flask.ext.{x} is deprecated, use flask_{x} instead." - .format(x=modname), ExtDeprecationWarning, stacklevel=2 - ) - - for path in self.module_choices: - realname = path % modname - try: - __import__(realname) - except ImportError: - exc_type, exc_value, tb = sys.exc_info() - # since we only establish the entry in sys.modules at the - # very this seems to be redundant, but if recursive imports - # happen we will call into the move import a second time. - # On the second invocation we still don't have an entry for - # fullname in sys.modules, but we will end up with the same - # fake module name and that import will succeed since this - # one already has a temporary entry in the modules dict. - # Since this one "succeeded" temporarily that second - # invocation now will have created a fullname entry in - # sys.modules which we have to kill. - sys.modules.pop(fullname, None) - - # If it's an important traceback we reraise it, otherwise - # we swallow it and try the next choice. The skipped frame - # is the one from __import__ above which we don't care about - if self.is_important_traceback(realname, tb): - reraise(exc_type, exc_value, tb.tb_next) - continue - module = sys.modules[fullname] = sys.modules[realname] - if '.' not in modname: - setattr(sys.modules[self.wrapper_module], modname, module) - - if realname.startswith('flaskext.'): - warnings.warn( - "Detected extension named flaskext.{x}, please rename it " - "to flask_{x}. The old form is deprecated." - .format(x=modname), ExtDeprecationWarning - ) - - return module - raise ImportError('No module named %s' % fullname) - - def is_important_traceback(self, important_module, tb): - """Walks a traceback's frames and checks if any of the frames - originated in the given important module. If that is the case then we - were able to import the module itself but apparently something went - wrong when the module was imported. (Eg: import of an import failed). - """ - while tb is not None: - if self.is_important_frame(important_module, tb): - return True - tb = tb.tb_next - return False - - def is_important_frame(self, important_module, tb): - """Checks a single frame if it's important.""" - g = tb.tb_frame.f_globals - if '__name__' not in g: - return False - - module_name = g['__name__'] - - # Python 2.7 Behavior. Modules are cleaned up late so the - # name shows up properly here. Success! - if module_name == important_module: - return True - - # Some python versions will clean up modules so early that the - # module name at that point is no longer set. Try guessing from - # the filename then. - filename = os.path.abspath(tb.tb_frame.f_code.co_filename) - test_string = os.path.sep + important_module.replace('.', os.path.sep) - return test_string + '.py' in filename or \ - test_string + os.path.sep + '__init__.py' in filename diff -Nru flask-0.12.2/flask/globals.py flask-1.0.2/flask/globals.py --- flask-0.12.2/flask/globals.py 2016-09-06 19:46:48.000000000 +0000 +++ flask-1.0.2/flask/globals.py 2018-05-02 00:50:32.000000000 +0000 @@ -6,7 +6,7 @@ Defines all the global objects that are proxies to the current active context. - :copyright: (c) 2015 by Armin Ronacher. + :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ @@ -25,8 +25,8 @@ Working outside of application context. This typically means that you attempted to use functionality that needed -to interface with the current application object in a way. To solve -this set up an application context with app.app_context(). See the +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.\ ''' diff -Nru flask-0.12.2/flask/helpers.py flask-1.0.2/flask/helpers.py --- flask-0.12.2/flask/helpers.py 2017-05-16 06:39:01.000000000 +0000 +++ flask-1.0.2/flask/helpers.py 2018-05-02 00:50:32.000000000 +0000 @@ -5,11 +5,12 @@ Implements various helpers. - :copyright: (c) 2015 by Armin Ronacher. + :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ import os +import socket import sys import pkgutil import posixpath @@ -17,31 +18,22 @@ from time import time from zlib import adler32 from threading import RLock +import unicodedata from werkzeug.routing import BuildError from functools import update_wrapper -try: - from werkzeug.urls import url_quote -except ImportError: - from urlparse import quote as url_quote - +from werkzeug.urls import url_quote from werkzeug.datastructures import Headers, Range from werkzeug.exceptions import BadRequest, NotFound, \ RequestedRangeNotSatisfiable -# this was moved in 0.7 -try: - from werkzeug.wsgi import wrap_file -except ImportError: - from werkzeug.utils import wrap_file - +from werkzeug.wsgi import wrap_file from jinja2 import FileSystemLoader from .signals import message_flashed from .globals import session, _request_ctx_stack, _app_ctx_stack, \ current_app, request -from ._compat import string_types, text_type - +from ._compat import string_types, text_type, PY2 # sentinel _missing = object() @@ -54,11 +46,41 @@ if sep not in (None, '/')) -def get_debug_flag(default=None): +def get_env(): + """Get the environment the app is running in, indicated by the + :envvar:`FLASK_ENV` environment variable. The default is + ``'production'``. + """ + return os.environ.get('FLASK_ENV') or 'production' + + +def get_debug_flag(): + """Get whether debug mode should be enabled for the app, indicated + by the :envvar:`FLASK_DEBUG` environment variable. The default is + ``True`` if :func:`.get_env` returns ``'development'``, or ``False`` + otherwise. + """ val = os.environ.get('FLASK_DEBUG') + + if not val: + return get_env() == 'development' + + return val.lower() not in ('0', 'false', 'no') + + +def get_load_dotenv(default=True): + """Get whether the user has disabled loading dotenv files by setting + :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the + files. + + :param default: What to return if the env var isn't set. + """ + val = os.environ.get('FLASK_SKIP_DOTENV') + if not val: return default - return val not in ('0', 'false', 'no') + + return val.lower() in ('0', 'false', 'no') def _endpoint_from_view_func(view_func): @@ -266,40 +288,40 @@ """ appctx = _app_ctx_stack.top reqctx = _request_ctx_stack.top + if appctx is None: - raise RuntimeError('Attempted to generate a URL without the ' - 'application context being pushed. This has to be ' - 'executed when application context is available.') + raise RuntimeError( + 'Attempted to generate a URL without the application context being' + ' pushed. This has to be executed when application context is' + ' available.' + ) # If request specific information is available we have some extra # features that support "relative" URLs. if reqctx is not None: url_adapter = reqctx.url_adapter blueprint_name = request.blueprint - if not reqctx.request._is_old_module: - if endpoint[:1] == '.': - if blueprint_name is not None: - endpoint = blueprint_name + endpoint - else: - endpoint = endpoint[1:] - else: - # TODO: get rid of this deprecated functionality in 1.0 - if '.' not in endpoint: - if blueprint_name is not None: - endpoint = blueprint_name + '.' + endpoint - elif endpoint.startswith('.'): + + if endpoint[:1] == '.': + if blueprint_name is not None: + endpoint = blueprint_name + endpoint + else: endpoint = endpoint[1:] + external = values.pop('_external', False) # Otherwise go with the url adapter from the appctx and make # the URLs external by default. else: url_adapter = appctx.url_adapter + if url_adapter is None: - raise RuntimeError('Application was not able to create a URL ' - 'adapter for request independent URL generation. ' - 'You might be able to fix this by setting ' - 'the SERVER_NAME config variable.') + raise RuntimeError( + 'Application was not able to create a URL adapter for request' + ' independent URL generation. You might be able to fix this by' + ' setting the SERVER_NAME config variable.' + ) + external = values.pop('_external', True) anchor = values.pop('_anchor', None) @@ -330,6 +352,7 @@ values['_external'] = external values['_anchor'] = anchor values['_method'] = method + values['_scheme'] = scheme return appctx.app.handle_url_build_error(error, endpoint, values) if anchor is not None: @@ -380,7 +403,7 @@ # session.setdefault('_flashes', []).append((category, message)) # # This assumed that changes made to mutable structures in the session are - # are always in sync with the session object, which is not true for session + # always in sync with the session object, which is not true for session # implementations that use external storage for keeping their keys/values. flashes = session.get('_flashes', []) flashes.append((category, message)) @@ -478,7 +501,12 @@ The `attachment_filename` is preferred over `filename` for MIME-type detection. - :param filename_or_fp: the filename of the file to send in `latin-1`. + .. versionchanged:: 1.0 + UTF-8 filenames, as specified in `RFC 2231`_, are supported. + + .. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4 + + :param filename_or_fp: the filename of the file to send. This is relative to the :attr:`~Flask.root_path` if a relative path is specified. Alternatively a file object might be provided in @@ -534,8 +562,19 @@ if attachment_filename is None: raise TypeError('filename unavailable, required for ' 'sending as attachment') - headers.add('Content-Disposition', 'attachment', - filename=attachment_filename) + + try: + attachment_filename = attachment_filename.encode('latin-1') + except UnicodeEncodeError: + filenames = { + 'filename': unicodedata.normalize( + 'NFKD', attachment_filename).encode('latin-1', 'ignore'), + 'filename*': "UTF-8''%s" % url_quote(attachment_filename), + } + else: + filenames = {'filename': attachment_filename} + + headers.add('Content-Disposition', 'attachment', **filenames) if current_app.use_x_sendfile and filename: if file is not None: @@ -584,17 +623,13 @@ 'headers' % filename, stacklevel=2) if conditional: - if callable(getattr(Range, 'to_content_range_header', None)): - # Werkzeug supports Range Requests - # Remove this test when support for Werkzeug <0.12 is dropped - try: - rv = rv.make_conditional(request, accept_ranges=True, - complete_length=fsize) - except RequestedRangeNotSatisfiable: + try: + rv = rv.make_conditional(request, accept_ranges=True, + complete_length=fsize) + except RequestedRangeNotSatisfiable: + if file is not None: file.close() - raise - else: - rv = rv.make_conditional(request) + raise # make sure we don't send x-sendfile for servers that # ignore the 304 status code for x-sendfile. if rv.status_code == 304: @@ -829,43 +864,56 @@ class _PackageBoundObject(object): + #: 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, import_name, template_folder=None, root_path=None): - #: The name of the package or module. Do not change this once - #: it was set by the constructor. self.import_name = import_name - - #: location of the templates. ``None`` if templates should not be - #: exposed. self.template_folder = template_folder if root_path is None: root_path = get_root_path(self.import_name) - #: Where is the app root located? self.root_path = root_path - self._static_folder = None self._static_url_path = None def _get_static_folder(self): if self._static_folder is not None: return os.path.join(self.root_path, self._static_folder) + def _set_static_folder(self, value): self._static_folder = value - static_folder = property(_get_static_folder, _set_static_folder, doc=''' - The absolute path to the configured static folder. - ''') + + static_folder = property( + _get_static_folder, _set_static_folder, + doc='The absolute path to the configured static folder.' + ) del _get_static_folder, _set_static_folder def _get_static_url_path(self): if self._static_url_path is not None: return self._static_url_path + if self.static_folder is not None: return '/' + os.path.basename(self.static_folder) + def _set_static_url_path(self, value): self._static_url_path = value - static_url_path = property(_get_static_url_path, _set_static_url_path) + + static_url_path = property( + _get_static_url_path, _set_static_url_path, + doc='The URL prefix that the static route will be registered for.' + ) del _get_static_url_path, _set_static_url_path @property @@ -964,3 +1012,33 @@ :rtype: int """ return td.days * 60 * 60 * 24 + td.seconds + + +def is_ip(value): + """Determine if the given string is an IP address. + + Python 2 on Windows doesn't provide ``inet_pton``, so this only + checks IPv4 addresses in that environment. + + :param value: value to check + :type value: str + + :return: True if string is an IP address + :rtype: bool + """ + if PY2 and os.name == 'nt': + try: + socket.inet_aton(value) + return True + except socket.error: + return False + + for family in (socket.AF_INET, socket.AF_INET6): + try: + socket.inet_pton(family, value) + except socket.error: + pass + else: + return True + + return False diff -Nru flask-0.12.2/flask/__init__.py flask-1.0.2/flask/__init__.py --- flask-0.12.2/flask/__init__.py 2017-05-16 06:39:30.000000000 +0000 +++ flask-1.0.2/flask/__init__.py 2018-05-02 14:16:14.000000000 +0000 @@ -6,11 +6,11 @@ A microframework based on Werkzeug. It's extensively documented and follows best practice patterns. - :copyright: (c) 2015 by Armin Ronacher. + :copyright: © 2010 by the Pallets team. :license: BSD, see LICENSE for more details. """ -__version__ = '0.12.2' +__version__ = '1.0.2' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. diff -Nru flask-0.12.2/flask/json/__init__.py flask-1.0.2/flask/json/__init__.py --- flask-0.12.2/flask/json/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ flask-1.0.2/flask/json/__init__.py 2018-05-02 00:50:32.000000000 +0000 @@ -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 ``') - assert rv == u'"\\u003c/script\\u003e"' - assert type(rv) == text_type - rv = render('{{ ""|tojson }}') - assert rv == '"\\u003c/script\\u003e"' - rv = render('{{ "<\0/script>"|tojson }}') - assert rv == '"\\u003c\\u0000/script\\u003e"' - rv = render('{{ "