Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inspection module to replace print_routes #1661

Merged
merged 38 commits into from
Mar 30, 2020

Conversation

CaselIT
Copy link
Member

@CaselIT CaselIT commented Feb 2, 2020

Summary of Changes

Create an inspection module to replace the print_route script.
This was inspired by the discussion about #1435 but has evolved in a inspection module that can obtain information regarding the application and create a pretty print of it.

The public interface of the inspection module has different functions that return information regarding an application.
There is a main function to inspect the entire application (routes, middleware, static_routes, sinks and error_handlers) and also individual functions for each.
The returned information are instance of classes (or list of them) which can return a pretty print, since this is more extensible than just a string.
There is also the ability to register inspection of different route class implementations by using a decorator on a inspection function that handles a particular type of router.

This is the output of a sample application

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

      ↣ MyMiddleware.process_resource
        ↣ OtherMiddleware.process_resource

            ├── Process responder

        ↢ OtherMiddleware.process_response
      ↢ CORSMiddleware.process_response
• Static routes:
    ↦ /tests/ C:\Users\Federico\Dev\GitHub\falcon\tests [C:\Users\Federico\Dev\GitHub\falcon\tests\conftest.py]
    ↦ /falcon/ C:\Users\Federico\Dev\GitHub\falcon\falcon
• Sinks:
    ⇥ /sink_cls SinkClass
    ⇥ /sink_fn sinkFn
• Error handlers:
    ⇜ RuntimeError my_runtime_handler

A more verbose output is

View
Falcon App (WSGI)
• Routes:
    ⇒ /foo - MyResponder (c:\users\federico\dev\github\falcon\try_print.py:19):
       ├── DELETE - on_delete (c:\users\federico\dev\github\falcon\try_print.py:26)
       ├── GET - on_get (c:\users\federico\dev\github\falcon\try_print.py:20)
       ├── POST - on_post (c:\users\federico\dev\github\falcon\try_print.py:23)
       ├── OPTIONS - options_responder (c:\users\federico\dev\github\falcon\falcon\responders.py:84)
       ├── CONNECT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── HEAD - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PATCH - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PUT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── TRACE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── CHECKIN - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── CHECKOUT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── COPY - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── LOCK - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── MKCOL - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── MOVE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PROPFIND - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PROPPATCH - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── REPORT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UNCHECKIN - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UNLOCK - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UPDATE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       └── VERSION-CONTROL - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
    ⇒ /foo/{id} - MyResponder (c:\users\federico\dev\github\falcon\try_print.py:19):
       ├── DELETE - on_delete_id (c:\users\federico\dev\github\falcon\try_print.py:35)
       ├── GET - on_get_id (c:\users\federico\dev\github\falcon\try_print.py:29)
       ├── POST - on_post_id (c:\users\federico\dev\github\falcon\try_print.py:32)
       ├── OPTIONS - options_responder (c:\users\federico\dev\github\falcon\falcon\responders.py:84)
       ├── CONNECT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── HEAD - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PATCH - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PUT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── TRACE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── CHECKIN - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── CHECKOUT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── COPY - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── LOCK - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── MKCOL - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── MOVE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PROPFIND - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PROPPATCH - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── REPORT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UNCHECKIN - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UNLOCK - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UPDATE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       └── VERSION-CONTROL - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
    ⇒ /bar - OtherResponder (c:\users\federico\dev\github\falcon\try_print.py:39):
       ├── DELETE - on_delete_id (c:\users\federico\dev\github\falcon\try_print.py:35)
       ├── GET - on_get_id (c:\users\federico\dev\github\falcon\try_print.py:29)
       ├── POST - on_post_id (c:\users\federico\dev\github\falcon\try_print.py:32)
       ├── OPTIONS - options_responder (c:\users\federico\dev\github\falcon\falcon\responders.py:84)
       ├── CONNECT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── HEAD - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PATCH - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PUT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── TRACE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── CHECKIN - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── CHECKOUT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── COPY - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── LOCK - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── MKCOL - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── MOVE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PROPFIND - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── PROPPATCH - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── REPORT - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UNCHECKIN - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UNLOCK - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       ├── UPDATE - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
       └── VERSION-CONTROL - method_not_allowed (c:\users\federico\dev\github\falcon\falcon\responders.py:59)
