forked from falconry/falcon
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add inspection module to replace print_routes (falconry#1661)
* feat(inspec): add inspect module to support programmatic inspection of falcon applications * refactor(print_routes): use the new inspect module in print_routes command * feat(inspect): add inspection of app, static routes and sinks * refactor(print_routes): use inspect_app by default * feat(inspect): add support for error handlers inspection * feat(inspect): add support for middleware inspection * chore(inspect): cleanup code * refactor(inspect): cleanup code, add types * style(inspect): fix quote style * refactor(inspect): move inspect module out of util. Rename script * feat(inspect): use a visitor to transform the info class to string * test(inspect): add tests to inspect_app command line * chore: add license, fix pep * test: fix import error by adding __init__ in test module while testing * test: add initial inspect tests * test: fix broken tests * test: more tests * test: continue adding test to string visitor * test: complete tests of inspect module * chore: add missing test and restore previous cmd alias * docs(inspect): add inspect documentation * chore: ignore routes without a responder. Improve middleware str output * chore: use the falcon-inspect-app in the doc, improve module resolution, fix pep * chore: deprecate print routes command * refactor: split inspect and verbose * chore: add missing test * docs: document internal flag * fix(mypy): fix mypy errors * doc(inspect.rst): Minor edits for organization and clarity * doc(inspect): Tweak docstrings * doc(inspect.rst): Fix typo * doc(inspect): "The" ==> "A" Co-authored-by: Kurt Griffiths <mail@kgriffs.com>
- Loading branch information
Showing
16 changed files
with
2,059 additions
and
197 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Added inspect module to collect information about an application regarding | ||
the registered routes, middlewares, static routes, sinks and error handlers | ||
(See also: :ref:`inspect`.) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,5 +16,6 @@ Framework Reference | |
cors | ||
hooks | ||
routing | ||
inspect | ||
util | ||
testing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
.. _inspect: | ||
|
||
Inspect Module | ||
============== | ||
|
||
* `Using Inspect Functions`_ | ||
* `Inspect Functions Reference`_ | ||
* `Router Inspection`_ | ||
* `Information Classes`_ | ||
* `Visitor Classes`_ | ||
|
||
This module can be used to inspect a Falcon application to obtain information | ||
about its registered routes, middleware objects, static routes, sinks and | ||
error handlers. The entire application can be inspected at once using the | ||
:func:`.inspect_app` function. Additional functions are available for | ||
inspecting specific aspects of the app. | ||
|
||
A ``falcon-inspect-app`` CLI script is also available; it uses the inspect | ||
module to print a string representation of an application, as demonstrated | ||
below: | ||
|
||
.. code:: bash | ||
# my_module exposes the application as a variable named "app" | ||
$ falcon-inspect-app my_module:app | ||
Falcon App (WSGI) | ||
• Routes: | ||
⇒ /foo - MyResponder: | ||
├── DELETE - on_delete | ||
├── GET - on_get | ||
└── POST - on_post | ||
⇒ /foo/{id} - MyResponder: | ||
├── DELETE - on_delete_id | ||
├── GET - on_get_id | ||
└── POST - on_post_id | ||
⇒ /bar - OtherResponder: | ||
├── DELETE - on_delete_id | ||
├── GET - on_get_id | ||
└── POST - on_post_id | ||
• Middleware (Middleware are independent): | ||
→ MyMiddleware.process_request | ||
→ OtherMiddleware.process_request | ||
↣ MyMiddleware.process_resource | ||
↣ OtherMiddleware.process_resource | ||
├── Process route responder | ||
↢ OtherMiddleware.process_response | ||
↢ CORSMiddleware.process_response | ||
• Static routes: | ||
↦ /tests/ /path/to/tests [/path/to/test/index.html] | ||
↦ /falcon/ /path/to/falcon | ||
• Sinks: | ||
⇥ /sink_cls SinkClass | ||
⇥ /sink_fn sinkFn | ||
• Error handlers: | ||
⇜ RuntimeError my_runtime_handler | ||
The example above shows how ``falcon-inspect-app`` simply outputs the value | ||
returned by the :meth:`.AppInfo.to_string` method. In fact, here is a simple | ||
script that returns the same output as the ``falcon-inspect-app`` command: | ||
|
||
.. code:: python | ||
from falcon import inspect | ||
from my_module import app | ||
app_info = inspect.inspect_app(app) | ||
# Equivalent to print(app_info.to_string()) | ||
print(app_info) | ||
A more verbose description of the app can be obtained by passing | ||
``verbose=True`` to :meth:`.AppInfo.to_string`, while the default | ||
routes added by the framework can be included by passing ``internal=True``. The | ||
``falcon-inspect-app`` command supports the ``--verbose`` and | ||
``--internal`` flags to enable these options. | ||
|
||
Using Inspect Functions | ||
----------------------- | ||
|
||
The values returned by the inspect functions are class instances that | ||
contain the relevant information collected from the application. These | ||
objects facilitate programmatic use of the collected data. | ||
|
||
To support inspection of applications that use a custom router, the | ||
module provides a :func:`.register_router` function to register | ||
a handler function for the custom router class. | ||
Inspection of the default :class:`.CompiledRouter` class is | ||
handled by the :func:`.inspect_compiled_router` | ||
function. | ||
|
||
The returned information classes can be explored using the visitor | ||
pattern. To create the string representation of the classes the | ||
:class:`.StringVisitor` visitor is used. | ||
This class is instantiated automatically when calling ``str()`` | ||
on an instance or when using the ``to_string()`` method. | ||
|
||
Custom visitor implementations can subclass :class:`.InspectVisitor` and | ||
use the :meth:`.InspectVisitor.process` method to visit | ||
the classes. | ||
|
||
Inspect Functions Reference | ||
--------------------------- | ||
|
||
This module defines the following inspect functions. | ||
|
||
.. autofunction:: falcon.inspect.inspect_app | ||
|
||
.. autofunction:: falcon.inspect.inspect_routes | ||
|
||
.. autofunction:: falcon.inspect.inspect_middlewares | ||
|
||
.. autofunction:: falcon.inspect.inspect_static_routes | ||
|
||
.. autofunction:: falcon.inspect.inspect_sinks | ||
|
||
.. autofunction:: falcon.inspect.inspect_error_handlers | ||
|
||
Router Inspection | ||
----------------- | ||
|
||
The following functions enable route inspection. | ||
|
||
.. autofunction:: falcon.inspect.register_router | ||
|
||
.. autofunction:: falcon.inspect.inspect_compiled_router | ||
|
||
Information Classes | ||
------------------- | ||
|
||
Information returned by the inspect functions is represented by these classes. | ||
|
||
.. autoclass:: falcon.inspect.AppInfo | ||
:members: | ||
|
||
.. autoclass:: falcon.inspect.RouteInfo | ||
|
||
.. autoclass:: falcon.inspect.RouteMethodInfo | ||
|
||
.. autoclass:: falcon.inspect.MiddlewareInfo | ||
|
||
.. autoclass:: falcon.inspect.MiddlewareTreeInfo | ||
|
||
.. autoclass:: falcon.inspect.MiddlewareClassInfo | ||
|
||
.. autoclass:: falcon.inspect.MiddlewareTreeItemInfo | ||
|
||
.. autoclass:: falcon.inspect.MiddlewareMethodInfo | ||
|
||
.. autoclass:: falcon.inspect.StaticRouteInfo | ||
|
||
.. autoclass:: falcon.inspect.SinkInfo | ||
|
||
.. autoclass:: falcon.inspect.ErrorHandlerInfo | ||
|
||
Visitor Classes | ||
--------------- | ||
|
||
The following visitors are used to traverse the information classes. | ||
|
||
.. autoclass:: falcon.inspect.InspectVisitor | ||
:members: | ||
|
||
.. autoclass:: falcon.inspect.StringVisitor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
#!/usr/bin/env python | ||
# Copyright 2013 by Rackspace Hosting, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the 'License'); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an 'AS IS' BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
""" | ||
Script that prints out the routes of an App instance. | ||
""" | ||
import argparse | ||
import importlib | ||
import os | ||
import sys | ||
|
||
import falcon | ||
from falcon.inspect import inspect_app, inspect_routes, StringVisitor | ||
|
||
sys.path.append(os.getcwd()) | ||
|
||
|
||
def make_parser(): | ||
"""Creates the parsed or the application""" | ||
parser = argparse.ArgumentParser( | ||
description='Example: falcon-inspect-app myprogram:app' | ||
) | ||
parser.add_argument( | ||
'-r', | ||
'--route_only', | ||
action='store_true', | ||
help='Prints only the information regarding the routes', | ||
) | ||
parser.add_argument( | ||
'-v', '--verbose', action='store_true', help='More verbose output', | ||
) | ||
parser.add_argument( | ||
'-i', | ||
'--internal', | ||
action='store_true', | ||
help='Print also internal falcon route methods and error handlers', | ||
) | ||
parser.add_argument( | ||
'app_module', | ||
help='The module and app to inspect. Example: myapp.somemodule:api', | ||
) | ||
return parser | ||
|
||
|
||
def load_app(parser, args): | ||
|
||
try: | ||
module, instance = args.app_module.split(':', 1) | ||
except ValueError: | ||
parser.error('The app_module must include a colon between the module and instance') | ||
try: | ||
app = getattr(importlib.import_module(module), instance) | ||
except AttributeError: | ||
parser.error('{!r} not found in module {!r}'.format(instance, module)) | ||
|
||
if not isinstance(app, falcon.App): | ||
if callable(app): | ||
app = app() | ||
if not isinstance(app, falcon.App): | ||
parser.error('{} did not return a falcon.App instance'.format(args.app_module)) | ||
else: | ||
parser.error( | ||
'The instance must be of falcon.App or be ' | ||
'a callable without args that returns falcon.App' | ||
) | ||
return app | ||
|
||
|
||
def route_main(): | ||
print('The "falcon-print-routes" command is deprecated. Please use "falcon-inspect-app"') | ||
main() | ||
|
||
|
||
def main(): | ||
""" | ||
Main entrypoint. | ||
""" | ||
parser = make_parser() | ||
args = parser.parse_args() | ||
app = load_app(parser, args) | ||
if args.route_only: | ||
routes = inspect_routes(app) | ||
visitor = StringVisitor(args.verbose, args.internal) | ||
for route in routes: | ||
print(visitor.process(route)) | ||
else: | ||
print(inspect_app(app).to_string(args.verbose, args.internal)) | ||
|
||
|
||
if __name__ == '__main__': # pragma: no cover | ||
main() |
Oops, something went wrong.