feat(cmd): Add a utility to print routes (#849)
Add a new CLI utility, falcon-print-routes, that takes in a module:instance_or_callable,
introspects the routes, and prints the results to stdout.
Example:
$ falcon-print-routes commissaire.testroutes:a
-> /api/v0/status
-> /api/v0/cluster/{name}
-> /api/v0/cluster/{name}/hosts
-> /api/v0/cluster/{name}/hosts/{address}
This commit is contained in:
committed by
Kurt Griffiths
parent
7d6106ba7b
commit
d9d1aed01e
102
falcon/cmd/print_routes.py
Normal file
102
falcon/cmd/print_routes.py
Normal file
@@ -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 API instance.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import inspect
|
||||
|
||||
import falcon
|
||||
|
||||
|
||||
def print_routes(api, verbose=False): # pragma: no cover
|
||||
"""
|
||||
Initial call.
|
||||
|
||||
:param api: The falcon.API or callable that returns an instance to look at.
|
||||
:type api: falcon.API or callable
|
||||
:param verbose: If the output should be verbose.
|
||||
:type verbose: bool
|
||||
"""
|
||||
traverse(api._router._roots, verbose=verbose)
|
||||
|
||||
|
||||
def traverse(roots, parent='', verbose=False):
|
||||
"""
|
||||
Recursive call which also handles printing output.
|
||||
|
||||
:param api: The falcon.API or callable that returns an instance to look at.
|
||||
:type api: falcon.API or callable
|
||||
:param parent: The parent uri path to the current iteration.
|
||||
:type parent: str
|
||||
:param verbose: If the output should be verbose.
|
||||
:type verbose: bool
|
||||
"""
|
||||
for root in roots:
|
||||
if root.method_map:
|
||||
print('->', parent + '/' + root.raw_segment)
|
||||
if verbose:
|
||||
for method, func in root.method_map.items():
|
||||
if func.__name__ != 'method_not_allowed':
|
||||
print('-->{0} {1}:{2}'.format(
|
||||
method,
|
||||
inspect.getsourcefile(func),
|
||||
inspect.getsourcelines(func)[1]))
|
||||
if root.children:
|
||||
traverse(root.children, parent + '/' + root.raw_segment, verbose)
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entrypoint.
|
||||
"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Example: print-api-routes myprogram:app')
|
||||
parser.add_argument(
|
||||
'-v', '--verbose', action='store_true',
|
||||
help='Prints out information for each method.')
|
||||
parser.add_argument(
|
||||
'api_module',
|
||||
help='The module and api to inspect. Example: myapp.somemodule:api',
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
module, instance = args.api_module.split(':', 1)
|
||||
except ValueError:
|
||||
parser.error(
|
||||
'The api_module must include a colon between '
|
||||
'the module and instnace')
|
||||
api = getattr(__import__(module, fromlist=[True]), instance)
|
||||
if not isinstance(api, falcon.API):
|
||||
if callable(api):
|
||||
api = api()
|
||||
if not isinstance(api, falcon.API):
|
||||
parser.error(
|
||||
'{0} did not return a falcon.API instance'.format(
|
||||
args.api_module))
|
||||
else:
|
||||
parser.error(
|
||||
'The instance must be of falcon.API or be '
|
||||
'a callable without args that returns falcon.API')
|
||||
print_routes(api, verbose=args.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
3
setup.py
3
setup.py
@@ -111,7 +111,8 @@ setup(
|
||||
tests_require=['nose', 'ddt', 'testtools', 'requests', 'pyyaml'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'falcon-bench = falcon.cmd.bench:main'
|
||||
'falcon-bench = falcon.cmd.bench:main',
|
||||
'falcon-print-routes = falcon.cmd.print_routes:main'
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
52
tests/test_cmd_print_api.py
Normal file
52
tests/test_cmd_print_api.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import sys
|
||||
import testtools
|
||||
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except ImportError:
|
||||
from io import StringIO
|
||||
|
||||
from falcon import API
|
||||
from falcon.cmd import print_routes
|
||||
|
||||
|
||||
_api = API()
|
||||
_api.add_route('/test', None)
|
||||
|
||||
STDOUT = sys.stdout
|
||||
|
||||
|
||||
class TestPrintRoutes(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Capture stdout"""
|
||||
super(TestPrintRoutes, self).setUp()
|
||||
self.output = StringIO()
|
||||
sys.stdout = self.output
|
||||
|
||||
def tearDown(self):
|
||||
"""Reset stdout"""
|
||||
super(TestPrintRoutes, self).tearDown()
|
||||
self.output.close()
|
||||
del self.output
|
||||
sys.stdout = STDOUT
|
||||
|
||||
def test_traverse_with_verbose(self):
|
||||
"""Ensure traverse finds the proper routes and adds verbose output."""
|
||||
print_routes.traverse(
|
||||
_api._router._roots,
|
||||
verbose=True)
|
||||
|
||||
route, options = self.output.getvalue().strip().split('\n')
|
||||
self.assertEquals('-> /test', route)
|
||||
self.assertTrue('OPTIONS' in options)
|
||||
self.assertTrue('falcon/falcon/responders.py:' in options)
|
||||
|
||||
def test_traverse(self):
|
||||
"""Ensure traverse finds the proper routes."""
|
||||
print_routes.traverse(
|
||||
_api._router._roots,
|
||||
verbose=False)
|
||||
|
||||
route = self.output.getvalue().strip()
|
||||
self.assertEquals('-> /test', route)
|
||||
Reference in New Issue
Block a user