• Middleware (Middleware are independent):
    → MyMiddleware.process_request

      ↣ MyMiddleware.process_resource
        ↣ OtherMiddleware.process_resource

            ├── Process responder

        ↢ OtherMiddleware.process_response
      ↢ CORSMiddleware.process_response
    - Middlewares classes:
        ↣ CORSMiddleware (c:\users\federico\dev\github\falcon\falcon\middlewares.py:2):
           └── process_response (c:\users\federico\dev\github\falcon\falcon\middlewares.py:3)
        ↣ MyMiddleware (c:\users\federico\dev\github\falcon\try_print.py:73):
           ├── process_request (c:\users\federico\dev\github\falcon\try_print.py:74)
           └── process_resource (c:\users\federico\dev\github\falcon\try_print.py:77)
        ↣ OtherMiddleware (c:\users\federico\dev\github\falcon\try_print.py:83):
           ├── process_resource (c:\users\federico\dev\github\falcon\try_print.py:87)
           └── process_response (c:\users\federico\dev\github\falcon\try_print.py:90)
• Static routes:
    ↦ /tests/ C:\Users\Federico\Dev\GitHub\falcon\tests [C:\Users\Federico\Dev\GitHub\falcon\tests\conftest.py]
    ↦ /falcon/ C:\Users\Federico\Dev\GitHub\falcon\falcon
• Sinks:
    ⇥ /sink_cls SinkClass (c:\users\federico\dev\github\falcon\try_print.py:57)
    ⇥ /sink_fn sinkFn (c:\users\federico\dev\github\falcon\try_print.py:53)
• Error handlers:
    ⇜ Exception _python_error_handler (c:\users\federico\dev\github\falcon\falcon\app.py:894)
    ⇜ HTTPError _http_error_handler (c:\users\federico\dev\github\falcon\falcon\app.py:891)
    ⇜ HTTPStatus _http_status_handler (c:\users\federico\dev\github\falcon\falcon\app.py:888)
    ⇜ RuntimeError my_runtime_handler (c:\users\federico\dev\github\falcon\try_print.py:66)

This is still very much still a proof of concept wip, with documentation and test missing. I wanted to open this PR to get feedback on this.

The main items that are missing are:

  • verify that everything work with an async app
  • tests
  • documentation on it. I think it would be useful if we include the output of the inspection in the example documentation to better visualize the configuration of the example

Related Issues

This fixes #1435

Pull Request Checklist

This is just a reminder about the most common mistakes. Please make sure that you tick all appropriate boxes. But please read our contribution guide at least once; it will save you a few review cycles!

If an item doesn't apply to your pull request, check it anyway to make it apparent that there's nothing to do.

  • Added tests for changed code.
  • Prefixed code comments with GitHub nick and an appropriate prefix.
  • Coding style is consistent with the rest of the framework.
  • Updated documentation for changed code.
    • Added docstrings for any new classes, functions, or modules.
    • Updated docstrings for any modifications to existing code.
    • Added references to new classes, functions, or modules to the relevant RST file under docs/.
    • Updated all relevant supporting documentation files under docs/.
    • A copyright notice is included at the top of any new modules (using your own name or the name of your organization).
    • Changed/added classes/methods/functions have appropriate versionadded, versionchanged, or deprecated directives.
  • Changes (and possible deprecations) have towncrier news fragments under docs/_newsfragments/. (Run towncrier --draft to ensure it renders correctly.)

If you have any questions to any of the points above, just submit and ask! This checklist is here to help you, not to deter you from contributing!

PR template inspired by the attrs project.

setup.py Outdated Show resolved Hide resolved
@codecov
Copy link

codecov bot commented Feb 25, 2020

Codecov Report

❗ No coverage uploaded for pull request base (master@98929d5). Click here to learn what that means.
The diff coverage is 100%.

Impacted file tree graph

@@           Coverage Diff            @@
##             master   #1661   +/-   ##
========================================
  Coverage          ?    100%           
========================================
  Files             ?      49           
  Lines             ?    3895           
  Branches          ?     606           
========================================
  Hits              ?    3895           
  Misses            ?       0           
  Partials          ?       0
Impacted Files Coverage Δ
falcon/status_codes.py 100% <100%> (ø)
falcon/inspect.py 100% <100%> (ø)
falcon/asgi/structures.py 100% <100%> (ø)
falcon/media/multipart.py 100% <100%> (ø)
falcon/routing/compiled.py 100% <100%> (ø)
falcon/asgi/request.py 100% <100%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 98929d5...6d630e3. Read the comment docs.

@CaselIT
Copy link
Member Author

CaselIT commented Feb 28, 2020

The code and the tests should be done.
The documentation is still missing. I was thinking of adding a new documentation page with a brief explanation of the module and an example output similar to the one in this page.
Also maybe mention it in some tutorial examples, like where it's adding the roues and the one on the middleware?

@CaselIT CaselIT marked this pull request as ready for review February 28, 2020 21:53
@codecov-io
Copy link

codecov-io commented Feb 28, 2020

Codecov Report

Merging #1661 into master will not change coverage by %.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##            master     #1661   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           49        49           
  Lines         3928      3928           
  Branches       611       611           
=========================================
  Hits          3928      3928           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 02d7829...02d7829. Read the comment docs.

@CaselIT CaselIT requested a review from vytas7 February 29, 2020 11:49
@CaselIT
Copy link
Member Author

CaselIT commented Feb 29, 2020

I've added a new documentation file for the inspect module and updated the quickstart and tutorial files to introduce the inspect module.

I think this is ready for a review

setup.py Outdated Show resolved Hide resolved
Copy link
Member

@kgriffs kgriffs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a quick test with suffixed responders and they were displayed correctly. Overall the patch looks sound. I still need to spend some time going through the details.

@CaselIT
Copy link
Member Author

CaselIT commented Mar 3, 2020

Take your time, it ended up being quite large 👍

The suffixed responders are not treated differently than the normal ones, but the name of the function is correctly printed, like "on_get_with_suffix", but maybe there should be an attribute in the route method class that contains the suffix?

Also I was wondering if we should separate the internal routers from the source info print. Now both are under the verbose flag. Maybe we could add an "internal" flag?

@kgriffs
Copy link
Member

kgriffs commented Mar 4, 2020

The suffixed responders are not treated differently than the normal ones, but the name of the function is correctly printed, like "on_get_with_suffix", but maybe there should be an attribute in the route method class that contains the suffix?

TBH I think it looks good as-is.

Also I was wondering if we should separate the internal routers from the source info print. Now both are under the verbose flag. Maybe we could add an "internal" flag?

That would be great, actually.

@CaselIT
Copy link
Member Author

CaselIT commented Mar 4, 2020

@kgriffs I've split the internal and the verbose options.

@CaselIT CaselIT requested a review from kgriffs March 4, 2020 20:32
Copy link
Member Author

@CaselIT CaselIT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. Just noticed a typo

The output would be:

.. code::
# my_module exposes tha application as a variable named "app"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tha -> the

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, thx!

@kgriffs
Copy link
Member

kgriffs commented Mar 28, 2020

Hope you don't mind my tweaks... just trying to polish up the prose a bit.

Copy link
Member Author

@CaselIT CaselIT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a small comment


Returns:
List[StaticRouteInfo]: The list of static routes of the application.
List[StaticRouteInfo]: The list of static routes that have
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A list? as above?


Returns:
List[SinkInfo]: The list of sinks of the application.
List[SinkInfo]: The list of sinks used by the application.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A list? as above?


Returns:
List[ErrorHandlerInfo]: The list of error handlers of the application.
List[ErrorHandlerInfo]: The list of error handlers used by the
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A list? as above?

@CaselIT
Copy link
Member Author

CaselIT commented Mar 28, 2020

Hope you don't mind my tweaks... just trying to polish up the prose a bit.

No, not at all. I'm not very good with the documentation :(

@kgriffs kgriffs merged commit b437e5e into falconry:master Mar 30, 2020
@CaselIT
Copy link
Member Author

CaselIT commented Mar 30, 2020

Nice!

@CaselIT CaselIT deleted the inspect-api branch March 30, 2020 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve falcon-print-routes tool
4 participants